teqo-backup/docs/plans/worktree-seed-minimal-paridade-e2e-ci-impl.md
Francisco Solla 0047d2637c ops28(OPS28): worktrees rodam o seed mínimo no provisionamento — e2e local espelha a CI
- scripts/worktree.mjs: provision() aplica migrate + db:seed:minimal nos
  bancos dev e test (paridade com a CI, que roda o par antes do e2e);
  --no-migrate pula os dois (o seed depende do catálogo migrado); fallback
  sem Docker também migra+seeda o teqo/teqo_test compartilhado (com warning
  de sobrescrita dos pins).
- Guard de regressão tests/e2e/seedParity.setup.e2e.spec.ts no projeto setup:
  falha cedo com mensagem clara se o banco de teste não estiver seedado;
  evidência = users do seed (MINIMAL_CAMPAIGN_USERS), não os pins de priority
  — o cleanup dos fixtures e2e reseta priority por round-robin persistente.
- scripts/lib/test-affected-core.mjs: scripts/worktree.mjs em HIGH_RISK_EXACT.
- Docs: AGENTS.md, skill local-database, CHANGELOG-AGENTS.md, impl plan.

Closes #584
2026-08-10 16:11:27 -03:00

8.4 KiB
Raw Permalink Blame History

Impl: Worktrees provisionam os bancos e2e sem o seed mínimo — e2e diverge da CI

Status: aprovado Atualizado em: 2026-08-10 Issue: #584 Intenção: docs/plans/worktree-seed-minimal-paridade-e2e-ci.md Appetite restante: herdado (~0,5 dia eng; um outcome verificável)

Leitura da intenção

  • Outcome: um worktree recém-provisionado (next e plan) roda pnpm test:e2e com o mesmo resultado da CI nos specs dependentes de dados do seed (campaignMunicipalities FAB suggest, campaignSavedFilters e os que tocam priority/expectedVotes/campaignGoals); a CI continua sendo a fonte canônica (nada do que este item faz muda o que a CI executa); o seed roda idempotente e protegido pelo guard de banco local; re-provisionamento não duplica nem corrompe.
  • O que NÃO negociar: não alterar specs e2e para "aceitar dados ausentes" (o provisão é o defeito); nunca rodar seed contra banco não-local; CI intocada; guard de regressão sem enfraquecer specs.
  • O que reavaliar: a hipótese de "Direção no codebase" aponta scripts/worktree.mjs + seed-minimal.mjs + manifest + setup e2e — confirmada na exploração; a mecânica real das falhas foi verificada no código (FAB suggest filtra priority === 'alta' em rankHomeSearchSuggestMunicipalities; o recorte ?priority=alta nasce vazio sem os pins; o nome sugerido vem de formatMunicipalityActiveFiltersSummary(state) — o fluxo quebra porque o recorte vazio derruba a linha 49 do spec).

Abordagem recomendada

flowchart LR
  A[provision worktree] --> B[runMigrate dev]
  B --> C[runSeedMinimal dev]
  C --> D[runMigrate test]
  D --> E[runSeedMinimal test]
  E --> F[env files + porta do slot]
  F --> G[e2e igual à CI]

Opções consideradas: A | B | C Recomendação: A — rodar pnpm db:seed:minimal dentro de provision() em scripts/worktree.mjs, logo após cada runMigrate, para os DOIS bancos (dev e test), reusando o mesmo padrão de env (DATABASE_URL + PAYLOAD_SECRET explícitos) e o mesmo script npm da CI (pnpm db:seed:minimal — idempotente por upsert, guard assertLocalDatabase já embutido). --no-migrate pula migrations e o seed (o seed depende do catálogo migrado). Re-provisionamento (next num worktree existente) roda tudo de novo — migrate e seed são idempotentes, o aceite "não duplica nem corrompe" vale por construção. Rejeitadas:

  • B — seedar só o banco de teste (test-only): corrige o e2e mas deixa o pnpm dev do worktree divergente (FAB suggest vazio no navegador manual) e cria dois contratos de provisionamento; o custo de seedar o dev é o mesmo.
  • C — documentar no AGENTS.md "rode o seed manualmente no worktree": trata o sintoma no papel; o ambiente padrão de entrega continua divergente e o agente segue aprendendo a ignorar o e2e vermelho (o padrão exato do ledger TECH-DEBT row 19).

