Long Run|INEMA.CLUB-PRO

Lesson 6 · 20 min

Headless loop: loop-longrun.sh, flock, timeout, and MemoryMax

In the interactive /goal, an evaluator reading the conversation decides when it's done. In the headless loop, the test decides, run by the loop itself, with the test files protected. In this lesson you run loop-longrun.sh with a fake agent: you see it finish, you see the cheat get reverted, and you see the lock stop two loops in the same folder, without spending any quota.

Start the lesson
A server rack in a dark room with LEDs lit and no monitor turned on.

In this lesson you will

  1. 1The loop.env: tests, protected files, allowed files, and caps
  2. 2flock, timeout, and MemoryMax on every cycle
  3. 3Protected test touched = reverted and failed cycle
  4. 4systemd: watches every 10 min, and cron with flock

Goal: run loop-longrun.sh with a fake agent and watch the test decide: done, cheat reverted, stagnation, and second-copy lock.

01One cycle of loop-longrun.sh

The agent works; the loop decides. Click each step.

Click each block in the diagram to see what it does.

If what's missing only shows up in the final test (slow), every cycle counts as "no progress": put those cases in the fast test too, or raise ESTAGNACAO (a warning from the script itself).

02Watch the loop decide

Run it, interrupt it halfway, and run it again. One of the cycles touches a protected test.

cycles to run

current cycle

checkpoint (commit)

reverted

Illustrative simulation in the browser. The real rehearsal, with a fake agent, is right below.

03Rehearsal with a fake agent

The loop accepts CODEX_BIN: point it at a script and no model is called. It's the cheap way to test your loop.env before spending quota.

