Ejecución larga|INEMA.CLUB-PRO

Clase 6 · 20 min

Loop headless: loop-longrun.sh, flock, timeout y MemoryMax

En el /goal interactivo, quien decide que terminó es un evaluador que lee la conversación. En el loop headless, quien decide es la prueba, ejecutada por el propio loop, con los archivos de prueba protegidos. En esta lección ejecutas el loop-longrun.sh con un agente falso: ves cómo concluye, ves cómo se revierte la trampa y ves cómo la trava impide dos loops en la misma carpeta, sin gastar cuota.

Comenzar la clase
Rack de servidores en una sala oscura con LEDs encendidos y ningún monitor encendido.

En esta clase vas a

  1. 1El loop.env: pruebas, protegidos, permitidos y topes
  2. 2flock, timeout y MemoryMax en cada ciclo
  3. 3Prueba protegida modificada = revertida y ciclo fallido
  4. 4systemd: vigila cada 10 min y cron con flock

Objetivo: ejecutar el loop-longrun.sh con un agente falso y ver cómo la prueba decide: concluido, trampa revertida, estancamiento y trava de segunda copia.

01Un ciclo del loop-longrun.sh

El agente trabaja; el loop decide. Haz clic en cada etapa.

Haz clic en cada bloque del diagrama para ver qué hace.

Si lo que falta solo aparece en la prueba final (lenta), cada ciclo cuenta como "sin avance": pon esos casos también en la prueba rápida o aumenta ESTAGNACAO (aviso del propio script).

02Mira cómo decide el loop

Ejecútalo, interrúmpelo a la mitad y vuelve a ejecutarlo. Uno de los ciclos toca una prueba protegida.

ciclos por ejecutar

ciclo en curso

checkpoint (commit)

revertido

Simulación ilustrativa en el navegador. El ensayo de verdad, con agente falso, está justo abajo.

03Ensayo con agente falso

El loop acepta CODEX_BIN: si apuntas a un script, no se llama a ningún modelo. Es la forma barata de probar tu loop.env antes de gastar cuota.

ensaio-loop.sh · monta el proyecto de prueba
# ensaio-loop.sh — monta un proyecto de prueba con 3 pruebas congeladas y dos 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 "Implementa conta_palavras en conta.py. Lee goal.md, state.md y plan.md antes. No toques 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 · las tres rondas
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) trampa
CODEX_BIN=/tmp/ensaio-loop/agente-bom.sh $L $D; echo "saída: $?"       # B) trabajo correcto

CODEX_BIN=/tmp/ensaio-loop/agente-lento.sh $L $D > /dev/null 2>&1 &   # C) dos loops
sleep 2; CODEX_BIN=/tmp/ensaio-loop/agente-lento.sh $L $D; echo "saída: $?"; wait
loop.env de una ejecución real (el piloto del kit, 01/10/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=<el modelo top de tu suscripción en Codex>
# si las pruebas levantan un servidor local, el sandbox workspace-write bloquea hasta 127.0.0.1:
# CODEX_ARGS="-c sandbox_workspace_write.network_access=true"
Ejemplo del repositorio
## 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

Las pruebas borradas por la trampa volvieron: el loop revirtió los tres ciclos y escribió tres líneas "revertido pelo loop" en failures.md.

04Fuera de la terminal: systemd y cron

El loop corre solo; estas tres piezas lo cuidan.

1. Vigia

El vigia.py busca ejecuciones activas (state.md sin "concluído") y alerta una vez por cada problema: PARADO (el loop.log registró una parada), PARADO SEM AVISO (hay loop.log pero ningún proceso del loop vivo) y OCIOSA (nada cambió en la carpeta ni en las sesiones hace N minutos).

Alerta = línea en ~/.local/state/execucao-longa/alertas.log; notify-send solo con --desktop.

05Activar el vigia (una vez por máquina, Linux)

Copia las units del kit, cambia la ruta por la tuya y activa el timer.

Terminal · vigia cada 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   # una ronda a mano, sin guardar estado

06Práctica

Las tres rondas del ensayo, después el vigia.

E13 · Trampa revertida

10 min

Ejecuta ensaio-loop.sh y la ronda A (agente-trapaca).

Listo cuando: la última línea del log es PARADO: 3 ciclos sem avanço mensurável, saída: 2, ls tests/ todavía muestra test_conta.py y grep -c "revertido pelo loop" $D/failures.md → 3.

E14 · Terminado por la prueba y traba de segunda copia

10 min

Ejecuta las rondas B y C.

Listo cuando: B termina con CONCLUÍDO no ciclo 1: teste final passou (3 passed) y saída: 0; C imprime outro loop já roda nesta pasta y saída: 3.

E15 · Vigia activado

15 min

Instala el timer del vigia con la ruta cambiada. Deja la carpeta de tu ejecución real (clase 1) sin "concluído" en state.md.

Listo cuando: systemctl --user list-timers longrun-vigia.timer muestra el próximo disparo y systemctl --user status longrun-vigia.service no muestra falla en la última ronda.

07Prueba de la clase

Pregunta 1 de 5

En el loop headless, ¿quién decide que la ejecución terminó?competencia

08En tres frases

La prueba decideEl loop corre la prueba rápida para medir el avance y la final para parar; el agente no hace commit ni declara que terminó.
Juez protegidoTocó PROTEGIDOS: revertido, ciclo fallido, línea en failures.md; 3 sin avance: se detiene con 2.
Topes y trabaflock (3), timeout y MemoryMax por ciclo, MAX_CICLOS (1); ensaya con un agente falso antes de gastar cuota.

Para ir más allá

Clase anteriorPróxima clase