Fix Backlog
23 sequenced fix tasks + 5 owner decisions, prepared for the fixing agent. Source: FIX_TASKS.md in the repo.
Canonical source: apps/dashboard/docs/audit-2026-09/FIX_TASKS.md (ground rules, per-task acceptance criteria, file:line links). Summary:
Ground rules for the fixing agent
One task = one branch = one PR, referencing the task ID. Re-verify findings before fixing (line numbers may have drifted; if a finding no longer holds, close the task with a note). Follow apps/dashboard/CLAUDE.md + packages/backend/CLAUDE.md patterns. Backend changes backward-compatible only (mobile shares it); never bump the auth-cluster pins. bun run check-all + bun test; browser-verify user-facing changes via http://localhost:3000 (never 127.0.0.1; smoke a real sign-in).
Decisions needed from the owner (do not invent policy)
| # | Decision |
|---|---|
| D1 | Orders tier: client gate dashboard vs backend requireAdminAccess — which is intended? (F-I4) |
| D2 | Ratings sub-pages: nav says admin, gate is dashboard — which is intended? (F-I34) |
| D3 | Authorization policy: gate all pages client-side, or officially adopt backend-only enforcement and strip client gates + fictional comments? (F-B2, F-A5) |
| D4 | lib/toast.ts: adopt or delete? (F-H4) |
| D5 | Rename the live -v2 files (no v1 exists)? (F-H7) |
Sprint 0 — correctness & honesty (≈1–2 days, do first)
| ID | Pri | Task | Findings |
|---|---|---|---|
| T1 | P1 | Hook-layer error contract ({data, error}) + sweep ~35 unchecked result.success sites — the audit's #1 fix | F-B8, F-B1, F-F1, + 12 site findings |
| T2 | P1 | error.tsx at /dashboard + route groups; fix eternal-loading not-found; guard deep-link literal casts | F-C1, F-I3, F-B5, F-I2-14 |
| T3 | P1 | Lock down the two public getCampaignById queries (check mobile callers first) | F-I2-1, F-I43 |
| T4 | P1 | logAdminAction on the three unlogged destructive families | F-D1–D4 |
| T5 | P1 | Confirmations + in-flight guards on the five unconfirmed destructive ops | F-F3/4/5, F-I38, F-I45, F-I2-9 |
| T6 | P2 | Safe-delete list + housekeeping (incl. components.json — prereq for T16) | F-H1–H9, F-A1, F-G14 |
| T7 | P2 | Doc reconciliation (TECH_DEBT, README, CHECKLIST, CLAUDE.md, env docs) | F-H10–H12, F-A9 |
| T8 | P2 | Install @tailwindcss/typography for tiptap; align sanitizer allowlist | F-E3, F-E4 |
Sprint 1 — authorization & states (≈3–5 days, after D1–D3)
| ID | Pri | Task | Findings |
|---|---|---|---|
| T9 | P1 | Authorization presentation sweep (37 pages) per D3; align orders/ratings tiers per D1/D2 | F-B2, F-A5, F-I34, F-I4, F-I42 |
| T10 | P1 | isProfileAccessActive on Shopify write paths; kbar role filter | F-D5, F-B4 |
| T11 | P1 | Ratings: states + working pagination + remove dead controls | F-F2, F-I32, F-I39 |
| T12 | P2 | Dead controls & fake surfaces: make real or remove (marketing-campaigns pagination, fake refresh buttons, fabricated KPIs, dead selection UI) | F-I44, F-I48/49/50, F-I2-7/13, + |
| T13 | P1 | Perf quick wins: overview remount, orders double-subscribe, memoize transforms, subscribe-on-open (bell/media picker), dynamic recharts | F-G3–G8, F-G12, F-I47 |
| T14 | P1 | Backend cost: aggregates migration where the dimension exists; paginate or surface truncation (export warning); bound bulk inputs; kill dead N+1; align export revenue | F-D6–D9, D11, D13, D14 |
| T15 | P2 | Auth-chain latency: optimistic shell render + stale-while-revalidate role snapshot | F-G1, F-G2 |
| T16 | P2 | shadcn refresh sequence: button (safe) → card (113-file visual review); never blind-refresh dialog/table | F-E1, F-E2, F-E12 |
Sprint 2 — structural (as capacity allows)
| ID | Pri | Task | Findings |
|---|---|---|---|
| T17 | P1 | Token system: add --success/--warning/--info, migrate ~650 raw utilities, ESLint guardrail | F-E11 |
| T18 | P2 | Component consolidation: one delete-dialog pattern, one table wrapper, two date recipes, split channel-multi-select, move 3 violating ui/ files | F-E5–E9, E13–E15, F-A6, F-A7 |
| T19 | P2 | Hook/data-layer restructuring: split use-loyalty.ts, extract vouchers data layer, fix inverted imports, move profile schemas | F-A2, F-A3, F-A8, F-A10, F-A11 |
| T20 | P2 | UX consistency: unsaved-changes guards (3/12 forms → all + route-level), badge semantics, header pattern, date/currency/toast wording, draft-clearing bug | F-F6–F9, F-F15–F19 |
| T21 | P3 | Accessibility batch: name ~30 icon buttons, keyboard rows, dialog descriptions, labels | F-F10–F14 |
| T22 | P3 | Backend consistency batch: error-key consistency, dual-minting divergences, privilege tiers, v.any() ×55, compose MAX_BULK, onboarding dead-end, pool atomicity | F-D10–D18, F-J1–J4, F-I35–I37 |
| T23 | P3 | Frontend long tail (work lane-file P3 sections when the queue is empty): CSP/proxy/Sentry hardening (needs an owner decision on strictness), misc polish | F-C4/C5/C7/C9, + |
Sequencing
T1 → T2 → T3 → T4 → T5 unlock everything (T11/T12 need T1's contract). D1–D5 block T9 and parts of T12/T19. Everything is independently shippable.