0013 — O make seed some do log do deploy, e o deploy diz que deu certo¶
| Status | CORRIGIDO |
| Branch | fix/0013-seed-some-do-log-do-deploy |
| Aberto em | 2026-08-23 |
| Severidade | ALTA |
| Apps afetados | nenhum — infra/prd, infra/dev-remote, .claude/skills/ship e, por herança, .claude/skills/tweak |
| Revisão do diagnóstico | aprovado na rodada 2 |
| Revisão da correção | aprovado na rodada 1 |
Status:
RASCUNHO→DIAGNOSTICADO→EM CORREÇÃO→CORRIGIDO
O fix atravessa dois portões, cada um revisado por um subagente de contexto limpo, em até 3 rodadas. Máquina do plugin superpowers, régua em .claude/rubrics/. Qualquer Critical reprova; Important em aberto reprova salvo decisão registrada do usuário.
- O diagnóstico (seções 1 a 4) é revisado na seção 5 (reprodução × causa raiz). É o portão barato: sem ele, o segundo mede o conserto contra um palpite.
- A correção (o diff e os testes) é revisada na seção 11 (correção × regressão), com duas perguntas — atacou a causa ou o sintoma, e o que prova que não volta.
1. Sintoma¶
O /ship termina dizendo "Deploy na box concluído", com exit 0, e o make seed nunca rodou. Três linhas somem do log, sempre as mesmas e sempre juntas:
>> [box] make seed (RBAC + admin inicial, idempotente)…
<a saída do seed>
>> [box] HEAD agora: <sha>
A saída emenda direto do make migrate no >> Deploy na box concluído., que é a última linha do script local — a que roda depois do ssh voltar.
Aconteceu nos três deploys que existem desde que o passo virou fixo: spec 0041 (2026-08-22), spec 0043 e spec 0044 (2026-08-23). Nas três vezes o seed foi rodado à mão em seguida e respondeu normalmente.
O que torna isto grave não é o log, é o que o log esconde. Permissão nova é dado de seed, não de schema: ela entra sem migration nenhuma. Um deploy que pula o seed sobe o código novo com o banco sem a permissão — o papel que a feature habilita toma 403, e a tela, que esconde o que vai ser negado, some com o botão. Nada quebra; a funcionalidade simplesmente não existe para quem deveria tê-la. É o caso da spec 0038, que foi o motivo de o passo virar fixo no /ship.
As três specs que passaram por isto não acrescentaram permissão, então nada ficou faltando. Foi sorte de escopo, não proteção.
2. Reprodução¶
A mais limpa não precisa de docker nem de produção — e isola o mecanismo sozinha. Um comando qualquer que leia stdin, dentro de um script alimentado por stdin:
cat <<'EOF' | bash -s
echo ">> passo 1"
cat > /dev/null
echo ">> passo 2 — o seed"
echo ">> passo 3 — o HEAD"
EOF
echo "exit=$?"
Saída: >> passo 1, e mais nada. exit=0. É a forma exata do sintoma em produção: o passo some, o seguinte some, e o script diz que deu certo.
Que o make não protege também se demonstra sem docker. Uma receita que roda cat imprime o resto do script — ou seja, ela recebe o script como entrada e o consome:
printf 'le:\n\t@cat\n' > /tmp/Makefile.teste
cat <<'EOF' | bash -s
make -f /tmp/Makefile.teste le
echo ">> SOBROU"
EOF
Saída: echo ">> SOBROU" — impresso pelo cat, e portanto não executado.
Com docker compose exec, e aqui a medição é menos redonda¶
Chamando o exec direto no script, mede-se limpo — 5 execuções por linha:
| Variante | Perde a linha seguinte? |
|---|---|
exec api echo oi | 5 de 5 |
exec api echo oi >/dev/null | 5 de 5 |
exec api echo oi >/dev/null 2>&1 | 5 de 5 |
| `exec api echo oi |