teqo-backup/docs/TECH-DEBT.md
Francisco Ignácio Fontoura Solla dc9a39f800 Pass 2 W0: pina contratos de URL e loaders das listas + truth-up TESTING/TECH-DEBT
- unit: campaignListUrl (canonicalize/clamp/redirect), supporterUi, actionPlanUi (matriz tab->where), 5 parsers de entidade
- int: campaignEntityListData smokes (escopo por role, busca por marker, shape de paginacao)
- fixtures: suporte a stateDeputy (create + cleanup)
- docs: contagens reais (45/47/7), Praca ~96 hits, conflito C8 F4 resolvido (10 ladders restantes -> W4d)
2026-07-25 13:30:39 -03:00

19 KiB
Raw Blame History

Tech Debt

Ledger of known engineering debt. Items large enough to be scheduled work become entries in roadmap.md (linked in the Ref column); this file tracks the full inventory so nothing is lost. Sourced initially from the 2026-07-23 engineering-standards audits.

Debt Ledger

Item Location Type Risk Effort Priority Status Ref
164 unused exports + 60 unused types reported by knip (warn-level; 158 at Pass 1) god modules, ui kit families dead code low M P2 open — Pass 2 W4b flips exports to error after W4a kills the biggest offender Pass 2 W4b
knip loader noise: Error loading src/payload.config.ts (server-only guard) knip run output tooling noise none (exit 0, static graph resolves) S P3 open
Missing-consent window fixture releases its exclusive lease during the observation window — readers can interleave (rare flake). Full fix: session-level advisory lock spanning delete→op→restore + updated fault-path pins tests/helpers/testDatabaseLease.ts (withMissingInviteConsentFixture) test-infra race low (ms-wide window; shared leases added 2026-07-23 narrow it) M P2 open
users collection has no roles (all admins omnipotent) src/collections/Users.ts RBAC medium (small trusted admin set) M (schema migration) P1 before widening /admin open AGENTS Known Gaps #1
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 (likely intentional LGPD path) low S P3 open — document or align
Legacy public consent flows hardcode document IDs (consent: 2) src/app/(frontend)/actions/submitWhatsapp.ts correctness medium S P1 open AGENTS Known Gaps #2
E2E suite thin (7 specs; most campaign CRUD int-only) tests/e2e/ coverage medium L P2 open
CI runs on GitHub mirror only (no Codeberg/Woodpecker pipeline) .github/workflows/ci.yml process low M P3 open
E2E campaign journeys latency-flake on loaded local machines (5s expect / 30s test budgets vs cold dev compiles; suite green with CI retries) tests/e2e/campaignMunicipalities.e2e.spec.ts test-infra low S P3 open — consider per-step budgets or a warmed dist dir
Post listing cache holds all published posts, filters in memory src/utilities/posts.ts scale (fine at ~tens of posts) low M P3 open — watch corpus growth
Petition generateStaticParams uses uncached getDocuments without select/depth: 0 src/utilities/documents.ts consumers over-fetch (build-time) low S P2 closed 2026-07-23 (Phase 2: getPetitionIds with depth: 0 + empty select)
Compare mode (?compare=) still queries per candidate uncached cross-request src/utilities/municipalityMapData.ts perf low M P3 open — React cache() dedups in-request; add unstable_cache if profiling warrants
Map bundle serializes all years × scenarios (~100KB raw) municipalityMapContract.ts consumers payload size low — gzip crushes repeating keys to ~15-25KB on the wire; instant year/scenario switching is a core analysis flow worth the bytes M P3 deferred 2026-07-23 with rationale (revisit only if RSC payload profiling says otherwise)
Props→state useEffect mirrors in list controls/comboboxes MunicipalityListExpectedVotesControl, AsyncSearchCombobox, StrictCombobox pattern none — reviewed 2026-07-23: guarded-equality sync is the correct "server echo vs local draft" reconciliation (A10 pattern), not derived state — — closed (accepted as correct)
User-visible "Praça" copy survived the M1 rename (map panel title "Mapa das Praças", admin field descriptions, provisional Onda 0 consent texts, SupportStatusBadge summary, fallback/error strings, code comments) rg -n 'Praça' src (~96 hits in 53 files, ~55 user-visible — 2026-07-25 audit) copy drift low (cosmetic; consent texts are provisional Onda 0 copies) S P2 open — scheduled Pass 2 W4f (consent texts stay for the legal batch) Pass 2 W4f

