O que o Prompt 02 Entrega
Estado YAML persistente com primitivas de acesso seguro
O Prompt 02 constrói a base de estado do revisor: o arquivo YAML que persiste entre turnos do Claude Code, os helpers para ler/escrever com segurança e o mecanismo de lockfile. Sem isso, o hook do Prompt 03 não tem onde guardar informação.
state/review.yaml com schema completo e campos tipadosstate/state-helpers.sh com read_state, write_state_atomic, cas_stateflock -n state/review.lockO Prompt 02 Completo
Cole este texto no Claude Code com o SPEC.md em contexto
Prompt 02 — Cole no Claude Code
Read SPEC.md — use the schema defined there as source of truth.
You are building Prompt 02: the state persistence layer.
Do NOT modify any hook logic yet.
**Deliver in this prompt:**
1. `state/review.yaml` — initial template (empty review):
pr_number: null
worktree_path: null
status: null # SETUP|REVIEWING|SUMMARIZING|DONE
round: 0
max_rounds: 3
persona: null # Author|Reviewer|Security
findings: []
started_at: null
version: 0 # CAS version — increment on every write
2. `state/state-helpers.sh` — source this in hooks and scripts:
a) `read_state_field <field>` — reads a field from review.yaml using
`grep "^field:" state/review.yaml | awk '{print $2}'`.
Returns empty string if field missing or file absent.
b) `write_state_atomic <yaml_content>` — writes content to
state/review.yaml.tmp then renames to state/review.yaml.
Creates state/ dir if absent.
c) `cas_state <expected_version> <new_yaml_content>` — reads current
version field. If it matches expected_version, calls
write_state_atomic. If it doesn't match: prints CAS conflict to
stderr and returns exit 1. Caller must handle.
d) `acquire_lock` — runs `flock -n state/review.lock -c 'echo locked'`
to test non-blocking. If another process holds the lock: prints
error to stderr, returns exit 1.
e) `release_lock` — removes state/review.lock. Idempotent.
3. Unit tests at `tests/test-state-helpers.sh`:
- Write state, read it back — fields must match
- CAS success: correct version → write succeeds
- CAS conflict: wrong version → write fails, exit 1
- Lock test: acquire → second acquire fails → release → acquire succeeds
**Constraints:**
- state-helpers.sh must be sourced, not executed directly
- write_state_atomic must use tmp + rename (never write directly)
- cas_state must never block — if version mismatch, fail immediately
- All helpers fail-open: errors log to stderr, never crash the caller
**Verification:**
- `bash tests/test-state-helpers.sh` runs all 4 tests and reports PASS/FAIL
- `source state/state-helpers.sh; write_state_atomic "pr_number: 42
version: 0"; read_state_field pr_number` prints "42"
- CAS test: set version to 0, call cas_state 99 ... — must fail
Tell me the test results (all 4 should PASS).
O Schema do Estado
PR number, worktree path, round, status, findings — cada campo com propósito
O schema não é apenas estrutura — é uma decisão de design. Cada campo responde uma pergunta específica que o hook vai precisar responder durante o loop.
YAML é legível por humanos sem ferramenta especial. Você pode abrir state/review.yaml num editor e entender exatamente o estado atual. JSON seria mais estrito, mas menos debugável quando algo dá errado às 23h com um PR urgente.
Escrita Atômica e CAS Aplicados ao Revisor
Estado que nunca corrompe, mesmo com interrupção
Claude Code pode ser interrompido a qualquer momento — Ctrl+C, timeout, crash. Sem primitivas de segurança, a interrupção durante uma escrita deixa o review.yaml corrompido e ilegível.
# ERRADO: escrita direta — se interrompida, o arquivo fica corrompido
echo "$new_content" > state/review.yaml
# CORRETO: escrever em tmp e renomear atomicamente
echo "$new_content" > state/review.yaml.tmp
mv state/review.yaml.tmp state/review.yaml
# mv/rename(2) é atômico no mesmo filesystem — não pode ser interrompido
# CAS: só escreve se a versão que você leu ainda é a atual
cas_state() {
local expected_version=$1
local new_content=$2
local current_version=$(read_state_field version)
if [ "$current_version" != "$expected_version" ]; then
echo "[pr-reviewer] CAS conflict: expected v$expected_version, got v$current_version" >&2
return 1 # falha — o chamador deve relêr o estado e tentar novamente
fi
# Versão confere — pode escrever (atomicamente)
write_state_atomic "$new_content"
}
Lockfile: Exclusão Mútua
Garantindo que apenas um revisor roda por vez no mesmo repositório
Se você abrir duas janelas do Claude Code no mesmo repositório, dois revisores podem ser disparados simultaneamente. Eles vão corromper o state/review.yaml um do outro. O lockfile previne isso.
# -n = non-blocking: falha imediatamente se o lock não está disponível
# -e = cria o lockfile se não existe
exec 9>state/review.lock
if ! flock -n 9; then
echo "[pr-reviewer] Another reviewer is already running. Aborting." >&2
exit 0 # fail-open: não bloqueia o usuário
fi
# Lock adquirido — o processo retém o lock até fechar fd 9 (EXIT trap)
trap 'flock -u 9; rm -f state/review.lock' EXIT
Stale Lock (lock fantasma)
Se o processo morreu sem liberar o lock, o arquivo state/review.lock existe mas não há processo segurando. O flock detecta isso automaticamente — um arquivo de lock sem processo = lock disponível.
Lock vs CAS
O lockfile garante exclusão mútua entre processos. O CAS garante consistência dentro de um processo contra interrupções. Ambos são necessários — são complementares, não alternativos.
Verificação: Testar CAS e Atomicidade em Isolamento
Antes de avançar para o Prompt 03
CAS e atomicidade são difíceis de debugar quando estão integrados com o hook completo. Testar agora, com o sistema simples, é a estratégia correta.
Teste de read/write
Escrever estado com write_state_atomic, ler com read_state_field, confirmar que todos os campos batem.
Teste de CAS bem-sucedido
Estado com version=0, chamar cas_state 0 com novo conteúdo — deve escrever. Verificar version=1 após.
Teste de CAS com conflito
Estado com version=0, chamar cas_state 5 — deve falhar com exit 1. Estado original deve permanecer inalterado.
Teste de lockfile
Adquirir lock em subshell, tentar adquirir em outra subshell — deve falhar. Liberar primeiro lock, segunda tentativa deve passar.
O arquivo tests/test-state-helpers.sh deve reportar 4/4 PASS. Se qualquer teste falha, o Prompt 03 vai construir sobre uma base quebrada.
✅ Resumo do Módulo 4.3
Próximo Módulo:
4.4 — Prompt 03: O Stop Hook e o Loop — o engine real da máquina de estados