ensaio-loop.sh · sets up the test project
# ensaio-loop.sh — monta um projeto de teste com 3 testes congelados e dois agentes FALSOS
E=/tmp/ensaio-loop; K=~/projetos/execucao-longa
rm -rf $E && mkdir -p $E/proj/tests && cd $E/proj
git init -q && git config user.name ensaio && git config user.email ensaio@local
cat > tests/test_conta.py <<'PY'
from conta import conta_palavras
def test_vazio(): assert conta_palavras("") == 0
def test_simples(): assert conta_palavras("um dois três") == 3
def test_espacos(): assert conta_palavras("  um   dois ") == 2
PY
printf 'def conta_palavras(texto):\n    raise NotImplementedError\n' > conta.py
printf '[pytest]\npythonpath = .\n' > pytest.ini
printf '__pycache__/\n' > .gitignore
git add -A && git commit -qm "base: testes congelados"
$K/tools/novo-longrun.sh $E/proj conta-palavras > /dev/null
D=$(ls -d $E/proj/longrun/*)
echo "Implemente conta_palavras em conta.py. Leia goal.md, state.md e plan.md antes. Não mexa em tests/." > $D/prompt.md
cat > $D/loop.env <<'ENV'
TESTE_RAPIDO="python3 -m pytest -q"
TESTE_FINAL="python3 -m pytest -q"
PROTEGIDOS="tests/ pytest.ini"
PERMITIDOS="conta.py"
MAX_CICLOS=4
MIN_CICLO=2
MEM=1G
ESTAGNACAO=3
ENV
git add -A && git commit -qm "longrun: pasta da execução"
printf '#!/usr/bin/env bash\ncat > /dev/null\nprintf "def conta_palavras(texto):\\n    return len(texto.split())\\n" > conta.py\n' > $E/agente-bom.sh
printf '#!/usr/bin/env bash\ncat > /dev/null\nrm -f tests/test_conta.py\n' > $E/agente-trapaca.sh
printf '#!/usr/bin/env bash\ncat > /dev/null\nsleep 6\n' > $E/agente-lento.sh
chmod +x $E/agente-*.sh
echo "pronto: $D"
Terminal · the three rounds
bash ensaio-loop.sh
cd /tmp/ensaio-loop/proj && D=$(ls -d $PWD/longrun/*) && L=~/projetos/execucao-longa/tools/loop-longrun.sh

CODEX_BIN=/tmp/ensaio-loop/agente-trapaca.sh $L $D; echo "saída: $?"   # A) trapaça
CODEX_BIN=/tmp/ensaio-loop/agente-bom.sh $L $D; echo "saída: $?"       # B) trabalho certo

CODEX_BIN=/tmp/ensaio-loop/agente-lento.sh $L $D > /dev/null 2>&1 &   # C) dois loops
sleep 2; CODEX_BIN=/tmp/ensaio-loop/agente-lento.sh $L $D; echo "saída: $?"; wait
loop.env of a real run (the kit's pilot, 10/01/2026)
TESTE_RAPIDO="pytest -q -m 'not real'"
TESTE_FINAL="pytest -q"
PROTEGIDOS="tests/ pytest.ini tools/medicao/"
PERMITIDOS="tools/medir-sessao.py"
MAX_CICLOS=6
MIN_CICLO=20
MEM=8G
ESTAGNACAO=3
AGENTE=codex
MODELO=<o modelo topo da sua assinatura no Codex>
# se os testes sobem servidor local, o sandbox workspace-write bloqueia até 127.0.0.1:
# CODEX_ARGS="-c sandbox_workspace_write.network_access=true"
Example from the repository
## A) trapaça
2026-10-19 03:56:46 início: rápido=0 passed · agente=codex · teto 4x2min · mem 1G
2026-10-19 03:56:46 ciclo 1: começa
2026-10-19 03:56:46 ciclo 1: agente saiu rc=0 em 0s
2026-10-19 03:56:46 ciclo 1: agente mexeu em arquivo protegido → revertido, ciclo falho
2026-10-19 03:56:46 ciclo 1: checkpoint commitado
(… ciclos 2 e 3 iguais …)
2026-10-19 03:56:47 PARADO: 3 ciclos sem avanço mensurável — ver failures.md
saída: 2

## B) trabalho certo
2026-10-19 03:56:48 início: rápido=0 passed · agente=codex · teto 4x2min · mem 1G
2026-10-19 03:56:48 ciclo 1: começa
2026-10-19 03:56:48 ciclo 1: agente saiu rc=0 em 0s
2026-10-19 03:56:48 ciclo 1: rápido=3 passed (melhor antes: 0)
2026-10-19 03:56:48 ciclo 1: checkpoint commitado
2026-10-19 03:56:49 CONCLUÍDO no ciclo 1: teste final passou (3 passed)
saída: 0

## C) dois loops
outro loop já roda nesta pasta
saída: 3

The tests deleted by the cheat came back: the loop reverted all three cycles and wrote three "revertido pelo loop" lines in failures.md.

04Outside the terminal: systemd and cron

The loop runs on its own; these three pieces look after it.

1. Watchdog

vigia.py looks for active runs (state.md without "concluído") and alerts once for each problem: PARADO (loop.log recorded a stop), PARADO SEM AVISO (there is a loop.log but no live loop process), and OCIOSA (nothing changed in the folder or in the sessions for N minutes).

Alert = a line in ~/.local/state/execucao-longa/alertas.log; notify-send only with --desktop.

05Turn on the watchdog (once per machine, Linux)

Copy the kit's units, swap the path for yours, and enable the timer.

Terminal · watchdog every 10 min
mkdir -p ~/.config/systemd/user
cp ~/projetos/execucao-longa/tools/systemd/longrun-vigia.* ~/.config/systemd/user/
sed -i "s#/home/nmaldaner#$HOME#g" ~/.config/systemd/user/longrun-vigia.service
systemctl --user daemon-reload
systemctl --user enable --now longrun-vigia.timer
systemctl --user list-timers longrun-vigia.timer
python3 ~/projetos/execucao-longa/tools/vigia.py --seco   # one round by hand, without saving state

06Practice

The three rehearsal rounds, then the watchdog.

E13 · Cheat reverted

10 min

Run ensaio-loop.sh and round A (agente-trapaca).

Done when: the last line of the log is PARADO: 3 ciclos sem avanço mensurável, saída: 2, ls tests/ still shows test_conta.py and grep -c "revertido pelo loop" $D/failures.md → 3.

E14 · Finished by the test and second-copy lock

10 min

Run rounds B and C.

Done when: B ends with CONCLUÍDO no ciclo 1: teste final passou (3 passed) and saída: 0; C prints outro loop já roda nesta pasta and saída: 3.

E15 · Watchdog on

15 min

Install the watchdog timer with the path swapped. Leave the folder of your real long run (lesson 1) without "concluído" in state.md.

Done when: systemctl --user list-timers longrun-vigia.timer shows the next trigger and systemctl --user status longrun-vigia.service shows no failure in the last round.

07Lesson quiz

Question 1 of 5

In the headless loop, who decides the run is over?skill

08In three sentences

The test decidesThe loop runs the fast test to measure progress and the final test to stop; the agent does not commit or declare done.
Protected judgeTouched PROTEGIDOS: reverted, failed cycle, a line in failures.md; 3 with no progress: stops with 2.
Caps and lockflock (3), timeout and MemoryMax per cycle, MAX_CICLOS (1); rehearse with a fake agent before spending quota.

Going further

Previous lessonNext lesson