# Master backlog index

**Single source of truth for everything outstanding.** Start here when
asking "what's left to build/fix?". Items are grouped by source doc;
each entry links back to the original spec for full context.

Last updated: 2026-05-08 (second non-Documenso sweep — storage-proxy
port-binding, system_settings NULLS NOT DISTINCT + dedup migration,
response-shape standardization, parseBody migration, custom-field merge
tokens, /api/v1/files companyId+yachtId filter, Company Documents tab,
file-upload zone wired for company/yacht targeting). Documenso phases
2-7 stay back-burnered per user.

---

## A. Documenso build (deferred for later)

**Source:** [`docs/documenso-build-plan.md`](./documenso-build-plan.md) — full phase plan with locked decisions (Q1–Q10).
**Tracker delta:** [`docs/admin-ux-backlog.md`](./admin-ux-backlog.md) — what landed in Phase 1.

Phase 1 (EOI generate flow polish + APPROVER-as-CC + per-port settings + signing-URL fix) is **DONE** and committed.

Remaining phases — explicitly back-burnered by the user on 2026-05-07:

| Phase       | Scope                                                                                                                                                                                                                                                                                                                                                                         | Estimate   | Notes                                                                                                                                                            |
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Phase 2** | Webhook handler enhancement: cascading "your turn" emails, on-completion PDF distribution, token-based recipient matching, idempotency lock                                                                                                                                                                                                                                   | ~3–4h      | Schema columns already in place from Phase 1 (`document_signers.invited_at / opened_at / signing_token`, `documents.completion_cc_emails`).                      |
| **Phase 3** | Custom doc upload-to-Documenso: `custom-document-upload.service.ts` + `POST /api/v1/interests/[id]/upload-for-signing`                                                                                                                                                                                                                                                        | ~6–8h      | Depends on Phase 2 webhook UX in anger before locking the upload UX.                                                                                             |
| **Phase 4** | Field placement UI: react-pdf + dnd-kit overlay + auto-detect anchor scanner via pdfjs `getTextContent`                                                                                                                                                                                                                                                                       | ~10–14h    | Largest piece. Plan locked in build-plan Phase 4 — regexes, anchors, type-to-bbox sizing all spelled out. Best done in a focused session with the user watching. |
| **Phase 5** | Embedded signing URL emission verification: confirm website's `/sign/<type>/<token>` page handles every signer-role × documentType combination; update `signerMessages` map; apply nginx CORS block from integration audit                                                                                                                                                    | ~1–2h      |                                                                                                                                                                  |
| **Phase 6** | Polish: auto-send delay, audit-log additions, per-document customisation, document expiration, reminder rate-limit display, failed-webhook recovery UI                                                                                                                                                                                                                        | each ~2–3h | All deferred until Phases 1–4 ship.                                                                                                                              |
| **Phase 7** | Project Director RBAC — UI binding for the developer-user fields. Add "Linked to CRM user" dropdown in `/admin/documenso/page.tsx`; auto-fill name/email; webhook handler matches against linked user's email for in-CRM signing-status updates. Schema + setting keys (`documenso_developer_user_id`, `documenso_approver_user_id`, `_label`) already in place from Phase 1. | ~1h        | Smallest piece; could be picked off independently of Phase 2.                                                                                                    |
| **Risk #4** | v2 webhook payload audit against a live v2 instance (`payload.documentId` vs `payload.id`, `recipient.token` vs `recipient.recipientId`) before relying on Phase 2 cascading emails                                                                                                                                                                                           | ~1h        | Needs a live v2 instance.                                                                                                                                        |

---

## B. Custom-fields hardening

**Source:** [`docs/admin-ux-backlog.md`](./admin-ux-backlog.md) §7.

- ✅ **Merge tokens** — `{{custom.<fieldName>}}` validators + resolver shipped 2026-05-08. Tokens expand at template-render time for client/interest/berth contexts via `mergeCustomFieldValues` in `document-sends.service.ts`. Banner updated.
- **Search index** — DEFERRED as design limitation. Adding GIN coverage requires either joining `custom_field_values` per search (slow at scale) or materializing values into a search_text column on the parent (additive maintenance burden). The amber banner documents this.
- **Audit diff** — N/A. Custom-field values live in their own table, not as a JSONB blob on the parent entity. The `setValues()` service-layer call already creates its own audit log entry (custom-fields.service.ts:349-358), so changes ARE audited — just separately from the entity-diff.
- **UI surfacing of `{{custom.…}}` tokens in template-edit pickers** — Open. The token list dialog currently only shows static catalog tokens. Surface per-port custom-field definitions as a dynamic group under "Custom" so reps can browse them. Backend already accepts the tokens; this is a UI follow-up.

---

## C. Audit-final deferred items

