O cleanup do campaignE2EFixtures estava completo em runs normais (proxy auto-own cobre contact/stateDeputy); o lixo vinha de runs crashados, cujo cleanup nunca roda. O claim e2e agora purga resíduo como o int já fazia, e a purga ganhou a classe dobradinha (stateDeputy vinculados via municipality.stateDeputies + fichas órfãs com ref-check incluindo stateDeputy — FK ON DELETE RESTRICT). peopleList.int.spec.ts passa a varrer todas as páginas (lista global de 25/pág) com dedupe; personDelete/peopleList usam personName() nos nomes fixos, imunes ao invariante global de nome de dobradinha.
4.2 KiB
OPS46 — Infra de teste e2e: e2e local quebrado (next/cache) + allocator de município com wrap
Status: rascunho Atualizado em: 2026-08-13 Issue: #? (a criar via agent:register) Intenção: débitos do OPS45 (capture-review-debts autônomo)
Fase 1 — e2e local não coleta: Cannot find module 'next/cache' (máquina do humano)
Sintoma: pnpm exec playwright test --list falha em todo spec — Error: Cannot find module '<repo>/node_modules/next/cache' imported from src/utilities/documents.ts.
Reproduz no worktree OPS45, no main repo (/home/fsolla/Code/teqo) e sob Node
v20.20.2 / v24.14.0 / v24.18.0 — pré-existente ao OPS45 (não é do diff).
Mecanismo medido: import { revalidateTag } from 'next/cache' (ESM, subpath
sem extensão) falha no resolver ESM nativo do Node — next/package.json NÃO tem
exports, então next/cache (arquivo cache.js) não resolve em ESM puro
(node --input-type=module -e "import('next/cache')" → ERR_MODULE_NOT_FOUND;
import('next/cache.js') → OK). O carregamento do graph do fixture e2e
(src/payload.config.ts → documents.ts) passa por esse import; o transpiler
do Playwright não reescreve bare specifier sem exports. CI passa (mesma versão
de next/playwright) — investigar o que difere no ambiente CI.
Ações prováveis: descobrir o deltas CI vs máquina (versão de node do runner,
flags de resolução, NODE_OPTIONS, corepack) e/ou tornar o import resolvível
(registrar alias next/cache → next/cache.js no carregador do Playwright,
ou mover revalidateTag para um shim com subpath com exports). Evidência de
referência: reproduzir em docker com node 24 latest (mesmo do CI) para isolar.
Rabbit holes: não é o OPS45; não mexer nos fixtures; não é npm registry.
Fase 2 — Allocator de município com wrap de módulo: colisão de claim deleta linhas vivas
Sintoma latente: nextval % municipalityCatalog.length (435) na sequência
persistente compartilhada campaign_fixture_municipality_alloc (int + e2e).
A sequência nunca é resetada; após 435 claims no MESMO banco (DBs persistentes
de worktree acumulam — o problema do OPS45), dois runs concorrentes podem
receber o MESMO índice e cada um purga as linhas vivas do outro (o purge-on-claim
— int desde OPS19/D10 F1, e2e desde OPS45 — transforma a colisão em corrupção).
Ações prováveis: registry de claims com geração (index, generation,
UNIQUE em index, ON CONFLICT retry) ou fail-loud no wrap; medir primeiro a
contagem real de claims por run completo (int + e2e) para dimensionar.
Rabbit holes: não alterar o contrato de nomes/URLs; não tocar no
testMunicipalityAllocatorConventions.unit.spec.ts sem migrar os guards.
Já resolvido no OPS45 (não reabrir)
- stateDeputy no ref-check de fichas órfãs + teste da forma compartilhada
(supporter+dobradinha) —
purgeMunicipalityResiduenão crasha mais. stateDeputies: []gated no resíduo (sem write por claim limpo).loadAllPeopleRowscom loop auto-curável + dedupe por contactID.
Explicitamente fora (deste lote)
- Contas staff residuais ('Coordenadora C128-') — sem âncora de
município e sem padrão seguro (emails
@example.comcoincidem com runs vivos); absorvidas pela paginação do peopleList. Gatilho: se voltar a quebrar um spec global, reavaliar sweep por created_at + ausência de joins. activity.tasks[].responsiblefora do ref-check — FK SET NULL, silencioso e auto-curável; shape improvável (atividade residual em município não claimado referenciando ficha purgada). Gatilho: 2ª referência a Contact fora do check.- personName letteriza o runID (invisível ao discoverMarkedRoots) —
latente até um spec alimentar personName via action real (sem proxy).
Gatilho: o 1º uso de personName fora do
createContact. - Import do módulo vitest no fixture e2e — funciona hoje (hooks inertes fora do runner); gatilho: vitest mudar o entry / quebrar o import.
Aceite de engenharia
- Fase 1:
pnpm exec playwright test --listcoleta na máquina do humano - Fase 2: sem colisão de claim após N wraps (teste de stress ou fail-loud)
- Guards:
testMunicipalityAllocatorConventionsverde após a mudança