mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-14 08:05:17 +00:00
plans(OPS86/OPS87/OPS88): simplificar a etapa de verificação CI/deploy — blast radius, pirâmide HTTP, setup único (Related #832, #833, #834)
This commit is contained in:
parent
10e0c17078
commit
6915e2a944
3 changed files with 215 additions and 0 deletions
70
docs/plans/ops86-blast-radius-testes-prs.md
Normal file
70
docs/plans/ops86-blast-radius-testes-prs.md
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
# OPS86 — Blast radius dos testes: PRs quebram testes que não rodaram no CI do PR
|
||||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-08-24
|
||||
Issue: #832
|
||||
Priority: P1
|
||||
Impeccable: A — N/A sem UI
|
||||
Rascunho UI: N/A — sem UI
|
||||
Appetite: ~1 dia eng; um outcome verificável
|
||||
Responsável: —
|
||||
|
||||
## Intenção
|
||||
|
||||
Os PRs estão quebrando testes que o CI do PR não executou — e os PRs que mais quebram são exatamente os que hoje rodam menos. Um diff que mexe em coleção, global, migração ou regra de acesso muda o RBAC e a superfície inteira do produto, mas a política atual (OPS72) manda esse tipo de diff para o modo "verificação máxima" — que, por custo, significa **zero e2e no PR**. O teste de verdade só acontece depois do merge, no deploy. Além disso, o CI tem um mapa de qual código cobre qual teste e2e, mas pedaços críticos (regras de acesso, schemas de formulário, push, transcrição de áudio) estão fora desse mapa — o CI nem sabe que o diff tocou área sensível e não roda nada. E no nível unit/int, o modo "rodar só o que mudou" pode selecionar zero testes e mesmo assim ficar verde. O outcome desejado: **nenhum PR de código passa no CI sem executar exatamente os testes que o diff pode quebrar** — e2e e unit/int —, com falha visível (e lista de specs) quando a cobertura não existe.
|
||||
|
||||
## Persona e fluxo
|
||||
|
||||
- **Persona / contexto:** quem abre PR no repositório (humano ou agente) e usa o CI como resposta à pergunta "esse diff está seguro para mergear?"; e o verify do deploy, que hoje é a única rede de segurança para os PRs de maior risco.
|
||||
- **Job principal:** saber, antes do merge, se o diff quebrou algo — sem rodar a suíte inteira a cada PR.
|
||||
- **Fluxo desejado:** abre-se um PR com diff de alto risco (coleções, migrações, RBAC, schemas) → o CI roda um conjunto **curado e representativo** de testes e2e (acesso/RBAC + smoke da superfície afetada) mais os unit/int que o diff pode tocar → verde ou vermelho com a lista exata de specs. Se o diff tocar código sem mapeamento declarado, o CI não fica verde em silêncio: cai num fallback explícito (roda a superfície, sinaliza a lacuna) ou falha pedindo mapeamento. Se a seleção de unit/int por dependência vier vazia, o CI roda o conjunto de fallback — nunca "0 testes, tudo verde".
|
||||
- **Anti-goals de produto:** não voltar a rodar e2e full por PR (o custo volta a travar o fluxo); não piorar o custo médio do job; não quebrar o invólucro "dono do PR, dono do CI" — a rede de segurança do verify no deploy permanece como está; não transformar o mapeamento em um catálogo de tudo (manutenção infinita sem ganho).
|
||||
|
||||
## Objetivo e aceite
|
||||
|
||||
- PR com diff de alto risco (coleções/globals/migrações/acesso) executa no CI um conjunto curado de e2e representativo — nunca zero — e o resultado aparece no check do PR.
|
||||
- Nenhum módulo de risco (regras de acesso, schemas, autenticação/push, IA) fica fora do mapa: diff nesses arquivos nunca cai em "nenhum e2e" sem explicação.
|
||||
- O modo "só o que mudou" dos testes unit/int nunca roda 0 testes em silêncio: seleção vazia dispara fallback explícito, e CI não fica verde com zero execuções.
|
||||
- Código tocado sem cobertura declarada não passa com aviso de log: ou roda o fallback, ou falha apontando a lacuna (fail-closed para área de risco).
|
||||
- Guardrails: PR nunca roda e2e full (a suíte completa segue só no verify do deploy); custo médio do job não sobe em relação ao hoje; classificador e mapa só mudam com o teste de invariante existente verde.
|
||||
|
||||
## Dados (intenção)
|
||||
|
||||
- **Vou apresentar dados?** Não — este item é sobre verificação, não sobre métricas; o sucesso é observável no comportamento do CI, não num painel.
|
||||
- **Decisões desbloqueadas:** nenhuma de negócio. (Se a Direção quiser acompanhar lacunas de cobertura ao longo do tempo, isso é outro item — com decisão de consumo nomeada.)
|
||||
|
||||
## Direção no codebase (hipótese)
|
||||
|
||||
- **Áreas prováveis:** o classificador de blast radius (`scripts/lib/test-affected-core.mjs` — high-risk→`full`), a porta de e2e no workflow do PR (`.github/workflows/ci-pr.yml`, gated em `selected`), o mapa de e2e (`scripts/e2e-affected.*` + manifest), a classificação de escopo (`scripts/ci-scope.mjs`, hoje só loga lacunas), e o gate de testes (`scripts/gate-ci.mjs`, `--changed` + `--passWithNoTests`).
|
||||
- **Precedente a olhar:** OPS72 (política full→zero e2e no PR), OPS83 (o que já foi feito em seleção de testes), e o teste de invariante do classificador (`ciSkipInvariants.unit.spec.ts`) como o pin de que mudanças no próprio mecanismo são verificadas.
|
||||
- **Risco de acoplamento:** o executor deve mexer no mecanismo **sem** mexer no contrato de deploy (verify full no homeserver) e sem relaxar os guards de banco local. Módulos fora do mapa hoje incluem, como hipótese: `src/utilities/campaignAccess.ts` + `src/utilities/access/*`, `src/lib/schemas/*`, `src/utilities/campaignPushClient.ts`, `src/utilities/ai/*`, `src/lib/formData.ts`, `src/components/ui/*`.
|
||||
|
||||
## Dependências
|
||||
|
||||
- Nenhuma dura. OPS72 e OPS83 já entregues (base onde o item se apoia, mas nada em voo).
|
||||
|
||||
## Fora de escopo
|
||||
|
||||
- Migração de testes unit para int/HTTP (OPS87 — separado).
|
||||
- Setup compartilhado de banco no CI (OPS88 — separado).
|
||||
- Reduzir tempo de build/e2e (apenas manter o custo médio atual).
|
||||
- Qualquer mudança no verify do deploy ou no fluxo pós-merge.
|
||||
|
||||
## Rabbit holes de produto
|
||||
|
||||
- **"Mapear tudo".** Se alguém "só completar", o mapa vira um catálogo de 400 linhas com manutenção infinita. **Corte neste item:** mapear só o que o classificador já trata como risco e hoje cai em zero/não-mapeado; o resto usa o fallback explícito.
|
||||
- **"Fallback = rodar tudo".** Vira e2e full de fato, matando o custo. **Corte neste item:** fallback é conjunto curado + unit/int full quando necessário — unit/int full é barato, e2e full nunca.
|
||||
- **"Só melhorar o log".** Continua verde com 0 testes rodados. **Corte neste item:** toda lacuna de cobertura em área de risco termina em fallback executado ou falha; aviso solto não conta.
|
||||
|
||||
## Questões em aberto (produto)
|
||||
|
||||
- **Seleção vazia no modo "só o que mudou": o fallback deve rodar unit/int full ou apenas a superfície?** **Opções:** A) unit/int full sempre que a seleção vier vazia; B) conjunto da superfície do diff; C) falhar pedindo mapeamento. **Recomendação:** A — unit/int full é barato e cobre com honestidade máxima; o conjunto curado fica para o e2e. _(assumido — validar com produto)_
|
||||
- **O conjunto curado de e2e para diff de alto risco: fixo e estável ou derivado por heurística de nomes?** **Opções:** A) curadoria explícita e estável (acesso/RBAC + smoke por superfície), revisável como o mapa atual; B) heurística automática por convenção de nomes. **Recomendação:** A — previsível e idempotente; heurística de nomes vira outro rabbit hole de manutenção.
|
||||
- **Código de risco sem mapeamento: falhar o PR ou rodar fallback e sinalizar?** **Opções:** A) fail-closed (falha apontando a lacuna) para path de risco; B) fallback + sinalização, mapeamento vira dívida; C) manter só o log. **Recomendação:** A para paths que o classificador já trata como risco — o custo de um falso positivo é rastrear uma linha no mapa; o custo de falso negativo é o bug que este item existe para evitar. _(assumido — validar com produto)_
|
||||
|
||||
## Referências
|
||||
|
||||
- OPS72 — política atual: modo `full` → PR sem e2e (`.github/workflows/ci-pr.yml`, comentário no passo "E2E tests (blast radius…)").
|
||||
- OPS83 — seleção de testes unit/int (precedente recente de `changed`).
|
||||
- Arquivos para o executor abrir primeiro (pistas, não contrato): `scripts/lib/test-affected-core.mjs`, `scripts/ci-scope.mjs`, `scripts/gate-ci.mjs`, `scripts/e2e-affected.*`, `.github/workflows/ci-pr.yml`, `tests/unit/ciSkipInvariants.unit.spec.ts`.
|
||||
- `AGENTS.md` — convenção "dono do PR, dono do CI" e guardrails de banco/testes (tocadas por este item).
|
||||
73
docs/plans/ops87-piramide-e2e-http-sem-browser.md
Normal file
73
docs/plans/ops87-piramide-e2e-http-sem-browser.md
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
# OPS87 — Pirâmide de verificação: migrar asserções de servidor dos e2e pesados para o paradigma HTTP sem browser (escalar o OPS35) + medir
|
||||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-08-24
|
||||
Issue: #833
|
||||
Priority: P1
|
||||
Impeccable: A — N/A (sem UI)
|
||||
Rascunho UI: N/A — sem UI
|
||||
Appetite: ~1–2 dias eng; um outcome verificável
|
||||
Responsável: —
|
||||
|
||||
## Intenção
|
||||
|
||||
O gate de deploy roda a suíte e2e full (~10 min só de e2e) e ela é frágil sob 4 workers. Muita coisa ali existe para garantir comportamento de **servidor** — quem pode ver o quê, o que a página renderiza antes de qualquer JS, o que persiste, para onde redireciona, o que devolve 404 — mas cada teste paga browser, hidratação e interação client mesmo quando a asserção é 100% server-side. Isso é caro duas vezes: tempo de espera de quem deploya e classes de flake que nascem da interação.
|
||||
|
||||
O precedente já existe e está em produção: o OPS35 (#600) entregou o paradigma HTTP full-stack sem browser (fixture `request` do Playwright, login real via REST, HTML renderizado asserido direto), mas só a família Territórios migrou — o resto da suíte continuou crescendo em browser. Queremos escalar o paradigma para as famílias pesadas com fatia de servidor destacável, **medindo o ganho por família**: mesma cobertura, fração do tempo, suíte mais estável. Não é reescrever a suíte; é achar a pirâmide certa — o que é servidor deixa de pagar browser.
|
||||
|
||||
## Persona e fluxo
|
||||
|
||||
- **Persona / contexto:** a equipe que aciona o deploy verify e espera o gate verde; o agente/humano que trava em suíte frágil quando o código mudado era só de servidor.
|
||||
- **Job principal:** obter as mesmas garantias de servidor com uma fração do tempo e menos flake.
|
||||
- **Fluxo desejado:** acionar o verify → a parte de servidor das famílias pesadas roda sem browser → o relatório registra o tempo de cada família (browser vs HTTP) → gate verde mais rápido, mesma cobertura.
|
||||
- **Anti-goals de produto:** não virar "reescrita de toda a suíte"; não remover teste sem equivalente em outro nível; não criar segundo framework de teste; não enfraquecer a garantia de falha de client (console.error/pageerror continua exclusivo de browser).
|
||||
|
||||
## Objetivo e aceite
|
||||
|
||||
- O tempo do e2e full cai e cada família migrada tem o ganho registrado (tempo browser vs tempo HTTP) — o ganho é o outcome, não a migração em si.
|
||||
- A cobertura não diminui: nada é removido sem equivalente em outro nível (int ou HTTP) e as asserções migram 1:1.
|
||||
- A suíte fica mais estável nas famílias migradas (menos interação client = menos classes de flake), sem reduzir o que se assegura.
|
||||
- A migração anda em lotes e o deploy não fica bloqueado no meio de um lote.
|
||||
- Guardrails que valem para o item: RBAC continua assegurado no conteúdo renderizado; auth é real (mesma sessão/cookie) sem duplicar lógica de login; fixtures e ownership existentes são reutilizados.
|
||||
|
||||
## Dados (intenção)
|
||||
|
||||
- **Vou apresentar dados?** Sim, superfície neste item — medição por família (tempo browser vs tempo HTTP, por spec migrado).
|
||||
- **Decisões desbloqueadas:** o humano/equipe decide se o paradigma vale escalar para as próximas famílias e onde parar (critério de parada), com número real de cada família na mesa.
|
||||
- **Forma:** _adiada ao plano de implementação_ — restrição de produto aqui: comparativo por família, tempo medido de verdade, nunca estimativa.
|
||||
|
||||
## Direção no codebase (hipótese)
|
||||
|
||||
- **Áreas prováveis:** a suíte e2e de campanha (specs `campaign*`) e a fixture HTTP já existente do OPS35 — o executor revisa a classificação das famílias, não a re-inventa.
|
||||
- **Precedente a olhar:** OPS35 (#600, closed) — `campaignTerritoriesHttp.e2e.spec.ts` + fixture de login REST/cookie `campaign-token`; `setup.e2e.spec.ts` roda sem browser no mesmo job/servidor — sem infra nova.
|
||||
- **Risco de acoplamento:** não duplicar o que o int já cobre (dados/loaders — o int **não** substitui asserção de HTML renderizado/SSR; o HTTP sim); o guard de falhas client (console.error/pageerror) fica só em browser.
|
||||
|
||||
## Dependências
|
||||
|
||||
- OPS86 (classificador de e2e corrigido passa a selecionar os novos specs HTTP no CI) — suave; este item se beneficia, não depende para começar.
|
||||
|
||||
## Fora de escopo
|
||||
|
||||
- Mudar o job de CI/deploy (OPS88 — item separado).
|
||||
- Consertar flakes individuais de famílias que não foram migradas.
|
||||
- Migrar interação/hidratação: WebAuthn, PWA, viewports, chat, colunas/filtros salvos, autosave, guard de console.
|
||||
- Chamar server actions por HTTP cru.
|
||||
|
||||
## Rabbit holes de produto
|
||||
|
||||
- **Reescrever a suíte inteira.** Se alguém "só completar", vira migração total em browser → meses, sem parada. **Corte neste item:** só famílias com fatia de servidor destacável, um lote por vez, appetite fixo.
|
||||
- **Migrar e não medir.** Sem o comparativo por família não há aceite — volta a ser opinião. **Corte neste item:** medição por família é condição de aceite, não cortesia.
|
||||
- **Duplicar o int.** Asserções que o int já cobre migradas de novo geram cobertura dobrada sem ganho de tempo. **Corte neste item:** só o que o int não cobre (HTML renderizado/SSR/rotas) migra para HTTP.
|
||||
|
||||
## Questões em aberto (produto)
|
||||
|
||||
- **Por onde começar?** **Opções:** (a) maior retorno + baixo risco primeiro (concepts, permission-profile, people, home-actions) e o journey RBAC de municípios em lote dedicado; (b) por tamanho decrescente; (c) pelas famílias mais flaky. **Recomendação:** (a) — colhe o ganho cedo e valida a medição antes do lote grande. _(assumido — validar com produto)_
|
||||
- **Onde para?** **Opções:** (a) parada pelo appetite (~1–2 dias; lote que não couber vira item novo); (b) continuar até esgotar as famílias A/C. **Recomendação:** (a) — o appetite manda; o resto fica documentado como próximo lote.
|
||||
- **Meta numérica para o e2e full?** **Opções:** (a) meta fixa (ex.: <5 min); (b) registrar o ganho por família sem meta no primeiro lote e decidir meta depois. **Recomendação:** (b) — o número real do primeiro lote calibra uma meta honesta; meta arbitrária antes dos dados é risco de corte de garantia.
|
||||
|
||||
## Referências
|
||||
|
||||
- GitHub Issue #600 (OPS35, closed) — precedente HTTP full-stack em produção.
|
||||
- Specs-família citadas na Direção (pistas para o executor, não contrato).
|
||||
- OPS86 / OPS88 — dependência e vizinho de escopo.
|
||||
- `AGENTS.md` — pipeline de verificação de deploy é convenção travada; este item não a altera.
|
||||
72
docs/plans/ops88-ci-verify-setup-unico.md
Normal file
72
docs/plans/ops88-ci-verify-setup-unico.md
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# OPS88 — Verify do CI (PR e deploy): um Postgres, um build, migrate/seed sem repetição
|
||||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-08-24
|
||||
Issue: #834
|
||||
Priority: P1
|
||||
Impeccable: A — N/A sem UI
|
||||
Rascunho UI: N/A — sem UI
|
||||
Appetite: ~0,5–1 dia eng; um outcome verificável
|
||||
Responsável: —
|
||||
|
||||
## Intenção
|
||||
|
||||
O verify — o check do PR e o verify do deploy — reconstrói o mesmo estado várias vezes na mesma run: dois Postgres (um deles existe só para o e2e não herdar resíduo dos testes de integração), dois migrates explícitos, dois seeds, dois builds — e cada build ainda re-roda o migrate por baixo. No fim, o build "default" nem é servido por nada no CI: o e2e roda sobre o build `.next-e2e`, e o deploy de verdade compila no homeserver com a URL real.
|
||||
|
||||
Queremos um setup único e compartilhado: um Postgres por run com um reset determinístico entre fases (limpa o schema, migrate, seed minimal), e um build só quando build+e2e rodam juntos. Menos tempo de máquina por run, menos pontos de falha, mesma garantia — o que protege o e2e do resíduo do int não é o segundo container, é o reset entre fases.
|
||||
|
||||
## Persona e fluxo
|
||||
|
||||
- **Persona / contexto:** mantenedor do pipeline (humano ou agente) que espera a cor da run do PR antes de mergear, ou que dispara o deploy manual e espera o verify fechar; contexto: fila de checks, minutos contados, re-run por falha.
|
||||
- **Job principal:** saber que o PR está pronto para merge e que a publicação é segura, sem pagar o custo de reconstruir o mesmo estado quatro vezes numa run só.
|
||||
- **Fluxo desejado:** numa run de PR que toca código, os testes de integração rodam num Postgres; um reset determinístico limpa o banco entre fases; o e2e serve o mesmo build compilado para produção (`.next-e2e`) — nada compilado duas vezes "só para conferir". No deploy verify, o mesmo fluxo, e a publicação continua gated pelo verify full. Se uma fase falha, a falha aponta a fase, e a re-run parte de um estado limpo.
|
||||
- **Anti-goals de produto:** não virar "rodar menos testes" nem "pular o verify"; não virar paralelização do job único (fail-fast estrutural continua valendo); não mexer no deploy do homeserver.
|
||||
|
||||
## Objetivo e aceite
|
||||
|
||||
- Uma run de PR com build+e2e compila o app uma única vez e roda integração, build e e2e no mesmo Postgres, com reset determinístico entre fases — sem segundo container.
|
||||
- O verify do deploy usa o mesmo setup único e continua gateando a publicação (rede de segurança inegociável).
|
||||
- Nenhuma perda de garantia: os pins de contagem dos testes de integração rodam antes do reset; o e2e continua contra o build de produção, nunca dev; migrate-before-build (OPS66) preservado.
|
||||
- Mensurável: migrates/seeds/builds repetidos caem (4× migrate → ~2×), run mais curta, e o ganho não volta em re-run.
|
||||
|
||||
## Dados (intenção)
|
||||
|
||||
- **Vou apresentar dados?** Não — sem superfície de dados para usuário; tempos de run são telemetria de CI, não dado de produto.
|
||||
|
||||
## Direção no codebase (hipótese)
|
||||
|
||||
- **Áreas prováveis:** `.github/workflows/ci-pr.yml` e `.github/workflows/deploy.yml` (passos de setup/seed/build), `tests/helpers/` (lease/reset/seed), `playwright.config.ts`, scripts de banco/build em `package.json`.
|
||||
- **Precedente a olhar:** `docs/plans/ci-e2e-paralelizar-job.md` (2026-08-10, timings por fase) e `docs/ops/teqo-1313-deploy.md` (contrato build-after-migrate).
|
||||
- **Risco de acoplamento:** o reset determinístico precisa cobrir exatamente o que o e2e não pode herdar do int (linhas fora de ownership, pins de contagem exata, resíduo de run abortada) — quem executa deve provar o reset antes de confiar no banco único; a ordem int → reset → build+e2e é o contrato novo.
|
||||
|
||||
## Dependências
|
||||
|
||||
- Nenhuma dura. Beneficia-se de OPS86 (classifier) e OPS87 (famílias HTTP no mesmo job) — suaves.
|
||||
|
||||
## Fora de escopo
|
||||
|
||||
- Sharding/paralelização do verify — fail-fast estrutural se mantém.
|
||||
- Mudar workers/retries dos jobs de teste.
|
||||
- Migrar testes de nível (OPS87).
|
||||
- Ajustar o classifier de e2e (OPS86).
|
||||
- Qualquer mudança no deploy homeserver, que segue compilando com a URL real.
|
||||
|
||||
## Rabbit holes de produto
|
||||
|
||||
- **"Já que estou aqui, reduzo a suíte."** Se o setup compartilhado virar desculpa para rodar menos testes ou tirar o e2e do build de produção, a garantia cai. **Corte:** nada de rodar menos testes; e2e sempre no build de produção.
|
||||
- **"Economizar o reset."** Se o reset determinístico for visto como custo e removido, o resíduo do int volta a contaminar o e2e — o risco exato que o segundo container existia para resolver. **Corte:** o reset entre fases é a proteção; se não couber no appetite, o item encolhe para o build único e a separação de Postgres fica como está.
|
||||
- **"Unificar os dois builds sempre."** O build default só tem valor quando é o único build da run (PRs sem e2e); colapsá-lo à força exigiria decidir env em tempo de build. **Corte:** colapsar só quando build+e2e rodam juntos.
|
||||
|
||||
## Questões em aberto (produto)
|
||||
|
||||
- **O que garante o e2e limpo: isolamento físico ou reset determinístico?** **Opções:** A) um Postgres com reset entre fases | B) manter dois containers. **Recomendação:** A — o reset é o que protege; o segundo container é o workaround de hoje, e o resíduo do int é finito e conhecido (fixtures, pins de contagem, claims).
|
||||
- **O build default continua existindo?** **Opções:** A) sim, quando é o único build necessário (runs sem e2e) | B) sempre um build só. **Recomendação:** A — o custo de mantê-lo só existe nas runs que já rodam sem e2e. _(assumido — validar com produto)_
|
||||
- **O verify do deploy muda de rigor?** **Opções:** A) mantém o fluxo full, só com setup compartilhado | B) reduz para o setup novo. **Recomendação:** A — a rede de segurança da publicação é inegociável.
|
||||
|
||||
## Referências
|
||||
|
||||
- `docs/plans/ci-e2e-paralelizar-job.md` — timings por fase e histórico do job e2e
|
||||
- `.github/workflows/ci-pr.yml` e `.github/workflows/deploy.yml` — passos atuais de Postgres/seed/build
|
||||
- `tests/helpers/campaignFixtures.ts` e `tests/helpers/testDatabaseLease.ts` — resíduo conhecido do int e leases de banco
|
||||
- `playwright.config.ts` — qual build o e2e serve
|
||||
- `docs/ops/teqo-1313-deploy.md` — contrato build-after-migrate (OPS66)
|
||||
Loading…
Reference in a new issue