teqo/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

76 lines
8.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
```mermaid
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