From 5af65a2c8488a1572f4d86e9c6fb00e70dacda81 Mon Sep 17 00:00:00 2001 From: Francisco Solla Date: Tue, 25 Aug 2026 14:44:58 -0300 Subject: [PATCH] =?UTF-8?q?docs(testing-audit):=20relat=C3=B3rio=20da=20ru?= =?UTF-8?q?n=202=20+=20entrada=20de=20changelog=20(movimentos,=20fix=20do?= =?UTF-8?q?=20metrics,=20recidiva=20#906)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-08-25-ops96-testing-audit-run2.md | 13 ++ docs/testing-audits/2026-08-25-run2.md | 117 ++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 docs/changelog/2026-08-25-ops96-testing-audit-run2.md create mode 100644 docs/testing-audits/2026-08-25-run2.md diff --git a/docs/changelog/2026-08-25-ops96-testing-audit-run2.md b/docs/changelog/2026-08-25-ops96-testing-audit-run2.md new file mode 100644 index 00000000..16e7fe4a --- /dev/null +++ b/docs/changelog/2026-08-25-ops96-testing-audit-run2.md @@ -0,0 +1,13 @@ +# 2026-08-25 — testing-audit run 2: 4 movimentos int→unit + fix do metrics, recidiva #906 com resíduo + +Segunda execução da skill `/testing-audit` na mesma data (relatório `docs/testing-audits/2026-08-25-run2.md`; a run 1 vive em `2026-08-25.md`). Inventário por 3 leitores read-only → rubrica validada contra o fonte → 5 melhorias com prova verde antes/depois e commits separados, PR único ready SEM auto-merge (desarme via `testing-audit-disarm.mjs`, rearm pós-push): + +1. **M1** — `payloadTransaction` int→unit (10 testes; stub puro, zero Payload/DB). +2. **M2** — `contactPhoneNormalization` int→unit (4; hook do Contact via stub). +3. **M3** — pins zod de `municipalityUpdateCreateSchema` extraídos do spec int que boota Payload → unit (2; Local API permanece int). +4. **M4** — helpers puros de identidade do `campaignAuth.int.spec` (telefone/schema/whatsapp/guarda de migração) → unit (13; RBAC do arquivo intacto). +5. **F1** — `parseVitestStdout` no core do metrics: suite vermelha voltou a imprimir o retrato (sufixo `ELIFECYCLE` do pnpm quebrava o parse; +5 testes unit no spec do core). + +Conservação exata em todos os movimentos (unit+int = 3361). Nenhum teste removido; intocáveis (LGPD/RBAC/lockdown/e2e/estruturais) sem diff. + +**Flake #906 recidivou com nova assinatura**: 7 runs full verdes → 8 runs com falhas só na família Google Calendar (1 falha com `VITEST_MAX_WORKERS=1`); `google_calendar_sync` acumula 38 linhas de resíduo por run full e o teste "no-op…not-configured" (`googleCalendarSyncAction.int.spec.ts:145`) lê doc residual; `db:reset` não curou. Fixes pertencem à Issue #906; nada editado na família à noite. diff --git a/docs/testing-audits/2026-08-25-run2.md b/docs/testing-audits/2026-08-25-run2.md new file mode 100644 index 00000000..51cf5c09 --- /dev/null +++ b/docs/testing-audits/2026-08-25-run2.md @@ -0,0 +1,117 @@ +# Testing audit — 2026-08-25 (run 2) + +Segunda execução da skill `/testing-audit` na mesma data da primeira noite (`2026-08-25.md`, PR #907). Como o arquivo `.md` já existia commitado com o relatório da run 1, esta noite usa o sufixo `-run2` para preservar os dois relatórios — única divergência do template (anotada aqui por transparência). + +## Sumário executivo + +- **4 movimentos int→unit aplicados (29 testes)** com conservação exata: unit+int = 3361 testes em todos os passos. Nenhum teste removido, nenhum enfraquecido. + - `payloadTransaction` (10) — stub puro do wrapper de transação, zero Payload/DB. + - `contactPhoneNormalization` (4) — hook do Contact exercitado via stub. + - `municipalityUpdateCreateSchema` (2) — pins puros de zod extraídos do spec int que boota Payload. + - `campaignAuthIdentity` (13) — helpers de identidade (telefone/schema/whatsapp/guarda de migração) extraídos do `campaignAuth.int.spec`; RBAC do arquivo intacto. +- **1 fix no tooling first-party**: `parseVitestStdout` no core do metrics — suite vermelha agora imprime o retrato (contrato do próprio script), tolerando o sufixo `ELIFECYCLE` do pnpm. +5 testes unit no spec do core. +- **Recidiva do flake #906 com nova assinatura**: após 7 runs full verdes no início da noite, a família Google Calendar passou a falhar de forma persistente sob a mesma carga; com `VITEST_MAX_WORKERS=1` o fracasso colapsa em UM teste determinístico (`googleCalendarSyncAction` "no-op…not-configured") causado por ~38 linhas de resíduo em `google_calendar_sync` deixadas pelo spec C114 num único run full. Reset completo do DB de teste (`db:reset`, OPS88) não resolveu — a família recria o resíduo dentro do próprio run. Evidência em #906 via relatório (sem escrita no tracker). +- Nenhuma edição em: intocáveis absolutos (LGPD/RBAC/lockdown de liderança), intocáveis estruturais (ci-scope, e2e-manifest, workflows, husky), specs e2e, e nenhuma remoção autônoma de teste. +- 0 passos revertidos. + +## Metodologia e tetos respeitados + +Fases: baseline (13:43) → inventário paralelo (3 leitores Task read-only, ~25min) → rubrica com validação de cada achado contra o fonte (~25min) → loop de melhorias sequencial (5 passos) → fecho. Tetos: budget ~6h ✓ (uso real ≈ 2h); ≤6 melhorias ✓ (5); ≤45min inventário ✓; ≤30min/passo ✓ (passo mais longo: ~8min); debug de AFTER ≤10min ✓ (nenhum AFTER falhou — as falhas posteriores são do flake conhecido, não das edições). + +## Retrato da suíte + +### Baseline (13:43–13:44, SHA 8199e2ee) + +| camada | arquivos | testes | parede | +| ------ | -------: | -----: | ---------: | +| unit | 277 | 2620 | 30,9s | +| int | 80 | 741 | 51,4s | +| e2e | 44 | 208 | não medido | + +### Final (SHA deste PR, 008b1ee7) + +| camada | arquivos | testes | parede | +| ------ | -------: | -----------: | ---------: | +| unit | 281 (+4) | 2654 (+34)\* | 40,8s | +| int | 78 (−2) | 712 (−29) | ~184s† | +| e2e | 44 (=) | 208 (=) | não medido | + +\* +29 movidos +5 novos do `parseVitestStdout`. Conservação global nos passos de movimento: unit+int = 3361 constante. +† parede int na medição vermelha (flake); a parede verde do mesmo SHA foi 51,4s. + +Top-8 unit mais lentas no SHA final (tabela completa: `node scripts/testing-audit-metrics.mjs unit --top 15`): + +| arquivo | testes | duração | teste mais lento | +| --------------------------------------------------- | -----: | ------: | ---------------------------------------------------------------- | +| tests/unit/activityOverlay.unit.spec.tsx | 14 | 4,7s | troca dia no calendário e hora/minuto nos selects inline… (2,0s) | +| tests/unit/petitionLead.unit.spec.tsx | 4 | 2,6s | fires exactly one Lead… (1,2s) | +| tests/unit/municipalityRelationEditor.unit.spec.tsx | 9 | 2,4s | serializes same-cell mutations… (0,7s) | +| tests/unit/campaignCellEditOverlay.unit.spec.ts | 19 | 2,0s | opens 'tendência' in a Drawer… (0,3s) | +| tests/unit/eslintConventions.unit.spec.ts | 12 | 2,0s | enforces PascalCase… (1,7s) | +| tests/unit/campaignQuickActionsDrawer.unit.spec.tsx | 15 | 1,8s | posts home-search… (0,4s) | +| tests/unit/campaignColumnPicker.unit.spec.ts | 8 | 1,4s | batches a whole editing session… (0,4s) | +| tests/unit/codebaseConventions.unit.spec.ts | 25 | 1,3s | keeps conflict markers out… (0,9s) | + +Cobertura diagnóstica disponível sob flag (nunca regra): `pnpm vitest run --config ./vitest.unit.config.mts --coverage `. + +## Disposições + +| id | camada | candidato | disposição | prioridade | prova/decisão | +| ------ | -------- | -------------------------------------------------------------------------------------------------------------------------------- | ---------- | ---------- | ----------------------------------------------------------------------------------------------------------- | +| M1 | int→unit | payloadTransaction.int.spec (10 testes, stub de `Payload['db']`, zero DB) | mover ✓ | alta | `tests/int/payloadTransaction.int.spec.ts:23-43` stubs; conservação +10/−10 | +| M2 | int→unit | contactPhoneNormalization.int.spec (hook do Contact via stub, `importOriginal`) | mover ✓ | alta | `tests/int/contactPhoneNormalization.int.spec.ts:24-35`; conservação +4/−4 | +| M3 | int→unit | 2 pins zod de municipalityUpdateCreateSchema dentro de spec que boota Payload | mover ✓ | alta | `tests/int/campaignMunicipalityUpdate.int.spec.ts:71-140` puros; Local API (:142+) permanece int | +| M4 | int→unit | helpers puros de identidade no campaignAuth.int.spec (telefone/schema/whatsapp/migração) | mover ✓ | alta | blocos :441-452 e :551-603 sem payload; RBAC :454-549 intocado; conservação +13/−13 | +| F1 | tooling | metrics CLI morria em suite vermelha (sufixo ELIFECYCLE do pnpm quebrava o JSON.parse) | reforçar ✓ | média | contrato do próprio script ("a red suite still prints its portrait"); `parseVitestStdout` no core + 5 tests | +| P1 | unit | catálogos quick-action repinados (registry × domínio × v2) | proposta | baixa | U1/U3: seams distintos como na run 1 (K1) — duplicata não comprovada | +| P2 | unit | scan de conflict-markers pinado em codebaseConventions + conflictMarkers.unit + CI | proposta | baixa | U2: o describe do codebaseConventions roda `git ls-files` inteiro a cada run | +| P3 | unit | 6 specs do agent-pool sobre scripts dormentes (OPS65) | proposta | baixa | U6: decisão de produto (remover ou suíte "dormant" separada) | +| P4 | int | origin policy pinada 2× (campaignInvite.int :393-550 × campaignInviteOrigin.unit) | proposta | média | I6: casos únicos em cada lado — merge exige toque em arquivo com conteúdo consent (369-391 intocável) | +| P5 | int | campaignInvite.int :298-607 blocos puros (token/whatsapp/schema) | proposta | média | I5: split parcial em arquivo com consent e RBAC — risco > valor à noite | +| P6 | int | cleanup de fixtures pinado 2× (campaignFixtures × campaignFixturesRowCounts) | proposta | baixa | I7: variante row-count estritamente mais forte; remoção exige prova forte | +| P7 | unit | campaignLoginAction asserta mock contra mock (`objectContaining({ login: mocks.login })`) | manter | baixa | U4: pin fraco, inofensivo; os pins reais (mapeamento/TTL) têm valor; remover asserção não é reforçar | +| P8 | unit | campaignAuthMemoization reimplementa `react.cache` num Map caseiro | manter | baixa | U5: asserção de identidade de promise (:69) é o sinal real; limitação documentada | +| P9 | unit | eslintConventions/codebaseConventions/campaignCompositionCleanup pesados (ESLint real, scans full-tree) | manter | baixa | U7/U8/U10: locks de convenção com valor alto; otimização é proposta, não melhoria noturna | +| E1–E12 | e2e | inventário: duplicatas com int (C113 agenda feed, C130 party facet, TTL×3, C143, C88), http-disfarçados, setup.e2e prewarm morto | proposta | variada | leitor e2e; e2e nunca editado/executado à noite — trilho OPS87/medição para as duplicatas HTTP | + +Intocáveis varridos e intactos: consentimento/LGPD (hash do consent no campaignInvite mantido no lugar), RBAC (`campaignAuth` :454-549, lockdown de liderança, I9 triple-pin mantido), intocáveis estruturais sem diff. + +## Evidências verde antes/depois + +| passo | antes | depois | commit | +| ----- | --------------------------------------- | ---------------------------------------------------------- | -------- | +| M1 | 13:54:34 unit 277/2620 ✓ · int 80/741 ✓ | 13:56:04 unit 278/2630 ✓ · int 79/731 ✓ (conservação 3361) | 7de9822f | +| M2 | = anterior | 13:57:42 unit 279/2634 ✓ · int 78/727 ✓ (conservação 3361) | adcfb39a | +| M3 | = anterior | 13:59:40 unit 280/2636 ✓ · int 78/725 ✓ (conservação 3361) | d8866d90 | +| M4 | = anterior | 14:01:47 unit 281/2649 ✓ · int 78/712 ✓ (conservação 3361) | 62a7dab8 | +| F1 | spec core isolado 10/10 ✓ | 14:09:29 spec core 15/15 ✓ · unit full 281/2654 ✓ | 008b1ee7 | + +Provas executadas bare (nunca piped). A partir de 14:05 os runs full de int passaram a falhar APENAS na família #906 (ver abaixo) — as provas AFTER de M1–M4 são anteriores ao surto e verdes. + +## Flake #906 — recidiva com nova assinatura (evidência desta noite) + +- 7 runs full verdes (13:43–14:03) → 8 runs seguintes com falhas na família Google Calendar: 25 falhas com workers default (14:05, 14:07, 14:12), 25 com `VITEST_MAX_WORKERS=4` (14:10), 1 falha com `VITEST_MAX_WORKERS=1` (14:14, 14:18, 14:22, 14:36). +- A família isolada segue verde (14:13:45 `googleCalendarSync` 25/25 em 7,8s) — mesma assinatura "verde isolada, vermelho full" da noite anterior. +- **Nova evidência causal**: `google_calendar_sync` acumulou **38 linhas de resíduo** após um único run full; o teste `googleCalendarSyncAction.int.spec.ts:121-146` ("no-op…not-configured") apaga só 1 doc stale (`limit: 1` no :124-131) e o action então lê outro doc residual → `expected 'synced' to be 'not-configured'` (:145). A hipótese: o afterChange do engine recria docs de config depois do cleanup do spec (corrida hook × passes), e o resíduo faz a falha persistir entre runs. +- `db:reset` completo (OPS88, 14:12) NÃO curou — a família recria o resíduo dentro do próprio run. +- **Conclusão**: recidiva do #906 com evidência de resíduo nova; os fixes pertencem ao trilho da Issue (Fase 1 planejada: assertar `outcome.status === 'synced'` na família; a esta evidência soma-se: cleanup do no-op deve apagar TODOS os docs stale, e a família precisa de tear-down resiliente ao hook). Nada editado nesta família à noite. + +## Propostas não executadas (com razão) + +| proposta | por quê não executou | +| --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | +| P1/P2/P3 (unit: catálogos re-pinados, scan de markers, agent-pool dormente) | decisão de produto/consolidação exige humano; scans de convenção têm valor de lock | +| P4/P5 (int: origin policy 2×, blocos puros do campaignInvite) | split parcial em arquivo com consent/RBAC — risco > valor no turno noturno | +| P6 (int: cleanup de fixtures 2×) | remoção exige prova forte; variante row-count é mais forte, mas duplicata parcial | +| E1–E12 (e2e) | e2e é inventariado, nunca executado/editado à noite; trilho OPS87 + medição para as HTTP | +| Otimizações de latência (U7/U8/U10: cache de scans, tier de conventions) | refactor de specs de convenção sem ganho de cobertura — proposta, não melhoria segura | + +Descartados pela triage (sem valor real): P7/P8/P9 (manter — ver disposições); dedup de sugestões wizard×home-search (I8, seams distintos); triple-pin de lockdown (I9, intocável). + +## Estado final do PR + +Preenchido na Fase 5 (ver seção de desarme abaixo). + +## Fase 5 — desarme do auto-merge + +Protocolo determinístico desta skill: criação ready → desarme → verificação `autoMergeRequest === null && isDraft === false` num ÚNICO comando (`scripts/testing-audit-disarm.mjs`), repetido imediatamente após cada push, nunca "aguardar o CI" para desarmar. Status preenchido abaixo após o fecho.