mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-14 17:43:04 +00:00
- 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)
60 lines
19 KiB
Markdown
60 lines
19 KiB
Markdown
# Tech Debt
|
||
|
||
Ledger of known engineering debt. Items large enough to be scheduled work become entries in [roadmap.md](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](plans/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 `// TODO`s.
|
||
- 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.
|