docs: Pass 5 engineering audit artifacts
Ledger, quality-plan section, entrega-engenharia-p5, GUARDRAILS harvest (empty miss queue), TESTING e2e count 17/51, and CHANGELOG entry for the 2026-08-03 autonomous audit (delta since Pass 4). Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
This commit is contained in:
parent
e20bbd2718
commit
36ec79f2c2
6 changed files with 225 additions and 6 deletions
|
|
@ -4,6 +4,8 @@ Conteúdo movido do `AGENTS.md` em 2026-07-30 (fatiamento do paradigma de agente
|
|||
|
||||
---
|
||||
|
||||
**Recently resolved (2026-08-03):** **Pass 5 — auditoria de engenharia autônoma** — precheck solitário (pool desligado); delta desde Pass 4 = 627 arquivos / 520 commits; baseline verde (madge **859** arquivos). **0 P0**; **1 P1** — push de notificação via `queueMicrotask` sob `req.transactionID` corria antes do commit (`createCampaignNotification`) → after-commit registry + soft-fail. **0** `kind:agent-miss` abertas. Carry-forward P4-A…P4-L re-medido; ondas novas P5-A…P5-L em [`entrega-engenharia-p5.md`](plans/entrega-engenharia-p5.md).
|
||||
|
||||
**Recently resolved (2026-08-03):** **OPS18** (Issue #296) — promote dual plan-issue: Action `plan-issue-ready-on-main-merge.yml` no merge lê `Related #N` e promove Issues ainda aguardando plano (`canPromotePlanIssue`); idempotente com `pnpm agent:ready`. Planos: [`plan-issue-promote-dual.md`](docs/plans/plan-issue-promote-dual.md), impl [`plan-issue-promote-dual-impl.md`](docs/plans/plan-issue-promote-dual-impl.md).
|
||||
|
||||
**Recently resolved (2026-08-02):** **OPS17** (Issue #292) — ciclo plan-issue: gate pós-overview antes de Issue/PR; `agent:register --plan` nasce `blocked`; após plano em `main`, `pnpm agent:ready` promove a `ready`. Planos: [`plan-issue-lifecycle-ready.md`](docs/plans/plan-issue-lifecycle-ready.md), impl [`plan-issue-lifecycle-ready-impl.md`](docs/plans/plan-issue-lifecycle-ready-impl.md). Action dual no merge = OPS18 (#296).
|
||||
|
|
|
|||
|
|
@ -41,4 +41,10 @@ Ledger dos guardrails determinísticos colhidos de `kind:agent-miss` (fluxo `pnp
|
|||
|
||||
## Misses colhidas aguardando guarda (fila do harvest)
|
||||
|
||||
(nenhuma — todas as 5 abertas em 2026-07-31 foram classificadas: 4 com guarda shippada + 1 com guarda já viva. Novas `kind:agent-miss` entram aqui até o próximo audit.)
|
||||
(nenhuma — Pass 4 fechou as 5 abertas em 2026-07-31; Pass 5 (2026-08-03) colheu **0** `kind:agent-miss` abertas. Novas entram aqui até o próximo audit.)
|
||||
|
||||
## Pass 5 — guardas adicionadas (não-miss)
|
||||
|
||||
| Guarda | Origem | Classe | Mecanismo | Status |
|
||||
| ----------------------------------------------------------------------------------------------- | -------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
|
||||
| Push de notificação de campanha só após commit da transação; send soft-fail se a row sumiu | P5-P1 | 3 + 5 | `onPayloadTransactionCommit` em `payloadTransaction.ts`; schedule em `createCampaignNotification`; pin em `notificationPushSchedule` unit | remediação Pass 5 (PR P5-P1) |
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Improve Code Quality Plan
|
||||
|
||||
Four passes so far: **Pass 1** (2026-07-23/24, phases 0–6 below, all done), **Pass 2** (2026-07-25, see [Pass 2](#pass-2--engineering-consolidation-2026-07-25)), **Pass 3** (2026-07-28, audit + plan, see [Pass 3](#pass-3--engineering-audit--consolidation-2026-07-28)) and **Pass 4** (2026-07-31, audit + P0/P1 remediation + miss guardrails in-session, see [Pass 4](#pass-4--engineering-audit--remediation--guardrails-2026-07-31)).
|
||||
Five passes so far: **Pass 1** (2026-07-23/24, phases 0–6 below, all done), **Pass 2** (2026-07-25, see [Pass 2](#pass-2--engineering-consolidation-2026-07-25)), **Pass 3** (2026-07-28, audit + plan, see [Pass 3](#pass-3--engineering-audit--consolidation-2026-07-28)), **Pass 4** (2026-07-31, audit + P0/P1 remediation + miss guardrails in-session, see [Pass 4](#pass-4--engineering-audit--remediation--guardrails-2026-07-31)) and **Pass 5** (2026-08-03, audit + P1 remediation in-session, see [Pass 5](#pass-5--engineering-audit--remediation-2026-08-03)).
|
||||
|
||||
## Context
|
||||
|
||||
|
|
@ -211,3 +211,47 @@ Statuses: pending · in-progress · awaiting-evidence · done · deferred: \<rea
|
|||
- [x] Artifacts PR = Ready + auto-merge (mesmo contrato `agent-pr-workflow`; sem exceção de merge humano); remediation PRs merge independently
|
||||
- [ ] P4-A → P4-L as independent deliveries ([entrega-engenharia-p4.md](plans/entrega-engenharia-p4.md)); oversized items → Issue rastreável via `plan-issue`
|
||||
- [ ] Leftovers → ledger via `capture-review-debts`
|
||||
|
||||
|
||||
# Pass 5 — Engineering Audit + Remediation (2026-08-03)
|
||||
|
||||
## Context
|
||||
|
||||
- **Started:** 2026-08-03 — second **autonomous (Cursor Cloud)** execution of the `engineering-audit` skill.
|
||||
- **Precheck (fail-closed):** `pnpm agent:pool -- status` → pool **desligado** (desde 2026-08-02); solitary audit confirmed. (`gh workflow run` write unavailable in Cloud — fail-closed path not needed.)
|
||||
- **Delta anchor:** Pass 4 (2026-07-31). **627 files** / **520 commits** in ~3 days of agent-paradigm work: quick-actions registry ×25, wizard/omnibox ×12+, e2e municipalities ×25, home actions, municipality v2 shell (B147), OPS17/18 plan-issue lifecycle.
|
||||
- **Method:** canon loaded (rules, ARCHITECTURE, AGENT-OPS, TECH-DEBT, TESTING, GUARDRAILS, `escala-dry-pos-*`, consolidation precedents, rejected-with-reason); hotspot map; **five parallel sweeps** (lib+scripts, utilities+access, components, app+collections+globals, tests+guard dodges); consolidation hunt under anti-DRY; **0 open `kind:agent-miss`** (fila vazia — #48/#49/#50/#54 already `done`); open ledger rows re-verified; **1 P1** remediated in-session (own PR); P2/P3 → plan/ledger only.
|
||||
- **Baseline:** green — `tsc` 0, `lint` 0 warnings, `knip` 0 findings (known `payload.config.ts` loader noise, ledger P3 — verified still true), madge 0 cycles (**859 files**, +85 since Pass 4's 774).
|
||||
|
||||
## Audit headlines (all measured)
|
||||
|
||||
- **0 P0.** Pass 4 global lockdown held: no `Boolean(user)` update on globals; `CampaignGoals` uses named predicates; collections still declare `access`. Consent fail-closed held.
|
||||
- **1 P1:** `createCampaignNotification` schedules push via `queueMicrotask` while the write often rides `req.transactionID` (hooks → `createCampaignNotifications`). Microtasks flush **before** `withPayloadTransaction`'s `commitTransaction`, so push can read/send for a not-yet-committed (or later-rolled-back) row. **Remediated in-session** (after-commit registry + existence-safe send + guard).
|
||||
- **Miss harvest:** 0 open `kind:agent-miss` → no new miss guardrails. Existing GUARDRAILS.md entries verified alive (allocator, sheet host, e2e navigation, WebAuthn readiness, global access, CLI, localApi overrideAccess).
|
||||
- **Pass 4 P2 waves still open (re-measured):** P4-A homeSearch twins (`utilities/homeSearch` **837** LOC, `limit: 0` ×5); P4-B HomeSearch\*Group ×6; P4-C notificationEvents `findByID`×7 / 254 LOC; P4-D notification write path (mark-all/subscribe/unsubscribe — narrowed, still open); P4-E wizard info Drawer ×3 + skip trailing ×2; P4-F mobile cards → `municipalityListUrl` labels; P4-G provider width + unconditional `aria-live` in `RelationChipCell` (**841** LOC); P4-H–L as ledgered.
|
||||
- **Ledger hygiene:** e2e thin row **17 specs / 51 cases** (was 48); `createSupporter`↔`touchedMunicipalities` still true (`campaignFixtures.ts:768-799`); B34+ F2 twin **closed in code** (`LeadershipStateDeputyRelationCell` delegates to `RelationChipCell`); `/campanha/municipio/.../v2` is **intentional** B147 parallel shell — defer until v2 cutover (not a accidental twin).
|
||||
- **New P2/P3 (delta):** wizard URL dispatch dual-switch (`wizardActionChain` + `campaignActionRoutes` 252 LOC / 31 exports); staff-detail wizard action id list ×3 adapters; `/acoes/[slug]` 296-line page dispatcher; detail-page metadata+page prologue doubles; `campaignFormFields` living in `utilities` imported by client login forms; guard dodges (`Boolean(user)` only via `=> Boolean(user)`; local `function die()` vs CLI guard; allocator/e2e regex edges).
|
||||
- **Rejected / look-alikes:** QuickActions host/overlay/FAB split (not twin of deleted Drawer); `searchOnlyListOmnibox` as degenerate adapter (only `q:` chip knowledge shared); municipality v2 route (product-owned parallel); HomeSearch `*GroupHasHits` too thin; profile/password `overrideAccess: true` on session-bound self-id (established trusted-server pattern — not P0/P1).
|
||||
|
||||
## Remediation deliveries (in-session)
|
||||
|
||||
| WS | Content | Status |
|
||||
| ----- | ------------------------------------------------------------------------------------------------ | ------------------------------------------- |
|
||||
| P5-P1 | Push-after-commit for campaign notifications + soft-fail missing row + convention/unit pin | own PR (Ready + auto-merge), same session |
|
||||
|
||||
## Pass 5 Decisions
|
||||
|
||||
| ID | Decision | Rationale |
|
||||
| --- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
|
||||
| D1 | No P0 this pass — do not invent security theatre on session-bound self-update bypasses | IDs from `getCampaignUser()`; pattern matches WebAuthn/notification modules |
|
||||
| D2 | P1 = transaction/push ordering only; P4-D mark-all/subscribe stays P2 | Partial mark-read is correctness debt already planned; push-before-commit can notify on rollback |
|
||||
| D3 | municipality v2 parallel route is defer-until-cutover, not a consolidation defect | B147 shipped it deliberately; "edit the owner" applies at cutover |
|
||||
| D4 | Miss fila empty → zero new miss guardrails; harden dodges only when shipping related fixes | Skill scope: miss guardrails + P0/P1; dodge map → plan P5-H |
|
||||
| D5 | Aikido MCP still unavailable in this Cloud environment | Same D6 as Pass 4; ledger row unchanged |
|
||||
|
||||
## Next Actions
|
||||
|
||||
- [x] Artifacts PR = Ready + auto-merge
|
||||
- [x] P5-P1 remediation PR = Ready + auto-merge
|
||||
- [ ] P5-A → P5-L as independent deliveries ([entrega-engenharia-p5.md](plans/entrega-engenharia-p5.md)); oversized → `plan-issue`
|
||||
- [ ] Leftovers → ledger via `capture-review-debts`
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ Ledger of known engineering debt. Items large enough to be scheduled work become
|
|||
| Any campaign user can enumerate staff names/roles (`canReadCampaignUsers`) | `src/utilities/access/campaignUsers.ts` | info disclosure | low | S | P2 | open | |
|
||||
| `removeSupporterData` deletes via `overrideAccess: true` while `canDeleteSupporter` is admin-only | `src/app/(campaign)/campanha/actions/supporter.ts` | access asymmetry (intentional LGPD path) | low | S | P3 | closed 2026-07-29 (P3-D): documented in-code — the delete is the staff-driven titular-removal flow, scope already proven by `assertCanManageSupporter` on the exact row; day-to-day deletes stay admin-only | |
|
||||
| Legacy public consent flows hardcode document IDs (`consent: 2`) | `src/app/(frontend)/actions/submitWhatsapp.ts` | correctness | medium | S | P1 | closed 2026-07-25 (Pass 2 D3): resolves `Consent.key = 'whatsapp-inscricao'` fail-closed; migration `20260725_170000` tags the live doc (id 2, key vazio) com a chave | AGENTS Known Gaps #2 |
|
||||
| E2E suite thin (**17 specs / 48 cases** measured 2026-07-31, Pass 4 — grew from 15/31 at Pass 3; most campaign CRUD still int-only, so the substance holds) | `tests/e2e/` | coverage | medium | L | P2 | open | |
|
||||
| E2E suite thin (**17 specs / 51 cases** measured 2026-08-03, Pass 5 — grew from 17/48 at Pass 4; most campaign CRUD still int-only, so the substance holds) | `tests/e2e/` | coverage | medium | L | P2 | open | |
|
||||
| CI runs on GitHub Actions only (no secondary pipeline) | `.github/workflows/ci.yml` | process | low | M | P3 | open | |
|
||||
| E2E campaign journeys flake with 2 workers — **root cause narrowed 2026-07-26 (B13)**, and it is not only latency: (a) `campaignMunicipalities`/`campaign-pwa` click the leader consent checkbox before hydration attaches the handler, and (b) the municipality fixture picks names by regex, so "Conde" also matches "Condeúba". Reproduced on a clean `main` tree, so it is not a B13 regression; both specs pass in isolation | `tests/e2e/campaignMunicipalities.e2e.spec.ts`, `tests/e2e/campaign-pwa.e2e.spec.ts` | test-infra | medium (masks real failures in the delivery gate) | S | P2 | open — mitigated 2026-07-31 (Pass 4): hydration helpers adopted (`checkRadixWhenHydrated`); the `/Cairu/i` substring literals are GONE and a catalog-keyed guard bans unanchored municipality-name regexes (miss #73, PR #93); prod full suite 48/48 green three times this session. What remains is the dev-mode `campaignLeaderships` chip failure — different class, own Pass 4 row below | |
|
||||
| Two e2e tests fail **deterministically at one worker on a clean `main` tree** (measured 2026-07-27 by stashing the whole B34 delivery and re-running): `campaignNearestMunicipality` "coordinator in Salvador is sent to the zone list" cannot find the `Salvador — ZE 1` link on `?q=Salvador`, and `campaignMunicipalities` "advisor opens a demand and decides it" cannot find its demand heading. These are **not** the 2-worker flake in the row above — retrying and lowering concurrency do not help — so the gate currently ships with two red specs whose failure nobody has diagnosed. **Widened 2026-07-28 (B17), same stash-and-compare method: it is not two tests but most of `campaignMunicipalities`** — five failed at one worker on the clean tree (`:56`, `:125`, `:292`, `:394`, `:446`) against four with the delivery applied, and the failing set is not stable between runs (`page.waitForURL` timeouts, a `getByLabel('Média')` strict-mode violation matching both the popover trigger and its input, a click whose target the bottom nav intercepts). Practical consequence for the next delivery: comparing the count of red tests against a stashed tree is the only way to read this spec — naming the two tests above is no longer enough. **2026-07-28 (Pass 3): root cause measured** — unanchored municipality-name regex at `campaignMunicipalities.e2e.spec.ts:263` (**23/435 prefix collisions** in the catalog: Conde/Condeúba, Catu/Caturama, Laje ×3, Barra ×4); the `getByLabel('Média')` strict-mode violation and the hydration-race consent checkbox are unchanged; fixture drift measured — the e2e cleanup lacks E14's `engagementLevel`/`levelNote`/catalog-name reset and owns+deletes the leased `apoiador-cadastro` row. Fix scheduled as **P3-C** ([entrega-engenharia-p3.md](plans/entrega-engenharia-p3.md)) | `tests/e2e/campaignNearestMunicipality.e2e.spec.ts:156`, `tests/e2e/campaignMunicipalities.e2e.spec.ts:56,125,292,394,446` | broken test (or real regression) | high — a permanently red gate trains everyone to ignore it, which is how a real failure ships | S | **P1** | closed 2026-07-29 (P3-C): full suite **31/31 green** at 2 workers. Root causes fixed — stale sort-summary copy (`déficit da meta`→`Cobertura`), `exact` trigger name vs appended status suffix (now anchored regex), option click not selecting (role=option + `toPass` hydration-retry loop, same class as `checkRadixWhenHydrated`), stretched-overlay click needing `force`, leader login using the advisor's password (each `createCampaignUser` mints its own), and 30 s test timeout → 60 s under cold-compile load. Guards: unanchored-template-regex ban in `codebaseConventions`, global `expect` 10 s | |
|
||||
|
|
@ -57,13 +57,22 @@ Ledger of known engineering debt. Items large enough to be scheduled work become
|
|||
| Unconditional `aria-live="polite"` per chip cell (~50 always-registered regions on lideranças); the overlay family already lazy-mounts its region from first open | `src/components/campaign/shared/RelationChipCell.tsx:821-823` vs `CampaignCellEditOverlay.tsx:114-120` | a11y scale | low | S | P2 | open — Pass 4 | P4-G |
|
||||
| Bypass ratchet dodge: one "bypass" comment in the first 40 lines documents **every** `overrideAccess: true` in the file — `notification/*` has 20, `campaignWebAuthnCeremony` 4, zero per-call justifications | `codebaseConventions.unit.spec.ts:541-623`; `src/utilities/notification/*`; `src/utilities/webauthn/campaignWebAuthnCeremony.ts` | guard dodge | medium — the P3-E "comment justifying" intent is satisfied in letter, not spirit | M | P2 | open — Pass 4 | P4-H |
|
||||
| `campaignLeaderships` chip-cell spec fails **deterministically in dev mode** on clean stage (4/4 runs; stash-and-compare 2026-07-31): instrumented with console/response listeners — **no POST fires in 60 s**; signatures vary per run (option visible + click no-ops; option count 1 → 0 after first fill). Prod build green (48/48 full suite ×3 the same day). NOT the P3-C hydration class (input onChange works, listbox renders); a `toPass` retry was attempted and reverted — retrying cannot fix what is not a race | `tests/e2e/campaignLeaderships.e2e.spec.ts:91-101`; suspect surface `RelationChipCell` suggestion state vs dev-mode RSC streaming of `municipalityIndex` | test-infra / possible dev-mode cell bug | medium — a red local signal teaches people to skip the spec | M | P2 | open — Pass 4 (debug workflow, not retry; `E2E_PROD=1` is the honest local mode meanwhile) | P4-L |
|
||||
| God modules on the delta: `municipalityPageData` 548 lines / `loadMunicipalityListPageBundle` 168; `municipalityListUrl` 500 lines / 23 exports (widest interface in utilities); `RelationChipCell` 826 lines / ~11 state hooks (measurement 80, commit 140, render 190) | `src/utilities/municipality/municipalityPageData.ts:307`, `src/utilities/municipality/municipalityListUrl.ts`, `src/components/campaign/shared/RelationChipCell.tsx` | bloater | low | L | P2 | open — Pass 4 (defer + trigger: next facet/sort; next chip relation or AT-scale complaint) | |
|
||||
| God modules on the delta: `municipalityPageData` 503 lines / `loadMunicipalityListPageBundle` 168; `municipalityListUrl` 500 lines / 23 exports (widest interface in utilities); `RelationChipCell` 841 lines / ~11 state hooks (measurement 80, commit 140, render 190) | `src/utilities/municipality/municipalityPageData.ts:307`, `src/utilities/municipality/municipalityListUrl.ts`, `src/components/campaign/shared/RelationChipCell.tsx` | bloater | low | L | P2 | open — Pass 4 (defer + trigger: next facet/sort; next chip relation or AT-scale complaint) | |
|
||||
| `createSupporter` does not mark `touchedMunicipalities` — a supporter-only fixture path can leave strategy fields dirty on the row | `tests/helpers/campaignFixtures.ts:769-799` | test-infra honesty | low | S | P3 | open — Pass 4 | |
|
||||
| `(app)/assessores/novo/page.tsx` redirects without `requireCampaignPageActor` — the P3-I guard bans `getCampaignUser()` in pages but not a missing actor helper (1 of 34 pages) | `src/app/(campaign)/campanha/(app)/assessores/novo/page.tsx:1-6` | convention gap | low (layout still session-gates) | S | P3 | open — Pass 4 | |
|
||||
| `deriveDecisionAuthor` re-spells the actor stamp (`data.decidedBy = req.user.id`) while `campaignAuditFields.ts` owns the pattern (`stampCampaignCreatedBy` hardcodes `createdBy`) | `src/collections/AllocationDecision.ts:53-57` | duplication | low | S | P3 | open — Pass 4 | |
|
||||
| homeSearch int suites cloned ×7 (656 LOC of same-shaped `it('returns empty…'/'scopes…'/'rejects leaders…')` — `describe.each` over domains would own the shared contract | `tests/int/homeSearch*.int.spec.ts` | duplication | low | M | P3 | open — Pass 4 | |
|
||||
| homeSearch int suites cloned ×7 (662 LOC of same-shaped `it('returns empty…'/'scopes…'/'rejects leaders…')` — `describe.each` over domains would own the shared contract | `tests/int/homeSearch*.int.spec.ts` | duplication | low | M | P3 | open — Pass 4 | |
|
||||
| Flake inventory: fixed timeouts (`CEREMONY_TIMEOUT=60s`, HomeActions `timeout: 15000` ×many, ColumnPicker `REFRESH=20s`), `Date.now()` markers in fixture names, e2e deep links now claimed (Pass 4 PR #93) but shared leased consent rows remain load-bearing | `tests/e2e/*`, `tests/int/municipalityPageData.int.spec.ts` | flake risk | low | M | P3 | open — Pass 4 (prefer event waits; allocate municipalities — both guarded since Pass 4) | |
|
||||
|
||||
| **P5-P1 (remediated in-session):** `createCampaignNotification` scheduled push with `queueMicrotask` under `req.transactionID` — microtasks run before `withPayloadTransaction` commit, so push could fire for an uncommitted / rolled-back notification | `src/utilities/notification/createCampaignNotification.ts:38-40`; `src/utilities/payloadTransaction.ts:40-65` | correctness (txn ordering) | high — push for phantom notification; noisy failure on rollback | S | P1 | open — remediating Pass 5 (P5-P1 in-session) | P5-P1 |
|
||||
| Wizard/action href knowledge dual-switched (`wizardActionChain` chain + previous) and wide URL surface (`campaignActionRoutes` 252 LOC / 31 exports) | `src/lib/wizardActionChain.ts`; `src/lib/campaignActionRoutes.ts` | duplication + wide interface | medium — every new wizard step touches both switches | M | P2 | open — Pass 5 | P5-A |
|
||||
| Staff-detail wizard action id list re-spelled ×3 (activity / leadership / demand adapters) | `src/lib/activityQuickActions.ts:18-24`; `campaignQuickActionLeadership.ts:19-25`; `campaignQuickActionDemands.ts:14-20` | duplication | low | S | P2 | open — Pass 5 | P5-B |
|
||||
| `/campanha/acoes/[slug]` page is a 296-line auth+load+branch dispatcher | `src/app/(campaign)/campanha/(app)/acoes/[slug]/page.tsx` | bloater / divergent change | medium — every quick-action lands here | M | P2 | open — Pass 5 | P5-C |
|
||||
| Detail pages double-run actor+load in `generateMetadata` and page body (org / dobradinha / demanda) | `organizacoes/[slug]/page.tsx`, `dobradinhas/[slug]/page.tsx`, `demandas/[slug]/page.tsx` | duplication | low — extra DB round-trips per detail view | S | P2 | open — Pass 5 | P5-D |
|
||||
| Client auth forms value-import pure helpers from `utilities/campaignFormFields` | `LoginForm.tsx`, `ForgotPasswordForm.tsx` ← `src/utilities/campaignFormFields.ts` | boundary | low | S | P2 | open — Pass 5 | P5-E |
|
||||
| Guard dodges measured: `globalAccessConventions` only `=> Boolean(user)`; Local API shorthand `user,`; scripts local `function die(`; allocator find-by-slug+mutate / encoded `municipio`; e2e settle tokens in comments | `tests/unit/*Conventions.unit.spec.ts`; `scripts/check-plans-only-pr-closes.mjs:12-15` | guard dodge | medium on access guards | M | P2 | open — Pass 5 | P5-G |
|
||||
| `/campanha/municipio/[slug]/v2` parallel detail shell (B147) alongside `/municipios/[slug]` | `src/app/(campaign)/campanha/(app)/municipio/[slug]/v2/` | intentional parallel — cutover debt | low until cutover; then divergent change | M | P3 | open — Pass 5 (defer + trigger: v2 replaces v1) | B147 |
|
||||
|
||||
## Smell Inventory
|
||||
|
||||
| Smell | Location | Refactoring | Status |
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Three layers, all local-database-only (see `local-database` skill; tests refuse
|
|||
|
||||
- **Unit** (`tests/unit/**/*.unit.spec.ts`, 117 specs as of 2026-07-31, Pass 4 — +6 convention guards land with the Pass 4 remediation PRs): pure logic; runs with a deliberately invalid `DATABASE_URL` so nothing can touch a database.
|
||||
- **Integration** (`tests/int/**/*.int.spec.ts`, 71 specs): real Payload + Postgres (`teqo_test`); campaign fixtures allocate seeded municipalities via a Postgres sequence so parallel spec files never share one, and purge residue on claim. Since the miss #73 guardrail (Pass 4), specs that mutate municipalities must use the allocator (no pinned slugs) — enforced by `testMunicipalityAllocatorConventions`. Shared consent rows with stable keys are leased (`tests/helpers/testDatabaseLease.ts`), never owned.
|
||||
- **E2E** (`tests/e2e`, Playwright, 17 specs / 48 cases): smoke-level; most campaign behavior is pinned at the int layer.
|
||||
- **E2E** (`tests/e2e`, Playwright, 17 specs / 51 cases (Pass 5, 2026-08-03; was 48 at Pass 4)): smoke-level; most campaign behavior is pinned at the int layer.
|
||||
|
||||
CI (`.github/workflows/ci-pr.yml`) runs parallel jobs — lint (zero warnings), format, typecheck, knip, cycles, unit/int (**affected by the PR diff**: `vitest --changed` against the PR base via `scripts/test-affected.mjs`, full-suite fallback on high-risk paths), e2e (manifest path→spec via `scripts/e2e-affected.mjs` + `scripts/lib/e2e-affected-manifest.mjs`, blocking; production build + `list`/`github` reporters in CI) and build — against a Postgres 17 service with **`db:seed:minimal`**. Push to `main` (`ci.yml`) always runs the full net including e2e.
|
||||
|
||||
|
|
|
|||
158
docs/plans/entrega-engenharia-p5.md
Normal file
158
docs/plans/entrega-engenharia-p5.md
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
# Pass 5 — Auditoria de engenharia e remediação P1
|
||||
|
||||
## Contexto e manchetes
|
||||
|
||||
- **Executado em 2026-08-03, modo autônomo (Cursor Cloud)** da skill `engineering-audit`: precheck solitário verde (pool **desligado**), varredura completa com foco no delta desde o Pass 4 (2026-07-31), **0** `kind:agent-miss` abertas, **1 P1** remediado na mesma sessão em PR próprio. Artefatos = Ready + auto-merge (contrato `agent-pr-workflow`).
|
||||
- **Âncora de delta:** Pass 4. **627 arquivos** / **520 commits** — quick-actions, wizard/omnibox, e2e municipalities, home actions, shell v2 de município (B147), OPS17/18.
|
||||
- **Baseline verde:** `tsc` 0, `lint` 0 warnings, `knip` 0 findings (ruído conhecido `payload.config.ts`), madge 0 ciclos (**859** arquivos, +85 desde Pass 4).
|
||||
- **Manchetes (medidas):**
|
||||
- **0 P0** — lockdown de globals do Pass 4 segurou; collections com `access`; consent fail-closed.
|
||||
- **1 P1** — `createCampaignNotification` agenda push com `queueMicrotask` sob `req.transactionID`; microtask corre **antes** do `commitTransaction` de `withPayloadTransaction` → push pode sair para linha ainda não commitada / depois revertida.
|
||||
- **Fila de misses vazia** — nenhuma guarda nova de miss; GUARDRAILS.md verificado vivo.
|
||||
- **Ondas P4-A…P4-L ainda abertas** (re-medidas) — o Pass 5 **não** as reabre com IDs novos; cita e atualiza contagens no ledger.
|
||||
- **Delta novo:** dispatcher `/acoes/[slug]` (296 LOC), dual-switch de href do wizard, lista de action ids de staff ×3, prólogos metadata+page, `campaignFormFields` em `utilities` no client, dodges de guarda.
|
||||
|
||||
## Protocolo de delta de comportamento
|
||||
|
||||
Inalterado do Pass 3/4: consolidação pode mudar saídas pequenas listadas por item; pins atualizados deliberadamente; URL B18 / schema / Consent fora de alcance sem item nomeado.
|
||||
|
||||
## Onda já executada na sessão (P1)
|
||||
|
||||
| PR | Conteúdo | Guarda (classe) |
|
||||
| ------ | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------- |
|
||||
| P5-P1 | After-commit registry em `withPayloadTransaction` + schedule de push; `sendCampaignPushForNotification` soft-fail se a row sumiu | pin unit do registry (5); convenção: push de notificação nunca via `queueMicrotask` sob txn (3) |
|
||||
|
||||
## Ondas do plano (P2→P3, ordenadas por risco × churn do delta)
|
||||
|
||||
### Onda 1 — Carry-forward Pass 4 ainda aberto (não re-planejar do zero)
|
||||
|
||||
Executar na ordem do [`entrega-engenharia-p4.md`](entrega-engenharia-p4.md). Contagens Pass 5:
|
||||
|
||||
| Item | Medição 2026-08-03 |
|
||||
| ---- | ------------------ |
|
||||
| P4-A homeSearch | `utilities/homeSearch` **837** LOC; `limit: 0` ×5 |
|
||||
| P4-B groups | 6 × `HomeSearch*Group` |
|
||||
| P4-C notificationEvents | 254 LOC; `findByID`×7 |
|
||||
| P4-D write path | mark-all `limit: 200` + `Promise.all`; subscribe sempre `create`; unsubscribe bypass delete |
|
||||
| P4-E wizard drawers | Signal/Trend/Leadership info chrome; Signal/Trend skip trailing ×2 |
|
||||
| P4-F labels | `MunicipalityListMobileCards` value-import de `municipalityListUrl` |
|
||||
| P4-G | provider no chrome staff; `RelationChipCell` **841** LOC + `aria-live` incondicional |
|
||||
| P4-H…L | ratchet bypass, `canReadCampaignUsers`, auth double-read, fixtures, chips-dev flake |
|
||||
|
||||
### Onda 2 — Consolidação do delta (quick-actions / wizard / rotas)
|
||||
|
||||
#### P5-A Dispatcher tipado de href de wizard/ação
|
||||
|
||||
- **Meta:** um `wizardHrefForAction(action, ctx)` (options object); `wizardActionChain` deixa de ter dois `switch` espelhados; enxugar exports de `campaignActionRoutes` (252 LOC / **31** exports).
|
||||
- **Conhecimento duplicado:** mapa ação→href do wizard/chain.
|
||||
- **Guarda:** pins `campaignActionRoutes.unit` + `wizardActionChain.unit` (5); tipo esgota o union de ações (1).
|
||||
- **Esforço:** M · **Sev:** P2
|
||||
|
||||
#### P5-B `STAFF_DETAIL_WIZARD_ACTION_IDS` single-source
|
||||
|
||||
- **Meta:** a lista idêntica em `activityQuickActions` / `campaignQuickActionLeadership` / `campaignQuickActionDemands` vira constante + helper de prefill de município.
|
||||
- **Guarda:** unit do registry (5).
|
||||
- **Esforço:** S · **Sev:** P2
|
||||
|
||||
#### P5-C `/acoes/[slug]` → loader + branches finas
|
||||
|
||||
- **Meta:** `page.tsx` (296 LOC) perde auth/payload/URL assembly para `utilities`/view-model; a page só compõe.
|
||||
- **Guarda:** convenção de prólogo de página (3) — mesmo espírito P3-I.
|
||||
- **Esforço:** M · **Sev:** P2
|
||||
|
||||
#### P5-D Prólogo metadata+page sem double-load
|
||||
|
||||
- **Meta:** helper tipado `requireCampaignDetailPage(slug)` (ou cache React já existente) para org/dobradinha/demanda detail — hoje `generateMetadata` e page repetem o ator+load.
|
||||
- **Guarda:** pin de convenção ou `cache()` obrigatório no loader (3/1).
|
||||
- **Esforço:** S · **Sev:** P2
|
||||
|
||||
### Onda 3 — Fronteiras e guardas
|
||||
|
||||
#### P5-E `campaignFormFields` → `lib/` (ou contrato client-safe)
|
||||
|
||||
- **Meta:** Login/ForgotPassword importam runtime de `utilities/campaignFormFields` — helper puro deve viver em `lib/`.
|
||||
- **Guarda:** ESLint ou convention: client em `(campaign)` não value-importa `@/utilities/**` fora allowlist (2/3).
|
||||
- **Esforço:** S · **Sev:** P2
|
||||
|
||||
#### P5-F Labels de coluna fora de `municipalityListUrl` (= P4-F, priorizar)
|
||||
|
||||
- Já no plano P4; Pass 5 só reafirma: churn mobile+omnibox sobe a prioridade.
|
||||
|
||||
#### P5-G Endurecimento de dodges de guarda (mapa)
|
||||
|
||||
| Dodge | Guarda atual | Endurecimento |
|
||||
| ----- | ------------ | ------------- |
|
||||
| `!!user` / bloco `return Boolean(user)` / helper em subdir de globals | `globalAccessConventions` | banir predicados "truthy user" genéricos + sweep recursivo |
|
||||
| `user,` shorthand / alias de método Local API | `localApiOverrideAccessConventions` | cobrir shorthand e `payload.db` paths críticos |
|
||||
| `function die(` local em scripts | `scriptCliConventions` | banir die local rotulado; exigir `dieWithLabel` |
|
||||
| `payload.find`+slug fixo+mutate; `URLSearchParams` municipio | allocator conventions | cobrir find-by-slug+mutate; encoding |
|
||||
| wrapper `goto` / settle em comentário | e2e navigation | tokens de settle só em statements |
|
||||
|
||||
- **Esforço:** M · **Sev:** P2 (classe segurança nos dois primeiros)
|
||||
|
||||
### Onda 4 — Escala / a11y / debug (carry + delta)
|
||||
|
||||
#### P5-H homeSearch bounded query (= P4-A elevado em prioridade)
|
||||
|
||||
- Mesma meta P4-A; Pass 5 confirma custo: route dispara 6 loaders por keystroke com `limit: 0`.
|
||||
- **Sev permanece P2** (não P1): sem gate vermelho nem vazamento de ACL; é harm de escala medido.
|
||||
|
||||
#### P5-I `RelationChipCell` live-region + tamanho (= P4-G)
|
||||
|
||||
- Trigger Pass 4 ainda válido; **841** LOC.
|
||||
|
||||
#### P5-J Flake chips-dev `campaignLeaderships` (= P4-L)
|
||||
|
||||
- Continua: debug com evidência, não retry.
|
||||
|
||||
#### P5-K Fixtures `createSupporter` → `touchedMunicipalities`
|
||||
|
||||
- Uma linha no helper; pin de cleanup (P3).
|
||||
|
||||
#### P5-L `assessores/novo` + `deriveDecisionAuthor`
|
||||
|
||||
- P3: redirect usa `requireCampaignPageActor` (ou exceção documentada na guarda P3-I); stamp via `campaignAuditFields`.
|
||||
|
||||
## Defer + gatilho (sem ID novo de entrega)
|
||||
|
||||
| Item | Gatilho |
|
||||
| ---- | ------- |
|
||||
| `/campanha/municipio/[slug]/v2` vs `/municipios/[slug]` | cutover B147 filhos / remoção do v1 |
|
||||
| `suggestionCatalog` 646 LOC | 9º pattern ou 2º consumer do evaluator |
|
||||
| `MunicipalityMapPanel` / `BahiaMap` | próxima feature que toque orquestração+camada |
|
||||
| `municipalityListUrl`/`Omnibox` puros em `utilities` | 3º adapter omnibox novo no mês **ou** P4-F labels move |
|
||||
| Omnibox `q:` chip helpers | 3º search-only adapter além dos atuais |
|
||||
|
||||
## Look-alikes rejeitados
|
||||
|
||||
- Profile/password `overrideAccess: true` com `id: user.id` da sessão — padrão trusted-server, não IDOR.
|
||||
- QuickActions host/overlay/FAB — evolução do Drawer antigo, não gêmeo.
|
||||
- `searchOnlyListOmnibox` — adapter degenerado legítimo; só o mecânico `q:` é conhecimento compartilhado.
|
||||
- HomeSearch `*GroupHasHits` — predicados finos demais para abstrair sozinhos.
|
||||
|
||||
## Mapa de guardas
|
||||
|
||||
### Novas nesta sessão
|
||||
|
||||
| Guarda | Classe | Origem |
|
||||
| ------ | ------ | ------ |
|
||||
| Push de notificação só após commit (registry) + soft-fail row ausente | 3 + 5 | P5-P1 |
|
||||
|
||||
### Existentes reafirmadas / a endurecer (P5-G)
|
||||
|
||||
Ver tabela da onda 3. Miss fila: vazia.
|
||||
|
||||
### Judgment-only
|
||||
|
||||
| Item | Convenção |
|
||||
| ---- | --------- |
|
||||
| Parallel municipality v2 shell | PRODUCT/B147; cutover apaga o desvio |
|
||||
| Map god components | trigger já no ledger Pass 4 |
|
||||
| Navegação e2e in-shell vs goto | TESTING.md (já declarado judgment-only no Pass 4) |
|
||||
|
||||
## Regras de execução
|
||||
|
||||
- Uma consolidação por entrega; gate completo bare (`tsc`, lint, format, knip, cycles, test, build) + Aikido onde MCP existir.
|
||||
- Commit de estrutura ≠ commit de comportamento; teste vermelho no meio = revert.
|
||||
- Migrations congeladas; guarda na **mesma** entrega do fix.
|
||||
- Nada vive só no chat — leftovers → `capture-review-debts`.
|
||||
Loading…
Reference in a new issue