**Source:** [`docs/audit-final-deferred.md`](./audit-final-deferred.md) — pre-merge + post-merge audit findings explicitly carried over.

The 2026-05-07 backlog sweep landed every small/concrete item. Remaining
entries are deferred because they need design decisions, live external
instances, or cross-cutting refactors:

### Deferred — Documenso-related (back-burnered until phases 2-7 land)

- **Documenso webhook does not enforce port_id on document lookups** — `src/app/api/webhooks/documenso/route.ts:96-148`. Bundle with Documenso Phase 2 (webhook handler enhancement) since they touch the same code.
- **Webhook dedup vs per-recipient signed events** — `src/app/api/webhooks/documenso/route.ts:103-110`. Replacing the body-hash dedup with a `(documensoDocumentId, recipientEmail, eventType)` composite unique requires a recipient_email column on `documentEvents`. Bundle with Phase 2.
- **v2 voidDocument endpoint shape verification** — `src/lib/services/documenso-client.ts:450-466`. Needs a live Documenso 2.x instance. Bundle with Phase 5.

### Deferred — pure refactor (no active bug)

- **Public POST routes bypass service layer** — `src/app/api/public/{interests,website-inquiries,residential-inquiries}/route.ts`. The audit's `userId: null as unknown as string` cast was already cleaned up to a proper `userId: null`. Remaining concern is testability: extract a shared `publicInterestService.create(...)`. Pure ergonomics — no active bug or security issue.

### Done in 2026-05-08 sweep (latest)

- ✅ Storage proxy port_id binding: `ProxyTokenPayload` gains optional `p` (port slug) claim; verifier asserts `key.startsWith(${p}/)`. document-sends 24h URLs opt in; other issuers continue working unchanged.
- ✅ system_settings index rebuilt with `NULLS NOT DISTINCT` (migration 0047) — global settings are now uniquely keyed by `key` alone. Surfaced + cleaned 65 duplicate `(storage_backend, NULL)` rows that had accumulated from race-prone delete-then-insert patterns.
- ✅ All 4 read-then-write systemSettings sites converted to true `onConflictDoUpdate` upserts (ocr-config, settings, residential-stages, ai-budget).
- ✅ Response shape standardization: 16 routes converted from `{ success: true }` → `204 No Content`. CLAUDE.md documents the convention.
- ✅ `req.json()` → `parseBody()` migration across 9 admin/CRM routes (custom-fields, expenses/export ×3, currency convert, search/recently-viewed, admin/duplicates, berths/pdf-{upload-url,versions,parse-results}). Portal-auth routes intentionally retained `{ success: true }`.
- ✅ Custom-field merge tokens: validator accepts `{{custom.<fieldName>}}` shape; resolver in `mergeCustomFieldValues` substitutes from per-port custom_field_definitions + per-entity values for client/interest/berth contexts. Banner updated.
- ✅ `/api/v1/files` accepts `companyId` and `yachtId` filters. uploadFile service writes both. file-upload-zone component accepts both props.
- ✅ Company Documents tab (CompanyFilesTab) re-enabled and added to company detail tabs.

### Done in 2026-05-07 sweep (commits in this session)

- ✅ Partial archived indexes (migration 0046) — `clients`, `interests`, `yachts`, `residential_clients`, `residential_interests`
- ✅ `document_sends` interestId port-verification helper
- ✅ Custom-fields per-entity permission gate (replaces hardcoded `clients.view/edit`)
- ✅ EOI Berth Range warn log (was already in place)
- ✅ v1 `placeFields` retry with backoff (was already in place)
- ✅ S3 bucket-exists check at boot (was already in place)
- ✅ Filesystem dev HMAC fallback warn (was already in place)
- ✅ Storage cache fingerprint documentation comment
- ✅ AI worker cost ledger writes (was already in place)
- ✅ Logger redact paths covering headers, encrypted blobs, two-level nesting (was already in place)
- ✅ `loadRecommenderSettings` accepts string `"true"`/`"false"` JSONB booleans
- ✅ `renderReceiptHeader` cursor math anchored to captured `baseY`
- ✅ Berth PDF apply: silent-drop logging for non-finite numeric coercions
- ✅ Saved-views: confirmed by-design owner-only (existing inline doc)
- ✅ Alerts ack/dismiss: confirmed by-design port-wide (service correctly bounded)
- ✅ Storage admin migration toasts (already in place)
- ✅ Invoice send/payment toasts + permission gates (already in place)
- ✅ Admin user list edit + remove gates (added remove gate)
- ✅ Email threads list skeleton + empty state (already in place)
- ✅ Scan page error state for OCR failures (already in place)
- ✅ Invoice detail typed (replaced `any` with `InvoiceDetailData` interface)
- ✅ All FK indexes called out in audit doc (already in place — audit was stale)
- ✅ `documentSends.sentByUserId` FK (already had `.references(...)`)