Smell Inventory

Smell Location Refactoring Status
God module: RBAC for every collection in one file (964 lines, ~80 exports) src/utilities/campaignAccess.ts split per domain under src/utilities/access/ done 2026-07-23 (14 domain modules; campaignAccess.ts kept as re-export surface)
God module: ~71 exports of electoral math src/lib/electionInsights.ts split by concern when next touched ledgered
Multi-feature action bag: CRUD + CSV import pipeline + LGPD in one file (722 lines) src/app/(campaign)/campanha/actions/supporter.ts split CRUD vs import done 2026-07-23 (supporterImport.ts)
Clone twins: create/update/staff-assert/unique-conflict skeleton actions/organization.ts ↔ actions/stateDeputy.ts extract shared factory done 2026-07-23 (runStaffEntityMutation in campaignEntityActions.ts)
14 near-identical formActions.ts try/catch→mapError→redirect/return ladders src/app/(campaign)/campanha/**/formActions.ts shared form-action wrapper partially done — C8 F4 (2026-07-21/23) unified the municipality staff wrappers + 4 */nova ladders on runCampaignRedirectFormAction; 10 ladders written since remain hand-rolled (844 lines: assessores, planos, apoiadores ×2, demandas/[slug], liderancas/[id], organizacoes/[slug], dobradinhas/[slug], municipios editar, convite) — scheduled Pass 2 W4d (status conflict with escala-dry-pos-c6.md "F4 entregue" resolved 2026-07-25: F4 was complete for the surfaces that existed then)
Flat 80+-file src/utilities/ junk drawer; lib/ vs utilities/ boundary undocumented src/utilities/, src/lib/ document convention + move misfiled modules done 2026-07-23 (convention in .cursor/rules/engineering-standards.mdc; RefreshRouteOnSave moved to components/)
Praça→Município rename leftovers in identifiers (PLAZA_MAP_YEARS, ACCESSIBLE_PLAZA_IDS_CONTEXT_KEY, …) map/list utilities finish rename done 2026-07-23
Props→state useEffect mirrors MunicipalityListExpectedVotesControl, comboboxes key-remount or fully controlled closed 2026-07-23 (accepted as correct — see Debt Ledger row)
Detail-page RSCs mixing loading + tab UI + forms (300+ lines) municipios/[slug]/page.tsx, planos/[slug]/page.tsx extract tab loaders/sections scheduled Pass 2 W4c (tabs stream via Suspense since Phase 3; extraction still open)
God module: list URL + filters + sort + labels + freshness in one file (765 lines, 59 exports) src/utilities/municipalityUi.ts split along url/filters/sort/labels/freshness seams scheduled Pass 2 W1-D1
components/campaign/ flat with 106 files; utilities/ flat with 92 src/components/campaign/, src/utilities/ within-layer domain subfolders components scheduled Pass 2 W2 (staged per-domain git mv); utilities/ deferred with trigger: a domain gaining its 3rd new module in a month gets its subfolder

Sprout / Wrap Register

Host Sprouted/Wrapped Notes
(none yet)

Adopted Conventions

  • Zero lint warnings. pnpm lint runs with --max-warnings=0; deliberate placeholders use a _ prefix (argsIgnorePattern).
  • as never is banned (ESLint no-restricted-syntax). Production code uses typed helpers (e.g. hookFilledCreateData for hook-completed create data); tests use stub<T>(partial) which keeps property-level checking. A justified eslint-disable-next-line is the only escape hatch.
  • Dead code is deleted, not commented. knip runs in CI (files/dependencies at error level). After any change: pnpm exec knip and remove what died.
  • Collections always declare access explicitly. Payload's default ("any authenticated user") is treated as a bug — see payloadAdminOnly.
  • Generated migration signatures are exempt from unused-args linting (src/migrations/** override); never hand-edit migration content for lint cosmetics.
  • Shared consent fixture rows are leased, never owned in integration tests (tests/helpers/testDatabaseLease.ts); deleting them steals from parallel spec files.

Debt Budget & Broken-Windows Policy

  • New warnings/as never/unused exports fail or get ledgered the same day — no untracked // TODOs.
  • Each delivery that runs /simplify registers surviving debts here or in docs/plans/escala-dry-pos-*.md (existing convention), whichever the item's size warrants.