MÓDULO 2.2

O Cérebro do Loop: Lifecycle, Findings e Sumarização

📚

Tópicos

7

⏱️

Minutos

60

🎯

Nível

Intermediário

🔧

Tipo

Prático

1

Prompt 03 — Stop Hook Lifecycle: BLOCK vs APPROVE

Substituindo o stub pelo cérebro real do sistema

O Prompt 03 é o maior salto do projeto: o hook sai de stub incondicional para o engine que orquestra todo o ciclo de vida do Claudex. Cada fase produz uma decisão diferente — algumas aprovam silenciosamente, outras bloqueiam com instruções detalhadas para Claude. A máquina de estados testada no Prompt 02 finalmente ganha um cérebro.

💡 Conceito Principal: BLOCK como instrução

O Stop hook não é apenas um porteiro (deixa passar / não deixa). Quando retorna BLOCK, ele entrega um reason que o Claude Code mostra a Claude como instrução. É um canal de comunicação unidirecional do hook para Claude, com conteúdo Markdown estruturado.

# Approve: turno termina normalmente
{"decision": "approve"}

# Block: Claude precisa agir antes de encerrar
{"decision": "block", "reason": "### Claudex round 1 of 3\n\nRun the reviewer:\n\`\`\`bash\nbash .claude/claudex/20240101-120000-abc123-runner.sh\n\`\`\`\n\nThen read findings from: ..."}

O Prompt Real (Cole no Claude Code)

03_stop_hook_lifecycle.md
Replace the fail-open stub in `plugins/claudex/hooks/stop-hook.sh`
with the real lifecycle engine. Read `SPEC.md` for the phase table.

Build:

1. **Helpers at the top.** `log()` appends to `.claude/claudex/log`
   with UTC timestamp. `approve(reason)` logs and prints
   `{"decision":"approve"}`. `block(reason)` logs the first 80 chars,
   JSON-escapes the reason via `python3 -c 'import sys, json;
   print(json.dumps(sys.stdin.read()))'` with a sed-based fallback if
   python3 is missing, then prints `{"decision":"block","reason":...}`.

2. **ERR trap unconditional.** First active line of the script.
   Catches anything not explicitly handled and fails open.

