teqo/docs/plans/ci-deploy-cooldown-vercel.md
Cursor Agent 746030d253
docs: registra OPS11 + B95–B99 (#102–#107) pós-gate
Matriz B98 confirmada (cadeia completa nos 4 entries). Cabeçalhos
dos planos com Issue # e status registrado.
2026-08-01 10:48:04 +00:00

7.2 KiB
Raw Blame History

CI — cooldown mínimo de 30 min entre deploys de produção (Vercel)

Status: registrado Atualizado em: 2026-08-01 Issue: #102 Priority: P0 Model: composer-2.5 Impeccable: A — N/A (sem superfície UI) Appetite: ~0,5–1d eng; 1 job step em ci.yml + retry/requeue; sem migration Responsável: —

Dados → decisão → apresentação

Dados: N/A — gate de infra/CI; sem KPI de produto.

Contexto

O verificador ci.yml (push em main) sobe produção com vercel deploy --prebuilt --prod. No plano Hobby/Free da Vercel o upload da CLI estoura o teto diário (api-upload-free, “Too many requests — try again in 24 hours / more than 5000”). Em 2026-08-01 vários merges em sequência (incl. B92–B94) passaram nos checks e falharam no job deploy — o código ficou em main sem ir a produção.

Já existe um “stale HEAD” guard (só o tip de main faz deploy) + requeue se main avançou. Não há intervalo mínimo entre deploys bem-sucedidos: cada merge verde dispara upload completo.

Pedido de produto (2026-08-01): mínimo 30 minutos entre um deploy e o próximo; antes de continuar, o job verifica a idade do último deploy.

Consequência imediata (não é bug de UI): o empty/suggest do wizard de município (B92–B94, Issues #92–#94, merged) não está em produção porque o deploy falhou por rate limit — não porque o idle esteja “quebrado” no código de main.

Objetivos

  • Antes de vercel pull/build/deploy --prod, consultar o último deployment de produção do projeto e, se a idade for < 30 min, não fazer upload agora.
  • Em skip por cooldown: sair com sucesso “adiado” (não vermelho falso) e reagendar o workflow (mesmo espírito do requeue atual) para rodar depois do restante do intervalo — assim o tip de main ainda chega a produção sem depender de humano.
  • Opcional barato no mesmo PR: passar --archive=tgz no vercel deploy (a própria CLI sugere) para reduzir pressão de upload — não substitui o cooldown.
  • Guardrails: sem migration; secrets VERCEL_* já usados; não tocar ci-pr.yml (sem deploy); Vercel preview Git continua ignorado (vercel-ignore-build.sh).

Decisões travadas

  • Fonte da idade = último production deployment do projeto Vercel (API/vercel ls/vercel inspect), não o last successful ci.yml run. Motivo: o teto é da conta Vercel; um deploy manual/CLI fora do Actions também conta. Rejeitado: só olhar gh run list --workflow=ci.yml (ignora deploys fora do GHA); sleep fixo de 30 min em todo run (desperdício quando já passou).
  • Cooldown = 30 min (constante nomeada no workflow/script). Fonte: pedido 2026-08-01. Rejeitado: 1 h (atrasa demais o tip); só backoff após 429 (já estouramos o dia — tarde demais).
  • Skip por cooldown ≠ failure. Job deploy marca “adiado”, requeue/dispatch agenda novo ci.yml com delay (ou workflow auxiliar workflow_dispatch + wait). Rejeitado: exit 1 (alarme falso; agentes/CI “dono do PR” interpretam vermelho); skip silencioso sem requeue (main fica sem deploy até o próximo merge).
  • Stale HEAD continua primeiro. Ordem: tip check → cooldown check → credentials → pull/build/deploy. Rejeitado: cooldown antes do tip (adiaria deploy que já seria skipado).
  • i18n: ids em inglês (DEPLOY_COOLDOWN_MS, lastProductionDeployAt); logs pt-BR ou EN ok no Actions.

Questões em aberto

  • Como obter o timestamp do último prod deploy? Opções: A) vercel ls <project> --prod / API REST deployments filtrando target=production + ready | B) gravar timestamp em Actions cache/artifact após deploy OK | C) GitHub Deployment API. Recomendação: A — fonte de verdade Vercel; B desincroniza se houver deploy fora do CI. (assumido)
  • Delay do requeue: gh workflow run imediato + step sleep residual, ou cron/repository_dispatch? Opções: A sleep no mesmo job até completar 30 min (bloqueia runner) | B dispatch com sleep curto só do residual num job leve | C Actions schedule a cada 15 min checando tip≠prod. Recomendação: B — job leve espera o residual (cap ~30 min) e re-dispara ci.yml; A gasta minutos caros no job de build. (assumido — validar custo de runner)

