26 KiB
Impl: Skill /testing-audit — auditoria autônoma da suíte de testes com implementação das melhorias
Status: rascunho Atualizado em: 2026-08-25 Issue: #898 Intenção: docs/plans/ops96-skill-testing-audit.md Appetite restante: herdado (~1–2 dias eng) — ~½ dia de construção (Fases 0–3) + uma noite de execução governada com tetos duros ~6h (Fases 4–5)
Leitura da intenção
- Outcome: disparo
/testing-audità noite; pela manhã existe UM relatório que explica toda a noite: retrato honesto da suíte (contagens/tempos por camada — unit ~221 / int ~96 / e2e ~44 inventariado estaticamente), duplicatas prováveis, candidatos mover/reforçar/remover cada um com disposição + justificativa, melhorias seguras JÁ aplicadas como código (cada passo provou suíte verde ANTES e DEPOIS), passos revertidos registrados e propostas não executadas documentadas. Entrega = UM PR ready (não draft), sem auto-merge armado, CI green e mergeable; descrição do PR = relatório final; o relatório também vive como artefato commitado referenciado pelo PR; fixes em commits separados quando possível (preferência, não requisito duro); diff revisável em pedaços lógicos. - O que NÃO negociar: intocáveis absolutos — testes de consentimento/LGPD fail-closed, access control/RBAC, lockdown de liderança (nunca remover nem enfraquecer autonomamente); OPS72/
ci-scope.mjs+scripts/lib/e2e-affected-manifest.mjsintocáveis (nenhum gate, workflow ou política de seleção e2e muda); remoção autônoma só com PROVA FORTE (na dúvida vira proposta no relatório); teto temporal fixo (auditoria termina, implementação começa); ferramentas só first-party; relatório é artefato de aceite; changelog append-only no mesmo PR; nenhuma % de cobertura vira regra dura; sem rewrite da suíte e sem segundo cadastro de métricas. - O que reavaliar: teto de 6 melhorias aplicadas é hipótese (a primeira noite calibra); formato do relatório após a primeira execução; se durações e2e reais serão um dia necessárias (hoje deliberadamente "não medido"); se o piloto do protocolo na Fase 3 já consome parte da cota noturna.
Abordagem recomendada
flowchart TD
A["Fase 0: baseline verde unit+int"] --> B["Fase 1: tracer<br/>skill + comando + guard"]
B --> C["Fase 2: motor de métricas baratas<br/>script versionado + cobertura atrás de flag"]
C --> D["Fase 3: protocolo na SKILL.md<br/>rubrica, provas antes/depois, tetos, fecho/desarme"]
D --> E["Fase 4: execução noturna<br/>inventário paralelo Task x3 → rubrica → loop de melhorias"]
E -->|"AFTER falho (≤10min/1 retry)"| R["revert do commit + registra"]
R --> E
E --> F["Fase 5: fecho<br/>relatório + changelog + push único + CI green"]
F --> G["DESARME automerge<br/>sempre o ÚLTIMO passo"]
G --> H{"autoMergeRequest === null?"}
H -->|sim| I["PR ready, mergeable, desarmado"]
H -->|não| J["fallback fail-closed:<br/>draft + registro no relatório"]
Decisões de engenharia
D1 — Forma da skill.
- A) SKILL.md única com fases sequenciais executadas pelo próprio agente (estilo
engineering-audit, 182 linhas). - B) Decomposição pesada em sub-agentes Task para tudo (estilo
agent-work-issue, padrão explorador→escritor→revisores→capturador). - C) Híbrido: fases sequenciais definidas na SKILL.md; inventário por camada delegado a leitores paralelos read-only via Task (unit/int/e2e estático); loop de implementação estritamente sequencial no agente principal.
Recomendação: C. A máquina roda à noite sem humano: os leitores paralelos cortam o tempo de parede da fase mais mecânica e são seguros porque não escrevem nada (cada achado vem com caminho+linha e é validado contra o fonte antes de virar disposição); o loop de melhorias precisa de continuidade de estado (provas verde encadeadas, commits incrementais) e não paraleliza sem corromper a prova. SKILL.md alvo: ~150–180 linhas, frontmatter mínimo {name, description}.
Rejeitadas: B (custo de orquestração acima do appetite; implementadores paralelos invalidariam as provas encadeadas); A puro (noite mais longa no inventário, desperdiça a única paralelização segura).
D2 — Coleta de métricas.
- A) Reporters JSON nativos parseados ad-hoc a cada execução (
node -eefêmero ou leitura manual). - B) Script versionado
scripts/testing-audit-metrics.mjs: roda os runners com--reporter=json --outputFilepara arquivo temporário e normaliza contagens por camada + specs mais lentas numa tabela compacta markdown/json. - C) Tudo manual pelo agente lendo saídas dos runners.
Recomendação: B, com teste unitário próprio para a função pura de normalização (tests/unit/testingAuditMetrics.unit.spec.ts). Repetível entre noites, barato (~100 linhas), knip-safe — verificado que knip.json tem scripts/*.mjs como entry, então o script novo não é órfão. Unit roda com DATABASE_URL inválido proposital (test:unit), int exige teqo_test (assertTestDatabase) — o script apenas orquestra os scripts existentes. E2E entra como inventário estático (parse dos spec files: contagem de test(/describe(, tags, fixtures), nunca execução (ver D4/riscos).
Rejeitadas: A (não repetível; joga fora o trabalho toda noite); C (queima tokens, propenso a erro de leitura, não é auditável por teste).
D3 — Cobertura (@vitest/coverage-v8).
- A) Instalar como devDependency NESTE PR, pinada na mesma versão major/minor do vitest instalado, acionável APENAS por flag documentada na skill (
pnpm vitest run --config ./vitest.unit.config.mts --coverage); acrescentar@vitest/coverage-v8aoignoreDependenciesdoknip.jsonpreventivamente (o plugin só carrega sob--coverage; knip flaggaria como dependência não usada e deixaria o gate vermelho durante a noite). - B) Instalar sob demanda na primeira execução (skill instrui
pnpm add -D @vitest/coverage-v8). - C) Não instalar.
Recomendação: A. Determinística: o PR entregue já sai com o comando funcionando e os gates previsíveis à noite. O ajuste no knip.json é whitelist declarativa de dependência invocada por flag — mesmo padrão das entradas existentes (tailwindcss-animate, tw-animate-css, shadcn, eslint-config-next) — e NÃO enfraquece check algum; nenhum gate/script passa a exigir ou reportar cobertura; nenhuma % vira regra (corte da intenção preservado).
Rejeitadas: B (worktree sujo no meio da noite, risco de esquecer o commit, comando documentado mas inoperante no PR entregue, surpresa de knip às cegas); C (rejeitada pela própria intenção — decidiu SIM atrás de flag; motivo registrado: diagnóstico barato e first-party do ecossistema vitest, opcional por construção).
D4 — Protocolo verde antes/depois + tetos.
- Prova padrão de cada passo:
pnpm test:unit+pnpm test:intverdes ANTES e DEPOIS da edição (int roda contrateqo_wt<slot>_test, já provisionada neste worktree — validar conexão na Fase 0; unit roda com DATABASE_URL inválido por design). Passos confinados a arquivos unit podem provar unit-only, mas o bloco de fechamento roda o par completo uma vez. - Categorias seguras de melhoria: mover (spec na camada errada realocado dentro da mesma suíte — ex.: teste puro vivendo em arquivo int); reforçar (assertion faltante dentro de escopo já testado); remover SOMENTE com prova forte: duplicata demonstrada (diff das assertions mostra comportamento idêntico ao teste gêmeo citado) ou teste morto de feature removida do codebase (referência morta verificada). Na dúvida → proposta no relatório.
- Intocáveis (nunca remover/enfraquecer): consentimento/LGPD fail-closed, access control/RBAC (
src/utilities/access/*), lockdown de liderança. Duplicata aparente envolvendo intocável vira proposta sempre. - Evidência: linhas-resumo literais dos runners ("Test Files X passed", "Tests Y passed") + hash curto do commit + timestamp, gravadas por passo na tabela de evidências do relatório. JSON bruto é efêmero (fora do git).
- Tetos duros: budget global da noite 6h; máx 6 melhorias aplicadas; 30min por passo incluindo provas; AFTER falho → debug ≤10min/1 retry, senão
git reset --hard HEAD~1e registrar como "revertido" com motivo; auditoria termina antes de implementar; estourou teto → candidato vira proposta.
Recomendação: exatamente o acima. Rejeitadas: provar só com unit (regressões de int ficariam invisíveis — melhorias movem specs entre camadas); provar rodando build/e2e a cada passo (custo proibitivo); sem teto (viola o corte explícito da intenção "auditoria termina, implementação começa").
D5 — Desarme do auto-merge (verificado no fonte).
Todo PR ready OPEN base main nasce ARMADO: .github/workflows/agent-pr-ready-automerge.yml dispara em opened/reopened/synchronize/ready_for_review/converted_to_draft e decideAutomergeAction (scripts/lib/github-pr-flow.mjs) arma automerge em qualquer ready OPEN base main (único veto estrutural = draft fora de cursor/*). Cada novo push REARMA via synchronize. Sequência obrigatória de fecho:
- Push único com tudo (relatório, changelog, melhorias) — minimiza rearms; o hook
.husky/pre-pushrodagate:push(gate-ci sem e2e + docs-guards) como validação gratuita pré-push. - Aguardar o run do workflow de automerge deste SHA final terminar (terá armado o automerge no synchronize).
gh pr merge <N> --disable-auto.- Verificar:
gh pr view <N> --json isDraft,autoMergeRequest,mergeable,mergeStateStatus→ esperado{false, null, MERGEABLE, CLEAN}. - Fallback fail-closed: se
--disable-autofalhar ouautoMergeRequestpersistir não-nulo → converter para draft (gh pr ready <N> --undo; draft fora decursor/*é veto estrutural, exit 0) e registrar no relatório que o humano vira ready pela manhã. Draft protegido é preferível a main mergeado sem querer.
Recomendação: sequência acima, executada SEMPRE como último passo da noite. Rejeitadas: entregar draft e virar ready de manhã (viola o aceite explícito — ready não draft — e o desarme resolve a causa real); confiar no humano para desarmar (viola aceite); não fazer nada (PR se auto-mergeia sozinho assim que o CI ficar verde, durante a noite). Regra derivada: qualquer push posterior ao desarme rearma — portanto NUNCA rebasar/repushar após o desarme (ver Riscos).
D6 — Relatório: localização e forma.
- A)
docs/plans/ops96-skill-testing-audit-report.md. - B)
docs/testing-audits/<YYYY-MM-DD>.md(diretório dedicado, chaveado por data). - C) Artefato fora do repo (gist/comentário de issue).
Recomendação: B, convenção definida dentro da SKILL.md para reuso por execuções futuras; esta entrega grava docs/testing-audits/<data>.md. Regra "descrição do PR = relatório": o corpo do PR contém o relatório integral (ou versão condensada com ponteiro para o artefato, se ultrapassar limites razoáveis de UI). Template embutido na SKILL.md com seções fixas: sumário executivo; metodologia e tetos respeitados; retrato por camada (unit/int com contagens+durações medidas; e2e inventário estático com durações "não medido — execução noturna proibida"); tabela de disposições (id, camada, arquivo, tipo mover/reforçar/remover/manter, prioridade alta/média/baixa, prova, decisão aplicada/proposta/revertida); evidências verde antes/depois por passo; passos revertidos com motivo; propostas não executadas; estado final do PR. Pedaços lógicos do diff: commits separados por scaffolding (tracer / métricas / protocolo+deps) e 1 commit por melhoria aplicada; relatório+changelog no(s) commit(s) finais.
Rejeitadas: A (acopla saída recorrente ao diretório de planos por ops; polui docs/plans/ nas próximas noites); C (quebra o aceite "relatório vive como artefato no repo").
D7 — Guard de comandos e espelho.
Atualizar o array pinado ['work-issue', 'plan-issue'] em tests/unit/opencodeCommands.unit.spec.ts para incluir 'testing-audit' e criar .opencode/commands/testing-audit.md no formato stub existente: frontmatter com description: (TUI) e model: deepseek/deepseek-v4-flash; corpo curto que carrega a skill testing-audit pelo nome exato, aponta a fonte canônica .agents/skills/testing-audit/SKILL.md (sem transcrever) e passa $ARGUMENTS. O guard exige: arquivo existe, skill existe, `testing-audit` literal, $ARGUMENTS, .agents/skills/testing-audit/SKILL.md literal, frontmatter válido — é o tracer da Fase 1.
Componentes / mudanças
.agents/skills/testing-audit/SKILL.md(~150–180 linhas, frontmatter{name, description}): objetivo; fases 0–5; rubrica de disposições + prioridades; protocolo verde antes/depois com evidências; categorias seguras + prova forte para remoção; lista de intocáveis; tetos duros; instruções dos leitores paralelos Task (read-only, citação obrigatória caminho:linha); fecho noturno completo incluindo a sequência de desarme D5 verbatim; template do relatório; apontamentos explícitos: pirâmide das 3 camadas CITADA detest-driven-development§144–198 +docs/TESTING.md(não duplicada); declaração explícita de que NÃO herda o contrato auto-merge deengineering-audit; ci-scope/e2e-affected intocáveis..opencode/commands/testing-audit.md: espelho stub no formato D7.tests/unit/opencodeCommands.unit.spec.ts: array ganha'testing-audit'.scripts/testing-audit-metrics.mjs: orquestra reporters JSON nativos, normaliza contagens/durações, emite tabela markdown + json opcional.tests/unit/testingAuditMetrics.unit.spec.ts: testa a normalização (fixtures de payload vitest JSON).package.json+ lockfile: devDependency@vitest/coverage-v8(versão casa com vitest instalado).knip.json:ignoreDependencies+=@vitest/coverage-v8.docs/testing-audits/<data>.md: relatório da primeira execução noturna (artefato de aceite).docs/changelog/<data>-ops96-testing-audit.md: entrada append-only (exigência docs-guards).- Melhorias aplicadas: até 6 commits, um por passo, cada um com provas registradas.
Fases verificáveis
Fase 0 — Preparação e baseline (antes de qualquer edição).
- Validar worktree: deps instaladas,
DATABASE_URLdo.env.test.localaponta parateqo_wt<slot>_test(nunca produção — guards locais intactos). - Rodar
pnpm test:unitepnpm test:int→ verdes; registrar contagens baseline (esperado ~221/~96) no futuro relatório. - Verificação: baseline verde registrado; nenhum arquivo modificado ainda.
Fase 1 — Tracer: skill + comando + guard.
- Criar SKILL.md esqueleto (fases nomeadas), espelho
.opencode/commands/testing-audit.md, adicionar'testing-audit'ao array do guard. - Verificação:
pnpm test:unit opencodeCommandsverde com os 3 comandos pinados; commit próprio.
Fase 2 — Motor de métricas baratas.
scripts/testing-audit-metrics.mjs+ spec de normalização; devDependency@vitest/coverage-v8+ entrada emknip.json.ignoreDependencies; comando de cobertura documentado na SKILL.md (flag only).- Verificação: script roda na suíte atual e emite tabela ~221 unit / ~96 int com specs mais lentas; spec novo verde;
pnpm exec knipsem novos alertas; commit próprio.
Fase 3 — Protocolo de execução segura dentro da SKILL.md.
- Escrever rubrica, provas antes/depois com evidências, categorias seguras, prova forte de remoção, intocáveis, tetos, regra "e2e read-only", fecho/desarme D5, template do relatório.
- Piloto opcional (se tempo): aplicar 1 melhoria trivial seguindo o protocolo à risca, para validar o mecanismo antes da noite.
- Verificação: checklist de autossuficiência — um agente lendo SOMENTE a SKILL.md consegue executar a noite inteira sem contexto extra;
pnpm lint+pnpm typecheckverdes.
Fase 4 — Execução noturna governada (a skill rodando sobre a suíte real).
- Inventário paralelo via Task ×3 leitores read-only (unit/int/e2e estático), ≤45min, achados com caminho:linha.
- Consolidação + rubrica ≤30min: cada candidato recebe disposição (mover/reforçar/remover/manter/proposta) + justificativa + prioridade.
- Loop sequencial de melhorias: ≤6 passos, 30min cada, provas verde antes/depois por passo, 1 commit por passo; AFTER falho → ≤10min/1 retry → revert + registro.
- Verificação: cada melhoria = commit com evidências no relatório;
git diff origin/mainNÃO toca ci-scope/e2e-affected/workflows/husky nem paths intocáveis; zero edições em arquivos e2e; tetos respeitados.
Fase 5 — Fecho noturno.
- Finalizar
docs/testing-audits/<data>.md; changelog append-only;pnpm lint && pnpm format:check && pnpm typecheck && pnpm exec knip && pnpm check:cycles && pnpm test:unit && pnpm test:intno SHA final. - Push único (hook roda
gate:push); abrir PR ready base main; colar relatório como descrição; aguardar CI green no SHA final (monitorar; flake aparente → rerun 1×; quebra real → fix-forward ≤2 ciclos). - Desarme (ÚLTIMO passo): sequência D5 completa; verificar
{isDraft: false, autoMergeRequest: null, mergeable: MERGEABLE}. - Verificação:
gh pr view --json isDraft,autoMergeRequest,mergeable,mergeStateStatusconfirma ready + desarmado + mergeable; fallback draft só se desarme falhar, com registro explícito no relatório.
Rabbit holes / Não escopo (engenharia)
- Rodar Playwright/e2e à noite — nem full, nem smoke. Custo alto + política OPS72 + flake noturno. E2E é inventariado estaticamente (contagens, estrutura, tags) com durações marcadas "não medido"; qualquer mudança em spec e2e é proposta, nunca edição autônoma.
- Não criar segundo cadastro/dashboard de métricas históricas. O script gera tabela pontual para o relatório; sem persistência além do artefato markdown.
- Não tocar
ci-scope.mjs,e2e-affected-manifest.mjs, workflows, husky, gates, seleção e2e — nem "só para arrumar" algo visto no caminho (vira proposta no relatório). - Não registrar Issues no tracker (propostas vivem apenas no relatório; captura de débitos é decisão humana posterior).
- Não perseguir % de cobertura como meta ou regra; flag diagnóstica apenas.
- Não reescrever/mover testes em massa; remoção autônoma só com prova forte; intocáveis nunca, nem com prova.
- Não rebasar após o desarme (rearma automerge); conflito com main é registrado para ação humana.
- Ferramentas só first-party: reporters nativos vitest/playwright +
@vitest/coverage-v8. Nada de libs third-party de análise de suíte. - Não implementar agendamento/cron para a skill disparar sozinha; o disparo é humano (
/testing-audit).
Riscos e mitigação
| Risco | Mitigação |
|---|---|
Rearme automático do automerge a cada synchronize |
Push único ao fecho; desarme SEMPRE último passo; verificação explícita de autoMergeRequest === null; fallback fail-closed draft; regra "não repushar após desarme". |
| CI flaky overnight bloqueia "green" da manhã | Máxima prova local pré-push (gate:push roda cascata sem e2e no hook); pós-push: 1 rerun se flake aparente, ≤2 ciclos de fix-forward; esgotado → registrado no relatório como pendência conhecida (não corrompe main porque automerge está desarmado). |
knip flagga @vitest/coverage-v8 e deixa o gate vermelho à noite |
Whitelist preventiva em knip.json.ignoreDependencies (D3-A); validado na Fase 2 com pnpm exec knip. |
test:int sem DB do worktree disponível |
Fase 0 valida teqo_wt<slot>_test antes de tudo (worktree já provisionado). Se falhar: melhorias restritas a provas unit-only e int vira verificação adiada REGISTRADA no relatório (fail-closed honesto). |
| Conflito com main ao amanhecer (main avançou durante a noite) | NÃO rebasar após o desarme (rearmaria); registrar no topo do relatório "main avançou; rebasar é passo humano". |
| Melhoria "segura" quebra comportamento não coberto pelos testes | Provas antes/depois por passo; categorias restritas (mover/reforçar/remover-com-prova); revert barato (commit isolado); teto de tentativas. |
| Tempo estoura / noite não cabe tudo | Tetos duros por fase; auditoria termina antes de implementar; excedentes viram propostas; hard stop: se faltar tempo, pular direto ao fecho com o que há. |
| Leitor Task alucina achados | Leitores read-only obrigados a citar caminho:linha; consolidador valida cada achado contra o fonte antes de virar disposição; implementação nunca delegada. |
| PR auto-mergeia durante janela entre CI green e desarme | Janela minimizada: desarme imediatamente após o run do workflow do SHA final terminar; monitoramento ativo nessa janela; fallback draft. |
Aceite de engenharia (checklist)
.agents/skills/testing-audit/SKILL.mdexiste, autossuficiente, cita OPS90 §144–198 +docs/TESTING.mdsem duplicá-los, declara explicitamente que NÃO herda o contrato auto-merge deengineering-audit, e contém a sequência de desarme D5 verbatim..opencode/commands/testing-audit.mdstub no formato do guard: frontmatterdescription:+model: deepseek/deepseek-v4-flash,$ARGUMENTS, aponta.agents/skills/testing-audit/SKILL.md.- Guard
tests/unit/opencodeCommands.unit.spec.tsinclui'testing-audit'e passa. - Script de métricas versionado + spec de normalização verde;
pnpm exec kniplimpo. @vitest/coverage-v8instalado (versão casa com vitest), acionável só pela flag documentada;knip.json.ignoreDependenciesajustado; nenhum gate/script usa ou exige cobertura.- Relatório
docs/testing-audits/<data>.mdcommitado: retrato por camada (unit/int medidos; e2e inventário estático), disposições com justificativa, evidências verde antes/depois por melhoria, reverts registrados, propostas restantes; mesma versão (ou condensado + ponteiro) como descrição do PR. - Cada melhoria aplicada em commit próprio quando possível; diff revisável em pedaços lógicos.
git diff origin/mainNÃO tocaci-scope.mjs,e2e-affected-manifest.mjs,.github/workflows/,.husky/nem política de seleção e2e.- Intocáveis intactos: consentimento/LGPD fail-closed, access control/RBAC, lockdown de liderança.
- Zero edições em arquivos e2e nesta execução.
- Changelog append-only
docs/changelog/<data>-<id>.mdpresente no mesmo PR (docs-guards verdes). - PR ready (não draft), CI green no SHA final, mergeable;
autoMergeRequest === nullverificado viagh pr view— ou fallback draft registrado com motivo no relatório. - Tetos respeitados: ≤6 melhorias aplicadas, ≤30min/passo, ≤2 ciclos de correção pós-push, budget ~6h.
pnpm lint, format check, typecheck,pnpm exec knip,pnpm check:cycles,pnpm test:unit,pnpm test:intverdes no SHA final.
Self-score decision-quality
| Pergunta | Nota | Justificativa |
|---|---|---|
| Decisões caras têm Opções/Recomendação/Rejeitadas? | 5 | D1–D7 todas deliberadas; as caras de reverter (forma da skill, protocolo de provas, desarme, cobertura) com alternativas e motivos de rejeição explícitos. |
| Cabe no appetite (~1–2 dias eng)? | 4 | Construção enxuta reusando padrões existentes + execução noturna amarrada por tetos duros; −1 pela variância inerente à primeira execução real. |
| Rabbit holes nomeados? | 5 | Lista própria + todos os cortes da intenção preservados (teto temporal, prova forte para remover, e2e read-only, ferramentas first-party, relatório como aceite). |
| Depth check (reuse > invenção)? | 4 | Reusa guard/comando existentes, reporters nativos, padrões engineering-audit/agent-work-issue, scripts de gate; −1 pelo pequeno script de métricas novo (justificado pela repetibilidade). |
| Intenção satisfeita? | 5 | Outcome integral coberto: relatório único explicando a noite, melhorias com provas antes/depois, reverts e propostas documentados, PR ready sem automerge armado, anti-goals respeitados. |
Média: 4.6 ≥ 4 — gate de qualidade satisfeito.