Estado fuera del modelo: archivos y git como memoria
En un loop largo, el contexto del modelo va a ser compactado, reseteado o cambiado por una sesión nueva. Lo que solo existía en la conversación desaparece o vuelve como resumen, mezclando hecho y suposición. La salida: sesión descartable, estado en archivos y en git, y todo ciclo empieza releyendo. Tú armas un ciclo así y lo interrumpes a propósito.
1Por qué la memoria del modelo no aguanta un loop largo
2La lista de ítems con passes, el archivo de progreso y el git log
3Interrumpir y retomar sin rehacer ni saltar
4Git como deshacer y como historial del loop
Objetivo: armar un ciclo descartable que relee el estado en archivo, hace un ítem, verifica y hace commit, y retomar después de interrumpir sin rehacer nada.
01 · Una sesión descartable, un estado que se queda
Haz clic en cada paso del ciclo: qué lee y qué deja grabado.
Clase 501 · Una sesión descartable, un estado que se quedaHaz clic en cada paso del ciclo: qué lee y qué deja grabado.
La sesión termina o se cae; la siguiente empieza releyendo los archivos y sigue desde el primer pendiente.
1/6
Sesión nueva
Puede ser un /clear, una compactación, un codex exec nuevo, el Ralph llamando de nuevo u otra persona tomando el relevo. El diseño parte del peor caso: la sesión no recuerda nada y puede interrumpirse a la mitad.
Desplázate por la página: cada etapa de abajo ilumina su parte del diagrama.
1/6
Sesión nueva
Puede ser un /clear, una compactación, un codex exec nuevo, el Ralph llamando de nuevo u otra persona tomando el relevo. El diseño parte del peor caso: la sesión no recuerda nada y puede interrumpirse a la mitad.
2/6
Relee el estado
En el harness de larga duración de Anthropic (26/11/2025), toda sesión empieza así: dónde estoy (pwd), qué se hizo (git log y el archivo de progreso), cuál es el próximo ítem, y una prueba básica antes de programar. La lista de ítems es JSON con el campo passes; el post dice que el modelo sobrescribe menos el JSON que el Markdown.
3/6
Hace un ítem
Elige el primer ítem pendiente y hace solo ese. En el post de Anthropic, las sesiones siguientes solo pueden cambiar el campo passes; no reescriben ni borran ítems de la lista.
4/6
Verifica y marca
El ítem solo pasa a passes: true si la verificación de ese ítem pasó. La línea en el progreso dice cuándo y el resultado. Si falló, el ítem sigue pendiente y el motivo queda registrado.
5/6
Commit
El commit es el checkpoint: si la próxima sesión se cae, nada de lo que se verificó se pierde; si un ítem salió mal, git revert lo deshace. LOOP-R usa el mismo principio para las versiones del proceso ("git es el mecanismo de promoción y rollback"; detalle en el curso 5).
6/6
Relee la carpeta, no la conversación
La sesión es descartable; el estado no. Lo que necesita sobrevivir a una compactación, a un reset o a un cambio de modelo va a un archivo y a un commit antes del fin del ciclo.
El kit execucao-longa lleva la idea a siete archivos (goal, plan, state, progress, failures, decisions, canal): es la clase 1 del curso 2.
02 · Un ciclo descartable en Python
Cada ejecución es una "sesión" que no recuerda nada: relee, hace un ítem, verifica, graba y hace commit. Sin modelo: la acción es escribir un archivo.
Terminal · preparar la carpeta
mkdir -p ~/ensaio-loop/estado && cd ~/ensaio-loop/estado && git init -q
cat > features.json <<'EOF'
[
{"id":"f1","descricao":"ler o CSV","passes":false},
{"id":"f2","descricao":"contar linhas","passes":false},
{"id":"f3","descricao":"gravar o resumo","passes":false}
]
EOF
git add -A && git commit -qm inicio
ciclo.py
#!/usr/bin/env python3
"""Un ciclo que empieza de cero cada vez: relee el estado en los archivos, hace UN ítem, graba y hace commit."""
import json, subprocess, sys, datetime, pathlib
estado = pathlib.Path("features.json")
itens = json.loads(estado.read_text())
pendentes = [i for i in itens if not i["passes"]]
if not pendentes:
print("nada pendente: objetivo cumprido")
sys.exit(0)
item = pendentes[0]
print(f"releu features.json: {len(itens) - len(pendentes)} feitos, {len(pendentes)} pendentes")
print(f"faz: {item['id']} ({item['descricao']})")
pathlib.Path(f"{item['id']}.txt").write_text(item["descricao"] + "\n") # la "acción"
item["passes"] = pathlib.Path(f"{item['id']}.txt").exists() # la "verificación"
estado.write_text(json.dumps(itens, ensure_ascii=False, indent=2) + "\n")
agora = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
with open("progress.md", "a") as f:
f.write(f"| {agora} | {item['id']} | passes={item['passes']} |\n")
subprocess.run(["git", "add", "-A"], check=True)
subprocess.run(["git", "commit", "-qm", f"feito: {item['id']}"], check=True)
print(f"commit: feito: {item['id']}")
Terminal · ejecutar, "caer" y retomar
python3 ciclo.py
# cierra el terminal aquí, abre otro y vuelve a la carpeta
cd ~/ensaio-loop/estado
python3 ciclo.py && python3 ciclo.py && python3 ciclo.py
git log --oneline
Los hashes cambian en tu máquina; el orden y los mensajes no.
Cambia la línea de la "acción" por una llamada al agente (codex exec o claude -p) y la de la "verificación" por una prueba que se ejecute de verdad. El resto (releer, un ítem, marcar solo con prueba, commit) queda igual. La verificación de esta versión de juguete es débil a propósito: solo comprueba que el archivo existe (nivel 2 en la escala de la clase 6).
03 · Interrumpe a la mitad y mira la retomada
Cada tarea es un ciclo. Ejecuta, interrumpe y ejecuta de nuevo: lo que ya tiene commit no se rehace.
pendientes (passes: false)
en esta sesión
passes: true + commit
la verificación falló
Simulación ilustrativa en el navegador. El fallo es a propósito: un ítem sin prueba no pasa a passes: true.
04 · Práctica
En tu máquina. No gasta cuota: no hay modelo en el ciclo.
E1 · Tres sesiones, tres commits
10 min
Prepara la carpeta, pega el ciclo.py y ejecútalo una vez. Cierra la terminal, abre otra y ejecútalo hasta que aparezca "nada pendente".
Listo cuando: La última ronda imprime nada pendente: objetivo cumprido, y git log --oneline | wc -l → 4.
E2 · La verificación que reprueba
15 min
Agrega el ítem f4 "arquivo vazio" y cambia la verificación para exigir un archivo no vacío (stat().st_size > 0). Haz que la acción de f4 grabe un archivo vacío y ejecútalo.
Listo cuando:python3 -c "import json;print([i['passes'] for i in json.load(open('features.json'))])" → [True, True, True, False], y tail -1 progress.md termina con passes=False |.
E3 · La lista de un trabajo tuyo
15 min
Elige una tarea real de varias horas y divídela en al menos 5 ítems verificables en un features.json, todos con passes: false, en una carpeta de proyecto con git.
Listo cuando:python3 -c "import json;d=json.load(open('features.json'));print(len(d)>=5, any(i['passes'] for i in d))" → True False, y git log --oneline -1 -- features.json muestra el commit de la lista.
05 · Prueba de la lección
Pregunta 1 de 4
Después de una compactación, el resumen dice "f3 concluído", pero features.json muestra f3 con passes: false y no hay commit de f3. ¿Qué vale?competencia
Correcto
El resumen mezcla lo confirmado con la suposición. Lo que tiene prueba es lo que está en el archivo con commit. Marcar sin verificar es exactamente el atajo que la lista existe para impedir.
No es esa
El resumen mezcla lo confirmado con la suposición. Lo que tiene prueba es lo que está en el archivo con commit. Marcar sin verificar es exactamente el atajo que la lista existe para impedir.Repasar: Una sesión descartable, un estado que se queda
Pregunta 2 de 4
¿Por qué el post de harness de Anthropic guarda la lista de ítems en JSON y no en Markdown?
Correcto
Es la razón que da el propio post: con JSON el modelo tiende a cambiar solo el campo passes, en lugar de reescribir la lista. Las sesiones siguientes solo pueden cambiar ese campo.
No es esa
Es la razón que da el propio post: con JSON el modelo tiende a cambiar solo el campo passes, en lugar de reescribir la lista. Las sesiones siguientes solo pueden cambiar ese campo.Repasar: Una sesión descartable, un estado que se queda
Pregunta 3 de 4
¿Cuál es la ventaja de un commit por ítem en lugar de un commit al final?
Correcto
Si la sesión se cae, lo que fue verificado queda guardado; si un ítem salió mal, git revert deshace solo ese. Un commit al final lo pierde todo en una caída y mezcla aciertos con errores.
No es esa
Si la sesión se cae, lo que fue verificado queda guardado; si un ítem salió mal, git revert deshace solo ese. Un commit al final lo pierde todo en una caída y mezcla aciertos con errores.Repasar: Un ciclo descartable en Python
Pregunta 4 de 4
En la simulación, la sesión se cayó en medio de un ítem. ¿Qué pasa con él al reanudar?competencia
Correcto
Sin verificación y sin commit, no hay prueba de que terminó: vuelve a pendientes y se rehace. Lo que ya tiene commit no se rehace.