3. **Source state-helpers.sh** (or fail-open if it's missing).

4. **Find the active loop.** If none, approve. If the review_id format
   is invalid, remove the bad state file and approve.

5. **Read state fields.** mode, phase, round, max_rounds,
   decision_signal, topic, repo_root, started_at_epoch.

6. **cwd validation.** If `repo_root` differs from current `pwd`,
   fail open with a log line.

7. **Validate numerics.** Garbage `round` or `max_rounds` falls back
   to 1 / max-default.

8. **`write_runner_script` helper.** Builds a self-contained bash script
   at `.claude/claudex/<id>-runner.sh`. The script writes the prompt to
   a heredoc with `<<'PROMPTEOF'` (single quotes — critical: prevents
   shell expansion) then runs
   `codex exec --dangerously-bypass-approvals-and-sandbox < $PROMPT_FILE`.

9. **Plan mode case statement.**
   - `drafting`: if PLAN.md missing → BLOCK. If present → CAS to
     `reviewing`, build round-1 Codex prompt, write runner. BLOCK with
     instructions to run the runner, read findings, and either revise
     PLAN.md or call `mark-done.sh`.
   - `reviewing`: if `decision_signal=no-material-findings` → CAS to
     `done`, cleanup, approve. Otherwise increment round; if new round
     > max_rounds, set `decision_signal=max-reached`, CAS to `done`,
     cleanup, approve. Otherwise: write next-round runner, BLOCK.
   - `done`: cleanup, approve.
   - any other phase: log unknown phase, approve.

10. **Review mode case statement.**
    - `reviewing`: write runner for diff review. CAS to `done`. BLOCK.
    - `done`: cleanup, approve.

11. **`plugins/claudex/scripts/mark-done.sh`.** Takes review_id arg.
    Sets `decision_signal=no-material-findings` and `phase=done`.

12. **Smoke test** at `plugins/claudex/tests/smoke-test.sh`. Spins up
    temp git repo. Fires hook, asserts BLOCK. Asserts state=reviewing.
    Asserts runner written. Fires again, asserts round increment. Asserts
    mark-done + hook fire = approve. Asserts concurrent-loop refusal.

Run both test scripts. Tell me the counts.

**Do NOT actually call Codex in any test.**

Fazer

  • JSON-escapar o reason do BLOCK via python3 (fallback sed)
  • ERR trap na primeira linha ativa, antes de qualquer import
  • Usar <<'PROMPTEOF' com aspas simples no heredoc
  • Validar numerics com fallback antes de usar em aritmética

Evitar

  • Interpolação de variáveis dentro do heredoc do prompt
  • Testar com Codex real no smoke-test (lento e flaky)
  • Continuar sem verificar que o runner foi criado
  • Usar echo sem -n para JSON (newlines quebram o parse)
2

Como o Hook Controla Cada Fase

Lógica de transição e decisões por estado

O hook é acionado no mesmo evento — Stop (fim de turno de Claude) — mas o que ele faz depende completamente da fase atual registrada no state file. Cada fase tem uma decisão principal e uma condição de transição diferente.

Fase Quem Age Hook Faz
drafting Claude escreve PLAN.md Verifica arquivo → CAS para reviewing → escreve runner → BLOCK com instruções
reviewing Claude roda runner e lê findings Se no-material-findings → summarizing. Se max → summarizing. Else → incrementa round, novo runner, BLOCK
summarizing Claude imprime o resumo CAS para done → limpa runner/lock → APPROVE silencioso
done Terminal Limpa artefatos restantes → APPROVE
cancelled / errored Terminal APPROVE imediato
🔍 Os 4 Canais de Comunicação
Hook → Claude stdout JSON: {"decision":"block","reason":"..."}
Claude → Codex runner script com prompt via heredoc; codex exec --dangerously-bypass-approvals-and-sandbox
Codex → Claude stdout do runner (transcript completo) + findings-round-N.md (bullets limpos)
Claude → Hook mutações no state file (mark-done.sh seta decision_signal=no-material-findings)
Dica Prática

Claude nunca sabe diretamente qual fase está ativa — ele só vê o que o hook diz. O hook lê o state file e decide o que instrucionar Claude a fazer. Isso é intencional: o hook é o único agente que tem visão completa do estado. Claude age como executor das instruções do hook.

3

O Runner Script: Como Claude Delega para o Codex

Geração dinâmica de scripts para invocação do Codex

O runner script é um dos mecanismos mais elegantes do Claudex. Em vez de Claude chamar o Codex diretamente (o que criaria problemas de permissão e rastreabilidade), o hook gera um shell script autocontido que Claude pode inspecionar e executar. O script encapsula o prompt como heredoc com aspas simples — proteção crítica contra expansão de variáveis.

💡 Estrutura do Runner Script
#!/usr/bin/env bash
# Claudex runner — Round 1 of 3 (Senior-engineer review)
# Generated: 2024-01-15T12:00:00Z

set -euo pipefail

PROMPT_FILE=".claude/claudex/20240115-120000-abc123-prompt.txt"

# ASPAS SIMPLES: impede expansão de $variáveis dentro do heredoc
cat > "$PROMPT_FILE" <<'PROMPTEOF'
You are a skeptical senior engineer reviewing PLAN.md.

[PERSONA STANZA]

Review PLAN.md in the current directory. Focus on:
- Design flaws and broken assumptions
- Missing edge cases
...

Write findings to .claude/claudex/20240115-120000-abc123/findings-round-1.md
PROMPTEOF

codex exec --dangerously-bypass-approvals-and-sandbox < "$PROMPT_FILE"
⚠️ Detalhe Crítico: Aspas Simples no Heredoc

A diferença entre <<PROMPTEOF e <<'PROMPTEOF' é enorme. Sem aspas, o shell expande $variáveis, backticks e \n dentro do prompt. Com aspas simples, o conteúdo é literal.

O prompt do Codex pode conter coisas como $PATH, ${variable} ou expressões com backticks como exemplos de código. Sem aspas simples, o shell tentaria expandir tudo isso ao gerar o arquivo, corrompendo o prompt.

📊 Por Que Não Chamar Codex Diretamente
  • Rastreabilidade: o runner script fica em disco — Claude (e o usuário) pode inspecioná-lo antes de executar
  • Isolamento: o hook não precisa ser um processo pai do Codex — Claude executa o script em seu próprio contexto
  • Depurabilidade: em caso de falha, Claude pode re-executar o runner manualmente ou inspecionar o arquivo de prompt
  • Permissões: Claude já tem permissão para rodar bash; não precisa de permissão especial para subprocessos do hook
4

Prompt 04 — Extração de Findings para Economizar Tokens

Por que Claude não deve ler o transcript completo do Codex

Field testing revelou um problema severo: o Codex gerava transcrições de 30.000+ tokens por rodada. Claude tentando ler esse output completo a cada rodada esgotava a janela de contexto rapidamente. O Prompt 04 resolve isso adicionando um segundo artefato de output: um arquivo de findings compacto com bullets estruturados que Claude lê ao invés do transcript.

💡 O Formato dos Findings
# Round 1 findings

## High
- Falta validação de entrada no endpoint /api/plan (adicionar schema)
- Race condition em concurrent writes do PLAN.md (usar file lock)

## Medium
- Sem tratamento de timeout no Codex runner (adicionar timeout 300s)
- CHANGELOG section não tem formato padronizado (definir template)

## Low
- Comentários desatualizados em state-helpers.sh (atualizar inline)

Se não há findings materiais, o arquivo contém exatamente: No substantive findings. — sinal explícito para o hook.

O Prompt Real (Cole no Claude Code)

04_findings_and_personas.md
Two changes, both additive:

## A. Findings file extraction

Field testing showed Claude was burning context reading 30k+ token
Codex transcripts every round. Have Codex also write a clean small
bullet summary that Claude reads instead.

1. Update the round-N Codex prompt in `stop-hook.sh` to include:

   > CRITICAL OUTPUT REQUIREMENT: After your full analysis, write a
   clean summary of just the findings (severity + one-line description
   + recommendation) to the file `<findings_path>`. Use this exact
   format: `# Round N findings\n\n## High\n- <description>
   (<recommendation>)\n...`. If no material findings, write exactly
   `# Round N findings\n\nNo substantive findings.`

2. The hook constructs the findings path as
   `.claude/claudex/<review_id>/findings-round-<round>.md`.
   Pre-create the per-review subfolder.

3. The BLOCK message tells Claude: "When Codex finishes, read the
   clean findings summary from `<path>`. Skip the full transcript
   unless you need extra context."

4. Add `claudex_findings_severity_counts <file>` to `state-helpers.sh`.
   Returns `high=N medium=N low=N` by counting bullet lines under each
   `## Severity` header. Return `high=0 medium=0 low=0` if missing.

5. On round 2+, prepend a "**Previous round:** high=N medium=N low=N"
   line to the BLOCK so Claude sees the trajectory.

## B. Persona rotation

1. Create `plugins/claudex/scripts/personas.sh`. Two functions:
   - `claudex_persona_for_round <N>` — multi-paragraph stanza.
     Round 1: skeptical senior engineer.
     Round 2: security and data-integrity reviewer.
     Round 3+: ops / SRE reviewer (round 4+ deepens prior angles).
   - `claudex_persona_label_for_round <N>` — one-line label.

2. In `write_runner_script`, source personas.sh and prepend the persona
   stanza to the Codex prompt. Pass the round number through.

3. Update BLOCK headers to: `### Claudex round N of M, <persona-label>`.

## Tests

Update tests to assert persona stanzas differ per round, severity
counts work correctly, and round-2 BLOCK contains "Previous round" line.

Run both test scripts. Send the counts.
Dica Prática

A instrução no BLOCK não proíbe Claude de ler o transcript — "Skip the full transcript unless you need extra context." Claude é instruído a começar pelo arquivo compacto, mas pode ir ao transcript se precisar de detalhes. O sistema confia no julgamento de Claude, mas fornece o caminho mais econômico como padrão.

5

Rotação de Personas por Rodada

Engenheiro, segurança e SRE — cada rodada ataca de um ângulo diferente

Um dos insights mais valiosos do Claudex é que revisões repetidas pelo mesmo "reviewer" ficam redundantes rapidamente. A rotação de personas força cada rodada a atacar o PLAN.md a partir de uma perspectiva fundamentalmente diferente, cobrindo blind spots que um único revisor nunca encontraria.

1

Rodada 1 — Engenheiro Sênior Cético

claudex_persona_label_for_round 1 → "Senior-engineer review"

Caça falhas de design e suposições quebradas. "Essa arquitetura vai escalar para 10x o volume? Esse fluxo tem um happy path mas o que acontece quando o third-party falha no meio?" Foco em design-level, não em implementação.

2

Rodada 2 — Revisor de Segurança e Integridade de Dados

claudex_persona_label_for_round 2 → "Security and data-integrity review"

Foca em auth, validação, race conditions, recuperação de falha parcial, secrets, audit trails, perda de dados. "Esse endpoint está autenticado? O que acontece se o banco falhar no meio de uma transação de duas tabelas?"

3+

Rodada 3+ — Ops e SRE

claudex_persona_label_for_round 3 → "Ops and SRE review"

Rollback safety, observabilidade, rollout gradual, version skew, ergonomia on-call. Rodada 4+ aprofunda os ângulos anteriores em vez de repetir o mesmo checklist.

💡 O Efeito Cumulativo

O header do BLOCK na rodada 2 mostra o resultado da rodada anterior: "Previous round: high=2 medium=4 low=1". Claude e o usuário acompanham a trajetória — se a contagem de high vai de 2 para 0, o plano melhorou. Se ficou estável, o plano pode estar chegando em seu teto.

Personas ficam em scripts/personas.sh como um arquivo sourceável separado. Isso permite testar as personas em isolamento e facilita customização futura sem tocar na lógica do hook.

6

Prompt 05 — A Fase de Sumarização

O loop não pode terminar em silêncio

Sem a fase summarizing, o loop terminaria com um silêncio desconcertante: o turno de Claude simplesmente para e o usuário fica sem saber se o loop terminou, se algo deu errado, ou quantas rodadas rodaram. O Prompt 05 adiciona uma fase de output explícito que garante que o usuário vê um resumo antes do approve final.

Mudança de Ciclo de Vida

ANTES drafting → reviewing → done
DEPOIS drafting → reviewing → summarizing → done

O Prompt Real (Cole no Claude Code)

05_summary_phase.md
Add a new phase between `reviewing` and `done` called `summarizing`.
The job of this phase is to BLOCK once with a final summary so Claude
prints it to the user, then approve on the next fire.

## Hook changes (`stop-hook.sh`)

1. Add a `build_rounds_table <final_round>` helper. Iterates 1..N,
   reads each findings file, calls `claudex_findings_severity_counts`,
   pairs with persona label. Returns lines like:
   - Round 1 (Senior-engineer review): high=2 medium=4 low=1
   - Round 2 (Security and data-integrity review): high=2 medium=2 low=0

2. In the `reviewing` case, when `decision_signal=no-material-findings`:
   - CAS `reviewing → summarizing` (NOT to `done`).
   - Build the summary using `build_rounds_table` and `format_elapsed`.
   - BLOCK with `### Claudex plan loop complete ✓`. Body: rounds run,
     total time, findings table, then "**Print this summary to the user
     so they see the loop landed.** Then end your turn. The Stop hook
     will allow exit cleanly."

3. When round overflow:
   - Set `decision_signal=max-reached`.
   - CAS `reviewing → summarizing`.
   - BLOCK with `### Claudex plan loop stopped at max rounds (round N
     of M)`. Include rounds table and three options: revise manually,
     re-run with `--rounds N` larger, accept as known-incomplete.

4. Add a `summarizing` case:
   - CAS `summarizing → done`.
   - Cleanup runner, prompt file, lockfile.
   - Log elapsed time.
   - Approve.

## `mark-done.sh` change

Change to **only** set `decision_signal=no-material-findings` and
**leave `phase=reviewing`**. The next hook fire will see the signal
and drive the summarizing path.

## Format gotcha

Bash double-quoted heredoc strings interpret backticks as command
substitution. Escape every backtick in the summary BLOCK: `\``.
Test that the JSON output parses cleanly with python3.

Run all tests, send counts.
Dica Prática

A fase summarizing é considerada "ativa" pelo start-loop.sh — você não pode iniciar um novo loop enquanto ela está em andamento. Isso é correto: o loop ainda não terminou do ponto de vista do usuário, mesmo que o trabalho principal tenha sido concluído.

7

Por Que o Loop Não Pode Terminar em Silêncio

Saídas explícitas como princípio de design de sistemas autônomos

Sistemas autônomos que terminam em silêncio são sistemas que os usuários param de confiar. Quando um processo que deveria levar 5-15 minutos simplesmente para sem nenhum output visível, o usuário naturalmente assume que algo deu errado — mesmo que tudo tenha funcionado perfeitamente. A fase de sumarização não é um enfeite: é um contrato de confiança com o usuário.

💡 O Que o Resumo Final Contém
### Claudex plan loop complete ✓

**Rounds completed:** 3 of 3
**Total time:** 4m 32s

**Findings by round:**
- Round 1 (Senior-engineer review): high=2 medium=4 low=1
- Round 2 (Security and data-integrity review): high=1 medium=2 low=0
- Round 3 (Ops and SRE review): high=0 medium=1 low=2

**Print this summary to the user so they see the loop landed.**
Then end your turn. The Stop hook will allow exit cleanly.

Caminho Happy: No-findings

Claude chama mark-done.sh quando não há findings materiais. O hook na próxima invocação CAS para summarizing e bloqueia com o resumo completo.

Caminho de Overflow: Max Rounds

O hook detecta que round + 1 > max_rounds. CAS para summarizing com decision_signal=max-reached. O resumo mostra 3 opções ao usuário.

⚠️ Gotcha: Backticks em Heredocs Double-Quoted

O BLOCK do resumo usa triple-backticks para fencing de código Markdown. Mas o heredoc que gera o JSON do BLOCK usa aspas duplas, então o bash interpreta backticks como command substitution. Cada backtick no conteúdo do BLOCK precisa ser escapado: \`\`\` em vez de ```.

📊 Princípios de Sistemas Autônomos com Output Explícito
  • Progresso visível: o usuário deve saber que o sistema está trabalhando, não apenas que está rodando
  • Completion explícita: um sistema que terminou deve dizer que terminou, não apenas parar de responder
  • Outcomes mensuráveis: o resumo mostra high/medium/low por rodada — o usuário pode avaliar o valor entregue
  • Próximos passos: quando o loop para no max, o resumo já fornece as 3 opções de ação — o usuário não precisa adivinhar

Resumo do Módulo 2.2

O que você aprendeu

  • Como o Prompt 03 transforma o stub em lifecycle engine real com BLOCK/APPROVE por fase
  • A tabela completa de fases e o que o hook faz em cada uma
  • Como o runner script funciona e por que heredoc com aspas simples é crítico
  • O Prompt 04: findings compactos para economizar contexto e rotação de personas
  • As 3 personas (engenheiro, segurança, SRE) e como elas cobrem ângulos diferentes
  • O Prompt 05: fase de sumarização como contrato de confiança com o usuário
  • Por que sistemas autônomos precisam de saídas explícitas, não silenciosas

Próximos passos

No Módulo 2.3, você vai ver os Prompts 06 e 07 — os comandos utilitários (/claudex:status, /claudex:doctor, /claudex:cancel, /claudex:rollback) e o safety pass final que torna cada error path fail-open.

Nesse ponto, os 5 prompts que você viu (01–05) já constroem um Claudex completamente funcional. Os prompts 06 e 07 adicionam ergonomia e robustez de produção.