E12 · Interromper e retomar
10 minClone o jornada-agentes e rode as partes 1 e 2 do ensaio.
Pronto quando: aparecem rodando: 03.md · feito: 01.md 02.md e depois execuções: 5 para 4 tarefas.
Aula 6 · 20 min
Uma fila em pastas sobrevive a queda: o que estava rodando volta para a entrada. Mas a mesma retomada que salva uma cópia atropela a outra quando duas rodam na mesma pasta. Nesta aula você interrompe e retoma uma fila, provoca a colisão com números, trava com flock e vê a guarda de colisão perguntar antes de editar.
Começar a aula
Objetivo: interromper e retomar uma fila, provocar a colisão de duas filas na mesma pasta e travar com flock e com a guarda de colisão.
A fila caseira do jornada-agentes, em pastas. Clique em cada estado.
A fila durável de verdade, com banco, tentativas e portões, é o inemaccbot do curso de Filas. Aqui a ideia mínima, para ver a falha.
Rode, mate o processo no meio e rode de novo. Uma tarefa falha na primeira execução.
Simulação ilustrativa no navegador. O ensaio de verdade, com o script e um claude falso, está logo abaixo.
Um claude FALSO no PATH anota cada execução num log. Contar linhas do log mostra quantas vezes cada tarefa rodou.
cd ~/jornada-agentes # git clone https://github.com/inematds/jornada-agentes.git ~/jornada-agentes
E=${TMPDIR:-/tmp}/ensaio-multi/fila; rm -rf $E && mkdir -p $E/bin
cat > $E/bin/claude <<'SH'
#!/usr/bin/env bash
# claude FALSO: anota quem rodou o quê, demora 1 s e responde (nenhum modelo é chamado)
echo "$PPID $2" >> "${E_LOG:?}"; sleep 1; echo "resposta para: $2"
SH
chmod +x $E/bin/claude; export PATH=$E/bin:$PATH E_LOG=$E/execucoes.log
nova_fila() { rm -rf $E/fila $E_LOG; mkdir -p $E/fila/entrada; for i in 1 2 3 4; do echo "tarefa $i" > $E/fila/entrada/0$i.md; done; }
F=./ferramentas/fila-caseira.sh
echo "## 1) roda e é interrompida no meio"
nova_fila; timeout -s INT 2.5 $F $E/fila
echo "rodando: $(ls $E/fila/rodando) · feito: $(ls $E/fila/feito | tr '\n' ' ')"
echo "## 2) roda de novo: retoma"
$F $E/fila | tail -1
echo "execuções: $(wc -l < $E_LOG) para 4 tarefas"
echo "## 3) duas filas na mesma pasta, ao mesmo tempo"
nova_fila; $F $E/fila > $E/a.log 2>&1 & $F $E/fila > $E/b.log 2>&1 & wait
echo "execuções: $(wc -l < $E_LOG) para 4 tarefas · repetidas: $(cut -d' ' -f2- $E_LOG | sort | uniq -d | tr '\n' ' ')"
echo "## 4) a mesma coisa com flock"
nova_fila; flock -n $E/fila.lock $F $E/fila > $E/a.log 2>&1 &
sleep 0.3; flock -n $E/fila.lock $F $E/fila || echo "outra fila já roda nesta pasta (saída $?)"; wait
echo "execuções: $(wc -l < $E_LOG) para 4 tarefas"## 1) roda e é interrompida no meio → 01.md feito → 02.md feito → 03.md rodando: 03.md · feito: 01.md 02.md ## 2) roda de novo: retoma entrada: 0 · feito: 4 · falhou: 0 execuções: 5 para 4 tarefas ## 3) duas filas na mesma pasta, ao mesmo tempo execuções: 8 para 4 tarefas · repetidas: tarefa 1 tarefa 2 tarefa 3 tarefa 4 ## 4) a mesma coisa com flock outra fila já roda nesta pasta (saída 1) execuções: 4 para 4 tarefas
Rodado três vezes, mesmo resultado. Em 2, a tarefa 3 rodou duas vezes (interrompida e retomada): a tarefa tem de aguentar rodar de novo. Em 3, as quatro rodaram duas vezes. Em 4, a segunda cópia nem começou.
O log de uma das duas cópias da rodada 3 (caminho encurtado para $E):
→ 01.md rm: cannot remove '$E/fila/fila/rodando/01.md': No such file or directory feito mv: cannot stat '$E/fila/fila/entrada/02.md': No such file or directory → 02.md (…)
O mv falha porque a outra cópia já pegou o arquivo, mas o script segue e roda a tarefa mesmo assim, com o arquivo que a outra moveu para rodando/. E a retomada de uma cópia devolve à entrada o que a outra está rodando. Não é defeito de um agente: o script foi feito para uma cópia só, e a trava é o que garante isso.
O projeto de treino traz colisao.mjs, um hook que anota quem editou cada arquivo e pergunta antes de outra sessão editar. Aqui, só a detecção; hook como guarda é o curso 4.
cd ~/treino-multi
export INEMA_RUNTIME_ESTADO=${TMPDIR:-/tmp}/ensaio-multi/colisao; rm -rf $INEMA_RUNTIME_ESTADO
G=runtime/mods/runtime-guarda/hooks/colisao.mjs
j() { printf '{"session_id":"%s","cwd":"%s","tool_input":{"file_path":"notas.md"}}' "$1" "$PWD"; }
echo "rascunho" > notas.md
j sessao-AAAAAAAA | node $G pos # a sessão A acabou de editar notas.md
echo "## A quer editar de novo"; j sessao-AAAAAAAA | node $G pre; echo "(saída $?)"
echo "## B quer editar o mesmo arquivo"; j sessao-BBBBBBBB | node $G pre; echo; echo "(saída $?)"## A quer editar de novo
(saída 0)
## B quer editar o mesmo arquivo
{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"ask","permissionDecisionReason":"Guarda de colisão: Outra sessão (sessao-A) editou notas.md agora há pouco. Seguir mesmo assim, usar outra cópia (worktree) ou cancelar?"}}
(saída 0)A mesma sessão passa calada. A outra recebe "permissionDecision":"ask": o Claude Code para e pergunta. A janela padrão é de 30 min (INEMA_COLISAO_MIN). INEMA_RUNTIME_ESTADO manda o registro para a pasta de ensaio, para não misturar com o seu real.
Do LESSONS.md do projeto de monitoramento do INEMA, com data. Cada uma virou uma regra de uma linha.
Cursos feitos em paralelo por subagentes dividiam a mesma pasta temporária. Pasta de imagens com nome genérico colidiu: o script de um agente pulou a imagem porque a arte do outro já existia.
Regra: pasta temporária com o id da tarefa no nome.
Em arquivo dividido entre sessões (pendências, limites), editar pelo número da linha errou o alvo: outra sessão tinha inserido no topo.
Regra: casar pelo texto da própria linha, nunca pelo número.
Uma imagem gerada direto na pasta do repo foi commitada por outra sessão que rodou git add -A. E edições do portal foram commitadas por outra sessão antes da hora.
Regra: gerar fora do repo e preparar a mudança em worktree isolada, mesmo com os arquivos limpos.
Um servidor de teste de outra sessão foi derrubado porque ocupava a porta "de sempre".
Regra: porta livre de verdade e só matar processo depois de conferir que a pasta dele (/proc/[pid]/cwd) é a sua.
Fila, colisão e guarda, sem gastar cota.
Clone o jornada-agentes e rode as partes 1 e 2 do ensaio.
Pronto quando: aparecem rodando: 03.md · feito: 01.md 02.md e depois execuções: 5 para 4 tarefas.
Rode as partes 3 e 4. Depois escreva, numa linha do seu AGENTS.md ou CLAUDE.md, a regra de trava para os scripts de fila que você usa.
Pronto quando: a parte 3 imprime execuções: 8 para 4 tarefas; a parte 4 imprime outra fila já roda nesta pasta (saída 1) e execuções: 4 para 4 tarefas; grep -c flock AGENTS.md CLAUDE.md acha a sua regra.
Rode o ensaio da guarda no projeto de treino.
Pronto quando: a sessão A sai sem texto e (saída 0); a sessão B imprime "permissionDecision":"ask" com Outra sessão (sessao-A) editou notas.md.
Pergunta 1 de 4
A fila caiu no meio da tarefa 3. Na retomada, quantas vezes a tarefa 3 terá rodado?competência
Pergunta 2 de 4
Duas cópias da fila caseira na mesma pasta. O que o ensaio mostrou?competência
Pergunta 3 de 4
Qual a correção mínima para a colisão de duas filas?
Pergunta 4 de 4
O que a guarda de colisão responde quando outra sessão editou o arquivo há pouco?
Escolha uma alternativa. A explicação aparece depois da sua tentativa.