Pular para conteúdo

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.

  1. 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.
  2. 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