### Documented limitations (no action planned)

- **`berths.current_pdf_version_id` lacks Drizzle FK** — `src/lib/db/schema/berths.ts:83`. The in-line comment fully documents why (circular FK between `berths` ↔ `berth_pdf_versions` makes column-level `.references()` infeasible). FK is enforced via migration 0030. Revisit if Drizzle adds deferred-FK support.
- **`systemSettings` schema declares `uniqueIndex` instead of `NULLS NOT DISTINCT`** — Drizzle's `uniqueIndex` builder doesn't surface the flag. Migration 0047 is the source of truth; `db:push` against an empty DB would skip the flag. Same documented-limitation pattern as `berths.current_pdf_version_id`.
- **One remaining `req.json()` in admin/custom-fields/[fieldId]** — intentional. The handler inspects raw body to detect `fieldType` mutation attempts; parseBody would lose the raw view. Documented inline.

---

## D. Inline TODOs in code (2 remaining)

| File:line                                                                      | Note                                                            | Status                                                                                  |
| ------------------------------------------------------------------------------ | --------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| ~~`client-yachts-tab.tsx:93`~~                                                 | YachtForm preset owner prop                                     | ✅ landed 2026-05-07 (`initialOwner` prop)                                              |
| ~~`interest-form.tsx:329`~~                                                    | Include company-owned yachts where client is a member           | ✅ landed 2026-05-07 (`yachtOwnerFilter` array filter)                                  |
| ~~`interest-form.tsx:330`~~                                                    | "Add new yacht" inline shortcut                                 | ✅ landed 2026-05-07 (Plus button + YachtForm sheet)                                    |
| [`src/lib/queue/scheduler.ts:44`](../src/lib/queue/scheduler.ts#L44)           | Per-user reminder schedule (override on top of per-port digest) | Placeholder — per-port digest works; revisit when a customer asks for per-user override |
| [`src/lib/queue/workers/import.ts:13`](../src/lib/queue/workers/import.ts#L13) | CSV/Excel import worker — entire feature surface                | Placeholder — nothing currently enqueues `import` jobs (verified)                       |

---

## E. Hidden / stubbed UI tabs

- ✅ **Company Documents tab** — landed 2026-05-08. `/api/v1/files` accepts `companyId`+`yachtId` filters; CompanyFilesTab + uploadZone wired through the storage abstraction.
- **Berth Waiting List + Maintenance Log tabs** — `src/components/berths/berth-tabs.tsx:346`. Removed entirely; revisit if/when product asks.
- **Interest Contract / Reservation tabs** — `src/components/interests/interest-{contract,reservation}-tab.tsx`. Render a "coming soon" friendly card; the real flow is gated on Documenso Phases 2–6.

---

## F. Historical audit docs (mostly resolved)

These dossiers drove the audit-fix commit waves on 2026-05-05/06. Items
not surfaced in §C above were resolved via the `fix(audit): …` commits
(`588f8bc`, `94331bd`, `a8c6c07`, `5fc68a5`, `da7ede7`, `c5b41ca`,
`b4fb3b2`, `0f648a9`, `c312cd3`, `0a5f085`, `1a87f28`, `f3143d7`,
`05babe5`). Keep for historical context:

- [`audit-comprehensive-2026-05-05.md`](./audit-comprehensive-2026-05-05.md) — pre-merge audit (1 CRIT + 18 HIGH at start)
- [`audit-comprehensive-2026-05-06.md`](./audit-comprehensive-2026-05-06.md) — post-merge audit (1 CRIT + 7 HIGH + 10 MED + 7 LOW)
- [`audit-frontend-2026-05-06.md`](./audit-frontend-2026-05-06.md) — frontend-only sweep
- [`audit-missing-features-2026-05-06.md`](./audit-missing-features-2026-05-06.md) — admin-promised-but-unwired features (V1–V12)
- [`audit-permissions-2026-05-06.md`](./audit-permissions-2026-05-06.md) — permission-gate gaps
- [`audit-reliability-2026-05-06.md`](./audit-reliability-2026-05-06.md) — transactional integrity / TOCTOU
- [`berth-feature-handoff-prompt.md`](./berth-feature-handoff-prompt.md) — berth recommender handoff (shipped, kept as reference)
- [`berth-recommender-and-pdf-plan.md`](./berth-recommender-and-pdf-plan.md) — berth recommender + per-berth PDF plan (Phases 0–8 shipped)
- [`documenso-integration-audit.md`](./documenso-integration-audit.md) — Documenso integration spec (drives §A)
- [`website-refactor.md`](./website-refactor.md) — public website cutover plan
