Adds a `residential_module_enabled` port setting (default ON) that
hides/disables the entire Residential surface when an admin turns it
off, mirroring the Tenancies / Invoices / Expenses module-toggle
pattern. Disabling is a soft hide — residential clients/interests are
preserved and reappear on re-enable.
Surfaces gated:
- Route guard: new residential/layout.tsx renders ModuleDisabledPage
(covers all 5 residential pages)
- Sidebar "Residential" section + mobile more-sheet tile (SSR-resolved
residentialModuleByPort threaded layout → app-shell → sidebar)
- Global search: residential client/interest buckets early-return at
the shared chokepoint so disabled-port records don't dead-end
- Public intake: /api/public/residential-inquiries 404s when off
- Admin Switch in settings-manager (writes via settings PUT)
Service TDD'd (residential-module.test.ts, 6 tests) plus a
disabled-port rejection test on the public endpoint. tsc + lint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bundles the rest of the in-flight work from this UAT round into one
checkpoint. Each sub-area is independent; see the headings below.
UAT polish (drained 11 findings from active-uat.md):
- Dialog primitive default bumped sm:max-w-xl/lg:max-w-3xl →
sm:max-w-2xl/lg:max-w-4xl so multi-field forms + PDF previews
aren't cramped at 1440-1920px.
- Notes tab badge aggregation: new countFor{Client,Yacht,Company}
Aggregated helpers in notes.service mirror the listFor*Aggregated
symmetric-reach joins. yacht-tabs + company-tabs render the
badge; client-tabs already had badge support.
- Supplemental-info form polish bundle: BrandedAuthShell gains a
`width: 'sm' | 'md'` prop (md uses min-h-dvh scroll instead of
fixed inset-0 pin so long forms scroll naturally). Form picks up
port branding (logoUrl + backgroundUrl + appName) via
loadByToken. Address fields completed (street + city + region +
postal + country). Port name eyebrow + success-state copy added.
- new-document-menu Upload-file landing toast: per-file completion
emits toast.success with action link to the destination entity
or folder.
- interest-tabs OverviewTab "from client" pill on Email + Phone
rows via new EditableRow `inheritedFrom` prop.
- create-document-wizard subject picker → segmented button strip
(5 types visible at once).
Launch infra:
- UTM column wiring (Init 1b step 4): migration
0089_website_submissions_utm.sql adds utm_source/medium/campaign/
term/content + composite index (port_id, utm_source, received_at)
for per-campaign rollups. website-inquiries intake accepts the
five fields. Residential intake intentionally untouched per audit
scope.
- Invoicing module gate (Init 1c spike): new
invoices-module.service + invoices layout guard + registry entry
invoices_module_enabled (default false). Audit conclusion in
launch-readiness.md: payments table is canonical money path;
/invoices flow is parallel infrastructure now hidden by default.
Smart-back navigation refactor:
- Replaced breadcrumb component with history-aware Back button.
New route-labels.ts + use-smart-back hook +
navigation-history-tracker so back falls through to the parent
route when there's no prior page in history.
- Sidebar / topbar / mobile-topbar adopt the new pattern; old
breadcrumb-store kept for back-compat consumers but the
breadcrumbs component is gone.
- 6 detail pages (admin/errors per-id + codes, invoices/
upload-receipts, reports kind, tenancies detail, analytics
metric, client detail) migrated.
Trackers + docs:
- docs/launch-readiness.md — master pre-launch tracker. Includes
the reports gap audit (cross-cutting filter set, Marketing +
Financial blockers, custom builder remaining entities, scheduled
CSV/XLSX, template scope picker).
- docs/superpowers/audits/active-uat.md — 15 findings flipped
OPEN → SHIPPED locally with fix-applied notes; 4 OPEN remaining
(each blocked on user input or cross-repo).
- CLAUDE.md — minor session notes carried forward.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 1 of the comprehensive Documenso upload audit per the
2026-05-26 locked-decisions block in docs/superpowers/audits/active-uat.md.
P1.1 — persist documensoId immediately after create
Was set only at the late `status: 'sent'` commit. Any throw between
documensoCreate and the late update left an orphaned Documenso
envelope the CRM had no link to. Now the UPDATE runs right after
documensoCreate succeeds; rollback paths can find and void the
envelope.
P1.2 — pre-flight validation hard-blocks Submit
UploadForSigningDialog computes a submissionErrors memo over
recipients + fields. Submit button disabled when errors > 0. Inline
amber summary lists every issue (missing email, invalid email,
missing name, field assigned to non-existent recipient, no fields
placed). Service layer mirrors the same email + name checks so
direct API hits reject early. No override path per locked decision.
P1.3 — cancel/delete affordance audit + sweep
Document-list per-row Delete + Send for Signing actions now:
- Wrapped in PermissionGate (documents.delete + send_for_signing).
- Surface toast on success + toastError on failure (were silently
swallowing errors).
- Use a broader predicate-based query invalidation so every doc
list across the app refreshes, not just the local key.
EOI tab Regenerate + Cancel EOI buttons + reservation/contract
tab Cancel buttons wrapped in PermissionGate (documents.edit, the
cancel route's auth check).
P1.4 — Documenso webhook URL auto-PATCH (env-gated)
scripts/update-documenso-webhook.ts written. Reads
DEV_AUTO_UPDATE_DOCUMENSO_WEBHOOK env flag (when 1, runs; otherwise
no-op). Lists every webhook on the Documenso instance via v2 (with
v1 fallback), identifies webhooks pointing at trycloudflare.com
hosts OR /api/webhooks/documenso paths, PATCHes them to the new
tunnel URL. scripts/tunnel-url.sh chains the script after the URL
print so a re-tunnel auto-rotates the webhook (when flag set).
P1.5 — state-machine refactor with rollbackTo() helper
custom-document-upload.service.ts:
- Single try around create → send → place steps.
- state.step tracks which step is current; state.documensoDocId
records the envelope id once we have it.
- rollbackTo(reason) composes the recovery: status='cancelled' on
the CRM row, documensoVoidSafe on the envelope when applicable.
Idempotent — calling twice is safe.
- Removes three independent try/catches.
P1.6 — recipient ↔ Documenso identity reconciliation
After documensoSend, validates every distinct email we sent
appears in sentDoc.recipients. If Documenso silently dropped one,
a ConflictError fires before field placement so the rollback path
triggers. Explicit message names the missing emails for the rep.
P1.7 — vitest extension + per-failure audit-log entries
- 5 new vitest cases (blank email, whitespace email, malformed
email, blank name, duplicate-emails-OK semantic).
- rollbackTo writes a structured audit_log entry with failedStep,
documensoEnvelopeId, errorClass, errorMessage. Post-mortem
investigation has structured data instead of just logger lines.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User answered 11 blocking + clarifying questions across the audit doc.
Decisions inlined as a summary block in the audit doc prelude so any
session reading the doc sees the answers up front before drilling into
individual findings.
Highlights:
- Documenso comprehensive audit ships as 5 discrete sub-PRs.
- Pre-flight validation hard-blocks Submit; no override path.
- `/documents/new` wizard refactor: delete upload branch, drop inapp
pathway, per-port doc-type template defaults, surface flow 3 from
dropdown, drop the route entirely.
- Automate Signing: pick-up on mid-flow enable; broadcast to all
recipients; single combined mode; manual override stays visible.
- Webhook URL auto-PATCH env-flag-gated.
- documenso_signing_order becomes a tri-state setting.
- OverviewTab inheritance writes to interest, prompt to also update
yacht record.
- Public-map flag inheritance applies across every map-flip dialog.
- Cancel/Delete affordance audit sweeps EVERY remove route.
- Orphan-scan script deferred; dev DB nuke acceptable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Every UAT finding the user surfaces during a live walkthrough now lands
in docs/superpowers/audits/active-uat.md regardless of which session
captured it. Persists across sessions until the user explicitly says to
wrap a round and archive (rename to YYYY-MM-DD-uat.md, start fresh).
CLAUDE.md's "Manual UAT" guidance updated to point at the new file +
documents the status-tag taxonomy and the append-protocol detail level
(file:line, React-grab anchor, root cause, fix proposal walking each
layer, effort estimate, alternatives + rejection reasons, open
questions, bundle-with notes, cross-refs, acceptance criteria). Historical
alpha-uat-master.md retained as the previous master through 2026-05-26.
This commit seeds the doc with the full body of findings captured during
this live session — Documenso reliability work, dialog width sweep,
recipient UX, recommender card polish, tenancy + notes plumbing, the
larger Documenso upload audit and Automate Signing feature specs. Each
entry follows the detail contract documented in the file footer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>