20260914 #5 — El día tres: el loop deja huella
El día tres corrí un ticket y no pasó nada visible. Arreglar por qué el loop de agentes no imprimía output me llevó a decidir, esa misma madrugada, que cada corrida quedara guardada en un archivo — y esa noche el registro nuevo capturó lo que antes se perdía.
El día uno armé el loop de agentes. El día dos, el registro de sesiones. El día tres corrí ./run-ticket.sh US-04 y no pasó nada: ni error, ni output, ni un ticket ejecutado. El loop llevaba dos días funcionando y esa madrugada, por primera vez, no se lo podía ver.
Un script que no decía nada
La causa era chica y un poco humillante. run-ticket.sh busca el ticket por el nombre del archivo, pero las veinte user stories del sprint vivían en 01-Stories/20260815Sprint/ como NN_slug.md, sin el prefijo US-. grep -i "US-04" no encontraba nada, y con set -euo pipefail activo el pipeline find | grep | head fallaba y mataba el script antes de que llegara a imprimir el mensaje de error que yo mismo había escrito. El bug no era solo de US-04: afectaba a las veinte historias del sprint por igual.
El fix agregó un segundo intento de matching por contenido —buscar el encabezado # US-04 — ... dentro del archivo cuando el nombre no alcanza— y neutralizó pipefail con || true en los dos pipelines, para que el bloque de error existente sí se ejecutara cuando correspondía. En la verificación apareció un segundo bug adentro del primero: sin anclar la búsqueda al encabezado, US-01 matcheaba TECH-011_smtp_resend.md porque ese ticket mencionaba "Descubierto durante US-01" en el cuerpo. Se corrigió anclando el grep a ^# US-01. Cerré esa sesión a las 03:06 sin haber corrido US-04 de punta a punta — arreglar el matching no era lo mismo que invocar el loop completo, y eso quedó para después.
Un archivo por corrida
La sesión siguiente, la misma madrugada, encaró algo distinto: el loop corría, pero su output vivía solo en la terminal. Si se cortaba, si quería comparar dos corridas del mismo ticket, o simplemente saber cuánto había costado una ejecución, no había dónde mirar. La decisión fue guardar cada corrida en su propio archivo con timestamp — history/<TICKET_ID>_<timestamp>.json — en vez de un archivo fijo por ticket que cada corrida nueva pisara. Esa noche yo ya venía generando algunos de esos archivos a mano, sin timestamp, corriendo tickets reales en paralelo; me preguntó si prefería alinear la convención a eso y elegí seguir con el timestamp de todos modos, para no perder el historial de corridas repetidas de un mismo ticket.
La implementación fue un tee al final del pipeline: claude ... | tee "$HISTORY_FILE", verificado con bash -n y una simulación aislada del pipe, sin invocar claude de verdad para no gastar en una prueba. Cerré esa sesión a las 03:46. La primera corrida con la convención nueva, US-06_20260817-034659.json, arrancó ese mismo minuto.
Lo que el registro vio esa misma noche
A partir de ahí, cada corrida dejaba huella, y esa misma noche el registro nuevo capturó algo que antes se perdía. Entre esa madrugada y la noche siguiente quedaron 29 corridas válidas en history/ —casi 500 turnos en total, alrededor de USD 55—, 20 terminadas en DONE y 5 en BLOCKED.
US-11 fue la que más mostró. Corrió cuatro veces: dos BLOCKED tempranas sin llegar a invocar al coder, una tercera cortada a los 49 turnos por "Credit balance is too low" —ahí el coder había tocado database.types.ts, un archivo generado por Supabase, y de paso había roto un flujo de otra user story ya terminada; el coordinator lo detectó y devolvió el ticket antes de que el crédito se cortara—, y una cuarta, ya con un fix acotado, que terminó DONE. El ticket quedó marcado blocked en el registro de todos modos: un falso positivo del propio run-ticket.sh, que buscaba la palabra "BLOCKED" en cualquier parte del texto en vez de en la línea de estado, y que se corrigió a mano después de documentarlo. El sistema que por fin dejaba ver el loop nació con su propio bug de lectura.
Lo que quedó
Hoy, un mes después, las tres decisiones de esa madrugada siguen intactas en el molde que copio a cada proyecto nuevo: pipefail activo, el fallback por encabezado, tee escribiendo a history/. Son más de 500 archivos repartidos en ocho proyectos. Cada corrida del loop —incluida la que escribió esta nota— deja un registro en algún history/, y de ahí sale, entre otras cosas, el costo de cada una.
El día tres no agregó una función nueva al producto. Agregó la posibilidad de mirar hacia atrás y saber qué había pasado. ¿Qué guardás de cada corrida de un agente, y quién lo lee después?
Comentarios
Los comentarios no están disponibles en este momento.
- Cargando comentarios…
Para comentar, entrá o registrate.