Componentes / mudanças

  • runSeedMinimal (scripts/worktree.mjs): irmã de runMigrate — execFileSync('pnpm', ['db:seed:minimal'], { cwd: dir, env: { ...process.env, DATABASE_URL, PAYLOAD_SECRET }, stdio: 'inherit' }). O script npm já carrega tsx/esm + seed-loader; o seed faz loadCliEnv() sem override, então os env explícitos vencem.
  • provision() (scripts/worktree.mjs): migrate dev → seed dev → migrate test → seed test; skipMigrate passa a pular os dois (mensagem atualizada).
  • Fallback sem Docker (writeFallbackEnv path): decisão Q1 aprovada no gate — paridade total (opção A): migrate + seed:minimal também no teqo/teqo_test compartilhados (ler PAYLOAD_SECRET do main env nesse branch; --no-migrate pula igualmente).
  • Guard de regressão (Q2 da intenção, decisão aprovada no gate — opção B, setup e2e): spec novo tests/e2e/seedParity.setup.e2e.spec.ts no projeto setup do Playwright (o match /setup\.e2e\.spec\.ts/ pega o arquivo; projeto setup roda primeiro e os projetos dependentes pulam se ele falhar): boota getPayload({ config }) (padrão já usado nos fixtures) e falha com mensagem clara ("rode pnpm migrate && pnpm db:seed:minimal — ou pnpm worktree next para reprovisionar") se o banco de teste não estiver seedado. Evidência checada = MINIMAL_CAMPAIGN_USERS (users sintéticos do seed), deliberadamente NÃO os pins de priority: 'alta': o cleanup dos fixtures e2e reseta priority dos municípios tocados (round-robin por sequência persistente — campaignE2EFixtures.ts), então pins morrem legitimamente após runs repetidas no mesmo banco de worktree e não são evidência indelével; os users do seed nunca são deletados pelo cleanup (que só deleta o que os fixtures criaram). Mensagem clara em vez de spec vermelho confuso. (Sem extensão extra do manifest unit — o pin existente de slugs fica como está.)
  • HIGH_RISK_EXACT (scripts/lib/test-affected-core.mjs): adicionar scripts/worktree.mjs — mudança no provisionador deve rodar a suíte cheia na CI, não só o diff.
  • Docs: AGENTS.md "Per-worktree environments" (migrations → migrations + seed mínimo, paridade com a CI); header/uso do scripts/worktree.mjs (--no-migrate agora pula migrate+seed); .agents/skills/local-database/SKILL.md (linhas 32/34, mesmo contrato); entrada curta em docs/CHANGELOG-AGENTS.md.
  • Migration: nenhuma (sem schema/dados novos).
  • Access / Consent: nenhum (o seed já faz provisionOnda0ConsentAndPrivacy + chaves fail-closed — intocado).
  • UI: Impeccable A — N/A (sem UI).

Dados → forma (se aplicável)

Não aplicável — infra de provisionamento; o "dado" é a paridade de estados entre ambientes, verificada pelo guard do setup (pin alta no banco de teste) e pelo e2e real.

Fases verificáveis

  1. Tracer — provisionamento (quota principal): runSeedMinimal + chamadas no provision() (Docker path) + --no-migrate → verificação manual num banco de teste recém-criado (seed aplicado, idempotência em re-execução). Fallback sem Docker segue a decisão do gate (Q1).
  2. Guard — seedParity.setup.e2e.spec.ts no projeto setup (falha com mensagem clara contra banco não-seedado — incluindo banco nunca-migrado, via try/catch com a mesma mensagem; verde com seed) + HIGH_RISK_EXACT.
  3. Gates — pnpm gate:fast na iteração; depois pnpm test:e2e -- tests/e2e/campaignSavedFilters.e2e.spec.ts + o spec do FAB suggest contra o banco do worktree seedado (reprodução real das duas falhas da intenção); pnpm test:int (unit+int); pnpm check:cycles/format:check/knip; entrega com pnpm push.

Rabbit holes / Não escopo (engenharia)

  • Não tocar em nenhum spec e2e existente (o único arquivo de teste novo é o guard).
  • Não mudar o CI (ci.yml/ci-pr.yml já rodam migrate → seed:minimal — nada a fazer).
  • Não alterar o seed-minimal.mjs nem o manifest (já idempotentes e corretos).
  • Não mexer nos bancos compartilhados teqo/teqo_test no fluxo Docker (fora do contrato de worktree).
  • Não adicionar lock/transação ao seed para o caso de dois fallbacks seedarem o mesmo teqo_test em paralelo (fallback é degradado; colisão de upsert find-before-create é raça rara e ruidosa — documentar no impl, não engenhar).
  • Gap observado (não deste item): o fallback não grava PAYLOAD_SECRET no .env.local/.env.test.local gerados — o seed receberá o secret explícito no env; o gap do dev no fallback é pré-existente e fica fora de escopo.

Riscos e mitigação

  • Seed adiciona ~10–30 s por banco no provisionamento (boot do Payload + upserts): aceitável — migrations já levam ~30 s cada; é one-time por banco e idempotente.
  • Re-execução em worktree existente: next re-roda provision(); migrate+seed idempotentes → sem duplicação (upsert por chave estável/slug/email/phone).
  • Guard do setup com banco compartilhado do fallback: se dois fallbacks rodarem e2e no mesmo teqo_test, o guard lê estado do banco — o seed é idempotente; risco residual documentado (raça find-before-create) e ruidoso se ocorrer.
  • E2E novo no projeto setup pode afetar a duração do setup: boot do payload ~2–5 s; aceitável (o setup já prewarms 13 rotas).

Aceite de engenharia

  • Aceite de produto da intenção ainda coberto (e2e do worktree = CI; CI intacta; idempotência)
  • Invariantes AGENTS/engineering-standards (local-only via assertLocalDatabase; nenhum schema/access/Consent tocado)
  • Testes previstos: guard e2e (falha clara + verde), unit do manifest estendido, reprodução real dos 2 specs que falhavam