Abordagem proposta

flowchart TD
  Tip["Guard stale HEAD"] -->|tip| Age["Idade último prod deploy"]
  Age -->|"< 30 min"| Wait["Skip + schedule requeue"]
  Age -->|">= 30 min"| Creds["Require VERCEL_*"]
  Creds --> Build["pull + build + deploy --prod --archive=tgz"]
  Build --> Ok["Record / log deploy ok"]
  Wait --> Later["ci.yml again after residual"]

Componentes:

  • .github/workflows/ci.yml (job deploy): step Check deploy cooldown após tip; output cooldown=true|false + wait_seconds; condicionar steps de build/deploy; estender requeue (ou step irmão) para “main tip sem deploy recente / cooldown”.
  • Script pequeno (scripts/vercel-deploy-cooldown.mjs ou shell no step): chama API Vercel com VERCEL_TOKEN + VERCEL_PROJECT_ID; devolve idade; constante DEPLOY_COOLDOWN_MS = 30 * 60 * 1000.
  • vercel deploy --prebuilt --prod --archive=tgz: redução de arquivos no upload (hint da CLI no erro 2026-08-01).
  • Verificação pós-entrega: após um deploy OK, smoke do idle do wizard (mode: wizard-municipality-suggest) em produção — confirma que B92–B94 saíram do limbo.
  • Migration: Sem migration, sem collection, sem server action de app.

Dependências

  • Nenhuma Issue de produto. Soft: entendimento do requeue atual em ci.yml; secrets Vercel já no repo.
  • Desbloqueia visualização em prod de tudo que está em main sem deploy (incl. B92–B94).

Não escopo

  • Upgrade de plano Vercel / mudança de limites da conta.
  • Deploy de preview por branch (já skipado).
  • Mudar gate de merge (checks / migration-lock).
  • Refatorar o orquestrador do agent-pool para “batch merges” (adiado).
  • Bugfix de UI do empty state — causa raiz = undeployed; se após deploy OK o idle ainda falhar, abrir Issue B nova com evidência.

Rabbit holes

  • “Já que toco deploy, migro para OIDC / Vercel Git / multi-env.” Explode appetite. Mitigação: só cooldown + archive + requeue.
  • State machine de fila de deploys no Postgres. Mitigação: workflow + API Vercel bastam.
  • Deduplicar builds com cache Vercel remoto exótico. Mitigação: fora; --prebuilt já isola build→upload.

Adiado com gatilho

  • Batch de merges → um deploy. Revisitar se, com cooldown de 30 min, o tip ainda atrasar >1 h sob carga do pool e produto aceitar “deploy só a cada N merges”.
  • --archive=tgz sozinho sem cooldown. Se o teto 5000/dia continuar após archive, o cooldown permanece necessário.

Referências

  • .github/workflows/ci.yml — jobs deploy / requeue, tip guard
  • Erro observado 2026-08-01: Too many requests … code: "api-upload-free" no merge de B94 (#100)
  • Issues B92–B94 (#92–#94) — código em main, deploy falhou
  • scripts/vercel-ignore-build.sh — previews Git off
  • docs/AGENT-OPS.md — entrega = merge em main; deploy gated por ci.yml
  • AGENTS.md — região gru1; deploy via Actions

Qualidade de decisão: 5/5