teqo/docs/TECH-DEBT.md
Francisco Ignácio Fontoura Solla adc0f31f5b Sync all project docs with the deployed remodel and hardening; compile roadmap
- Compile docs/roadmap.md into a next-steps document (277→206 lines): R0–R5/M1–M5
  tables and the delivered map/list item paragraphs compressed into 'Já entregue';
  janelas, grafo and cortes list only open IDs; C10/C11/C8 fill-ins keep their
  original triggers; C5 moved under 'A validar'.
- Record the production deploy of the destructive remodel chain (2026-07-23 Vercel
  builds) across roadmap, AGENTS.md, the project notebook and plans — supersedes
  every 'deploy pendente'; smoke pós-deploy + onboarding remain the open blockers.
- Fix AGENTS.md internal contradictions: Município locked-decisions bullet (435,
  Camaçari whole, candidate role), campaignUser role enum, deploy-checklist item 7
  as historical, access/* structure note, election-aggregates artifact +
  election-tse revalidate runbook.
- Sync open plans to real identifiers (plaza*/nucleus* → municipality*): the
  intelligence program (master + E8–E15/B13/C12 with artifact/caching context and
  satisfied deps), B8 rescoped to Salvador ZE 1–19 only, WhatsApp D3–D5, D2, A6
  (now the insight layer over stateDeputy), RS+, onda-0 smoke step.
- Close items verified done in code: C8 F4 (DRY de forms) complete, filtros-auto
  shared pending shell, petition generateStaticParams select/depth; ledger the
  surviving user-visible 'Praça' copy in src as new P2 debt.
- Banner delivered praça-era plans (production note + rename pointer); mark
  remodelagem-pracas.md superseded and remodelagem-municipios.md delivered.
- TECH-DEBT smell statuses, TESTING RBAC row, README quality gates, campanha
  UI rules updated to current component names.
2026-07-24 02:04:45 -03:00

18 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
158 unused exports reported by knip (warn-level) god modules, ui kit families dead code low M P2 open — drive exports rule to error after splits Phase 5
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 (5 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 (~15 hits) copy drift low (cosmetic; consent texts are provisional Onda 0 copies) S P2 open — found 2026-07-24 docs sync

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)
13 near-identical formActions.ts try/catch→mapError→redirect ladders src/app/(campaign)/campanha/(app)/*/formActions.ts shared form-action wrapper partially done 2026-07-23 — 4 */nova ladders on runCampaignRedirectFormAction; remainder tracked as C8 F4 (plan)
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 ledgered (tabs stream via Suspense since Phase 3; extraction still open)

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.