Files
pn-new-crm/docs/launch-readiness.md
Matt cb8292464c feat(launch-readiness-batch): UAT drains, navigation refactor, launch infra, trackers
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>
2026-05-27 22:42:37 +02:00

27 KiB
Raw Blame History

Launch Readiness — Pre-Prod Initiative

Scope: the user enumerated five launch-blocking initiatives on 2026-05-27. This doc is the single home for all of them so we can track progress without losing items between sessions. Companion to docs/superpowers/audits/active-uat.md (which keeps the live UAT findings) and docs/BACKLOG.md (master backlog index).

Status tags per item: OPEN | IN PROGRESS | SHIPPED in <hash> | BLOCKED | DEFERRED.

Initiative 1 — Reports overhaul

Status: IN PROGRESS · Active phase

Goals (per user, 2026-05-27):

  • Cover all four report categories: Sales performance, Financial, Marketing / funnel, Operational.
  • Template system: load template → modify → re-save OR save as new.
  • Rich data density: more charts, more graphs, more KPIs.
  • Output formats: PDF + CSV + Excel for each report.
  • Scheduled reports: cron-driven; auto-email is optional (so the admin can schedule a run without forcing an email blast).
  • Custom builder: full ad-hoc (pick entity, columns, filters, group-by), save as template — but quality-first; we don't ship a janky composer.
  • UI/UX: stunning, fluid, beautiful. Within the existing white/navy brand language — no off-brand experimental themes.

Decisions locked (2026-05-27):

  • Currency: port branding default
  • Rep visibility: port-scoped admin setting (default depends on team size; PN is single-rep so default = full team)
  • AR aging buckets: standard 30-day (current / 1-30 / 31-60 / 61-90 / 90+)
  • Custom builder entity scope: all 10 entities
  • Pulse data: fold into Sales report
  • Inquiry-link audit: yes, audit + fix; no website-repo edits required for the audit itself (link logic is server-side)
  • Scope cut for launch: Sales + Operational ship first as fully-functional reports; Marketing + Financial ship in tandem with their data sources being wired (see Initiatives 2c + 2d below).

Phases (status snapshot 2026-05-27):

  1. Foundation + UX overhaul — landing page (within existing design system); charts library audit done; ExcelJS installed.
  2. Sales Performance + Operational builders — full report pages with KPIs / charts / tables; client-side Export to CSV + Excel + PDF; server-side PDF endpoint for branded output. See gaps below.
  3. Marketing report — NOT BUILT. Pending Init 1b cutover.
  4. Financial report — NOT BUILT. Pending Init 1c decision on whether to enable the invoices module (currently default OFF).
  5. ⚠️ Custom (ad-hoc) report builder — partial ship.
  6. Scheduled reports with optional emailing — BullMQ poll + render path live; recipients optional; PDF-only output.
  7. Templates — load / modify / save / save-as / URL deep-link.

Open considerations carried forward:

  • Chart library mix. Project already has recharts (simple bar/line/pie) and echarts (heatmaps, funnels, complex). Lean on each where it fits; don't add a third unless something specific is missing.
  • PDF cover-page treatment. Each report PDF should open with a branded cover (port logo, title, date range, generated-on stamp). Reuse the existing branded-document.tsx shell.

Working spec: docs/reports-content-spec.md (per-category KPIs + charts + tables proposed; updated as we walk through each).

Reports — what's left (gap audit 2026-05-27)

Comparing the working spec against shipped code, here's the bucketed backlog. Items marked LAUNCH-BLOCK are needed for the beta cutover; everything else is post-launch polish unless promoted.

Cross-cutting capabilities (apply to every report)

  • Period comparison toggle — "this period vs prior period" delta arrows on KPI cards. Spec calls for it on every report. Not on any.
  • Rep multi-select filter — exists implicitly via the single-rep leaderboard collapse, but no explicit multi-select dropdown.
  • Source multi-select filter — Sales has lead-category + outcome filters; the spec also calls for a generic source filter (website / referral / broker / manual) on every report.
  • Empty-state copy per report — currently shows a skeleton; spec wants a "this report needs data first" hint pointing at the right onboarding step.

Phase 2 — Sales report gaps

  • Operational-style filter set on Sales — beyond stage / lead-cat / outcome that shipped, the cross-cutting filters above (period comparison, rep multi-select, source multi-select) are missing.

Phase 2 — Operational report gaps

  • Operational-specific filters: berth area · tenure type · document type · status filter. None of the four exist. The spec calls these out as drill-down affordances for the heatmap + tables.

Phase 3 — Marketing report (LAUNCH-BLOCK if Marketing is in beta scope)

Not built. Spec at docs/reports-content-spec.md § Report 03 calls for:

  • 6 KPIs (inquiries, inquiry→interest %, inquiry→EOI %, inquiry→won %, top source, avg time-to-respond)
  • 6 charts (inquiries by source donut, source ROI stacked bar, full funnel, conversion trend, country geo map via react-simple-maps, time-to-respond histogram)
  • 3 tables (top-converting sources, recent inquiries, stuck inquiries)
  • Filters: specific source, mooring, UTM campaign

Blocker: depends on the website actually sending UTM params (Init 1b step 4 — CRM-side shipped, website-side pending) AND on inquiry data flowing from the new intake endpoint (Init 1b step 1 — pending website env flip).

Phase 4 — Financial report (LAUNCH-BLOCK if Financial is in beta scope)

Not built. Spec at docs/reports-content-spec.md § Report 02 calls for:

  • 7 KPIs (revenue collected, pipeline value, deposits, outstanding AR, overdue AR, expenses, net contribution)
  • 6 charts (revenue by month stacked, quarterly/yearly toggle, EOI → Deposit → Contract funnel, AR aging, cash flow line, expense breakdown donut)
  • 4 tables (outstanding invoices, recent payments, refund/write-off log, expense ledger)
  • Filters: invoice kind, payment status, currency, billing entity type

Blocker: depends on the invoices module being in use. Per Init 1c spike, the module is default OFF and the canonical money path is the per-interest Payments tab. Decision needed: ship Financial with data from payments only (no invoice surface) OR flip invoices module on for PN + train rep + ship Financial. Today the report would be 90% empty.

Phase 5 — Custom builder gaps

v1 ships 4 entities; full spec wants 10 + advanced composition.

  • Missing entities: yachts, companies, invoices, expenses, documents, websiteSubmissions, payments. Each is a registry-only extension — add a CustomEntityDefinition to src/lib/reports/custom/registry.ts. ~30 min per entity.
  • Filters beyond date range — spec wants per-column filter rows (column → operator → value, AND/OR between rows). Today only the date range filter exists.
  • Group by + aggregate — single group-by dimension + per-column aggregate (count / sum / avg / min / max). Today only a flat list.
  • Column sort — pick a column + direction. Today rows return with the registry's hardcoded orderBy.
  • Live preview as you build — spec wants debounced re-render on filter / column change. Today the rep clicks "Run query" to fetch.
  • Column whitelist per role — PII columns (email, phone) should be gated by clients.view_pii. Today all listed columns are available to anyone with reports.export.
  • Run-once vs Save-as-template — the spec asks for three buttons on save (Run once / Save as template / Update existing). Today only the template-save path exists.

Phase 6 — Scheduled runs gaps

  • Custom cron strings — three hardcoded cadences (weekly Mon 9 · monthly 1st 9 · quarterly 1st 9). Spec implies arbitrary cron. nextRunFor in report-schedules.service.ts switches on the enum; extend to support a cron_expression mode.
  • Scheduled CSV / XLSX — only PDF is wired through the worker (renderStandaloneReportRun in report-render.service.ts). For CSV/XLSX, the worker would need to either run the existing client-side exporter server-side (drop ExcelJS into the worker bundle) or build format-specific server renderers.

Phase 7 — Templates gaps

  • "Modified ●" indicator — when the rep changes view state after loading a template, the active-template badge currently just clears. Spec wants a visible "modified" marker so they know they've drifted.
  • Personal vs port-wide scope — schema has the visibility column with 'private' | 'team' but the UI always saves as port-wide. The Save dialog needs a scope picker.
  • "Owned by" attribution — templates with visibility='team' should show creator name. Schema captures createdBy; UI doesn't surface it.
  • Promote-to-port-wide affordance — once shipped, a "Share with team" action on personal templates that flips visibility.

Net launch-readiness for reports

If the launch scope is Sales + Operational only, reports are launch-ready with the polish items above as post-launch follow-ups.

If the launch scope includes Marketing + Financial, both reports need to be built AND their data plumbing finished (Init 1b website flip + UTM forwarding for Marketing; invoices module + rep training for Financial).

The cross-cutting filter set (period comparison, rep / source multi-select, empty-state copy) is the highest-value polish that's visible on every report — call it ~6-8 hours of work spread across both shipped report pages + the shared FilterBar component.


Initiative 1b — Marketing data pipeline cutover

Status: OPEN · Blocks the Marketing report

The CRM has the full infrastructure for marketing intake + attribution; it's just not connected end-to-end.

What's built:

  • Email-open pixel tracking: src/app/api/public/email-pixel/[sendId]/route.ts
    • src/lib/email/tracking-pixel.ts. Sales sends with trackOpens=true get a 1×1 pixel; opens record to email_send_opens and cross-post to Umami.
  • Umami integration: @umami/node installed; src/lib/services/umami.service.ts is the wrapper. Outcome events (EOI sent, deposit received, etc.) already cross-post into Umami.
  • Website inquiry intake endpoint: /api/public/website-inquiries in the CRM, paired with /api/public/residential-inquiries. Both validate + dual-write into website_submissions.
  • Website posting code: Port Nimara/Website/server/utils/crmIntake.ts:72 has the matching POST. Just needs the env var to point at the new CRM.

What's NOT connected yet:

  1. Website env CRM_INTAKE_URL still points at the old portal (or isn't set). Flipping this is a ~5-min config change inside the website Nuxt deploy. After flip, every website inquiry lands in website_submissions + auto-routes to the inquiry-triage queue.
  2. Backfill of historical inquiries from the old portal so the Marketing report has launch-day history rather than starting from zero. Reads from client_portal_v2's inquiry table, inserts into website_submissions with original receivedAt timestamps, re-links to existing CRM clients via dedup (email/phone).
  3. Umami funnel events on the marketing site itself. The Umami project exists; what's unclear is whether the marketing site is firing event: calls on key actions (form submitted, brochure downloaded, virtual-tour started). Audit needed.
  4. UTM column wiring. CRM-side SHIPPED — migration 0089_website_submissions_utm.sql adds utm_source / utm_medium / utm_campaign / utm_term / utm_content to website_submissions plus a (port_id, utm_source, received_at) composite index for per-campaign rollups. /api/public/website-inquiries accepts the five fields in the request body and persists them on insert. Pending website-side change: the marketing site's crmIntake.ts POST must forward UTM params from the form's query string / cookies. Pending residential parity: residential inquiries (/api/public/residential-inquiries) don't go through website_submissions; if Marketing report needs UTM attribution on residential leads too, add the same columns to residential_clients in a follow-up.

Sequencing:

  • Step 1 is the cutover unblock (do during launch window itself).
  • Step 2 is part of Initiative 5 (data migration).
  • Step 3 is a website-side audit (Initiative 3).
  • Step 4 is a small CRM-side schema add (one migration + 4 column reads). Decision pending: ship at launch or defer to Phase 2.

Initiative 1c — Invoicing audit-and-finish

Status: SPIKE COMPLETE · Module-toggle shipped · Financial report deferred

Audit findings (2026-05-27 spike)

The CRM has two parallel money-receiving flows in active code:

  1. payments table — canonical, in active use. Schema comment at src/lib/db/schema/pipeline.ts:75 is unambiguous: "The CRM does NOT generate invoices — clients pay banks directly. We record that money was received." Linked to interests. recordPayment auto-advances pipeline to deposit_paid when the cumulative deposit total hits depositExpectedAmount. This is the surface reps actually use; payments are recorded from the per-interest Payments tab.
  2. invoices + invoice_line_items table — orphaned in the UI. Full builder (line items, PDF, send, mark-paid) exists at /[portSlug]/invoices/new. The sidebar nav entry was removed earlier; only the page itself can link to invoices/new. Dev DB has zero rows. The standalone surface is parallel infrastructure for the rare case where an operator wants to invoice a client directly from the CRM, plus the employee-expense-report flow (expenses → invoices PDF).

Decision (per the existing "intentionally manual elsewhere" branch)

Ship a port-level module toggle, default OFF, identical pattern to the Tenancies and Expenses toggles. The Financial report stays deferred from launch since the canonical Payments tab feeds the Sales report (which is shipping) — separate Financial dashboard adds no value when there's no second money-receiving flow.

What shipped (2026-05-27):

  • system_settings registry entry invoices_module_enabled (boolean, port-scoped, default false) — added to src/lib/settings/registry.ts.
  • New module-gate service src/lib/services/invoices-module.service.ts with isInvoicesModuleEnabled(portId) (same shape as isExpensesModuleEnabled).
  • Layout-level guard at src/app/(dashboard)/[portSlug]/invoices/layout.tsx — every /invoices/* route renders <ModuleDisabledPage> when the port hasn't opted in. Admins can flip on from Admin → Settings; historical rows preserved.

What's NOT changed:

  • API endpoints (/api/v1/invoices/*) still respond — historical PDF links + send-flow webhooks keep resolving regardless of the toggle.
  • The payments flow is untouched and continues to be the canonical money-received path.
  • The expense → invoice flow (employee expense reports) is unaffected since employee-expense PDFs flow through a different surface (/expenses) that lives behind its own module gate.

Follow-up: if the user later wants per-port branded client-facing invoicing from inside the CRM, the surface is ready to turn on with no schema work — just flip invoices_module_enabled = true.


Initiative 2 — Multi-agent codebase audit

Status: OPEN · Awaiting kickoff

User ask: "deep, multi-agent audit of all routes, naming, text, UX, and … dig through the entire code of everything in the system (especially related to the sales process) and find any issues in the logic or how the functionality interacts with each other, how data is shared and persists where needed. Also a deep security audit."

Audit dimensions (use one specialised agent per dimension, in parallel):

# Dimension Specialised agent Output
1 Sales pipeline logic feature-dev:code-explorer Trace every stage transition; verify auto-advance rules, EOI gating, deposit handling, contract signing. Look for stale enum references (the 9→7 stage migration left some bugs).
2 Cross-entity data flow feature-dev:code-explorer Map polymorphic ownership (yacht/company), interest_berths (multi-berth), document folders (aggregated projection), notes (4-table dispatch). Find divergence between docs and code.
3 Security security-review (existing skill) OWASP API Top 10, auth bypass, IDOR, injection, secret leakage, GDPR exposure. Multi-tenant boundary checks (port_id at every join).
4 API surface consistency code-review:code-review { data: T } envelope adherence, errorResponse(error) usage, parseBody(req, schema) usage, 204 vs JSON, withAuth+withPermission composition.
5 UI/UX consistency frontend-design:frontend-design review Visual inconsistencies, copy/text issues, accessibility, mobile parity, brand drift, em-dashes, generic SaaS slop.
6 Schema vs code divergence feature-dev:code-explorer Migrations vs Drizzle schema files vs service helpers — find any column the DB has that no service touches, or any service field with no migration.
7 Documenso integration feature-dev:code-explorer Full v1↔v2 path coverage, webhook idempotency, template field mapping, EOI generation (both pathways), error recovery.
8 Storage & file lifecycle feature-dev:code-explorer S3↔filesystem switching, file orphans, signed-URL expiry, GDPR export coverage, magic-byte validation everywhere.

Coordination:

  • Use a single coordinator session that fans out via Agent / TaskCreate with subagent_type set per dimension. Each agent writes findings to a per-dimension scratch file under docs/audits/2026-05-27/<dimension>.md, then the coordinator consolidates into a single triage doc with severity tags.
  • Pass model: "opus" on every agent spawn — Sonnet/Haiku context windows compact too fast under MCP baseline (per memory feedback_subagent_context_bloat).

Output: docs/audits/2026-05-27/findings-master.md with per-finding severity (CRITICAL | HIGH | MED | LOW), file:line refs, and recommended fix. Critical + High get fixed before launch.


Initiative 3 — Marketing website integration

Status: OPEN · Needs scope clarification

User ask: "make our relevant edits to the marketing website to prepare for the deployment and integration of our new system."

The marketing site lives in /Users/matt/Repos/Port Nimara/Website (separate Nuxt repo). Integration touch points the CRM exposes today:

  • /api/public/berths + /api/public/berths/[mooringNumber] — feeds the marketing site's berth list / detail. Status precedence Sold > Under Offer > Available is already wired.
  • /api/public/health — dual-mode health check; the website should call the authenticated variant (with WEBSITE_INTAKE_SECRET) on startup so it refuses to start when pointed at the wrong CRM env.
  • /api/public/website-inquiries — intake endpoint for the contact form; dual-writes inquiry into the CRM.
  • Inquiry email ownership — at cutover, inquiry emails move from the website to the CRM (per memory project_email_ownership_at_cutover). Templates + settings keys already exist; berth public endpoint + admin recipient UI still needed (per existing memory).
  • Cover photography + branding assets — the new system uses branding_email_background_url etc.; ensure the website assets match.

Open work (needs user input on priority):

  • Wire the website's contact form to /api/public/website-inquiries with the new payload shape.
  • Add the WEBSITE_INTAKE_SECRET to the website's env, point at the authenticated /api/public/health.
  • Update berth-detail page to consume the new /api/public/berths/... shape (the JSON mirrors the legacy NocoDB shape so this should be a no-op — VERIFY).
  • Replace any hard-coded "noreply@portnimara.com" sender on the website side with the CRM-controlled From address (so per-port branding wins).
  • Confirm the website's caching headers don't fight ours (s-maxage=300, stale-while-revalidate=60 on berth endpoints).

Initiative 4 — End-to-end testing

Status: OPEN · Needs scope clarification

User ask: "end to end testing of all sales functions, generating EOIs/documents (especially), ensuring all UX/UI is fluid, beautiful, relevant and helps the user go through the sales process effortlessly."

Existing infrastructure (per CLAUDE.md):

  • tests/e2e/smoke — fast click-through (~10 min, ~125 specs)
  • tests/e2e/exhaustive — deeper UI coverage
  • tests/e2e/destructive — archive/delete/cancel paths
  • tests/e2e/realapi — opt-in real Documenso + IMAP round-trip
  • tests/e2e/visual — pixel-diff baselines

Pre-launch test gaps to fill (proposed):

  1. End-to-end sales journey (single Playwright spec, real-API): new inquiry → qualified → EOI generated (Documenso) → client signs → developer countersigns → reservation → deposit recorded → contract generated → contract signed → tenancy auto-created → berth marked sold. Assert every stage transition + every email fires.
  2. EOI generation parity between both pathways (in-app fill-eoi-form vs Documenso template). Same EoiContext should produce equivalent PDFs.
  3. Multi-berth EOI rendering — berth range formatter assertion (A1-A3, B5-B7 from interest_berths).
  4. Documenso webhook idempotency — replay the same DOCUMENT_COMPLETED webhook three times; assert single files.folder_id write + no duplicate audit-log rows.
  5. Storage backend swap — switch port to filesystem, generate EOI, verify file lands; switch back to S3, confirm migrate script moves the blob correctly.
  6. Visual snapshot refresh for the new Reports UI + back-button smart-back changes (this conversation).
  7. Mobile parity for the entire sales journey (different Playwright project or --config variant).

Each gap above becomes one or two new spec files. Coordinate with Initiative 2's audit so we don't double-test.


Initiative 5 — Data migration (legacy → new)

Status: OPEN · High effort · Likely blocker for cutover

User ask: "start pulling all existing prod data from the old system and connected systems (we'll have to backfill the EOIs by pulling them through MinIO — it's a fucking mess so I'll really need your help automating/speeding up that process) and initiate a preliminary switch over."

Sources to drain:

Source Storage Entities Notes
Old NocoDB tables Postgres / NocoDB Clients, yachts, companies, interests, berths, EOIs (metadata) Already imported in earlier migration; verify currency vs prod NocoDB.
Old portal (client_portal_v2) Nuxt + Postgres Portal users, signing history, sent invitations Need to confirm what hasn't been migrated yet.
MinIO (legacy bucket) Object storage EOI PDFs (signed + unsigned), receipts, contracts The "fucking mess" — naming is inconsistent, organisation unclear, need to map each blob back to its CRM entity.
Documenso v1 (live) Documenso server In-flight signing envelopes + signed PDFs Migration question: do we cut new EOIs to v2 and let v1 envelopes finish, or migrate the in-flight?
Email archives IMAP / mail server Inquiry replies, signing reminders, deposit confirmations Probably out of scope for cutover (read-only history).

Migration script plan (write under scripts/migration/):

  1. probe-minio.ts — scan the legacy MinIO bucket, list every blob, try to extract a client / interest / berth identifier from filename patterns. Produce docs/migration/minio-blob-inventory.csv with key, size_bytes, mime, probable_entity_type, probable_entity_id, confidence.
  2. backfill-eoi-pdfs.ts — for each inventoried blob with confidence ≥ HIGH, copy from legacy MinIO into the new storage backend, create a matching files row + documents row, deposit into the right entity folder via the existing ensureEntityFolder helper. Idempotent via legacy_minio_key column (add via migration if missing).
  3. reconcile-nocodb.ts — diff the live NocoDB tables against our imported state; report rows added/changed/deleted since last import.
  4. preflight-cutover.sh — orchestrator script that runs the three above in order, writes a final report.

Cutover plan:

  1. Freeze writes on the old system (NocoDB read-only, portal maintenance page).
  2. Run preflight-cutover.sh against frozen sources.
  3. Manual reconciliation of probe-minio rows where confidence < HIGH (likely a few hundred blobs — the user explicitly flagged this is manual labour, automation helps but doesn't replace it).
  4. DNS / website pointer flip.
  5. Watch error_events for 24h; rollback plan = re-enable old system writes and stop the cutover commit.

Cross-initiative open questions

  • When to wrap the launch audit doc. I'd suggest: after Initiative 2's findings are triaged AND Initiatives 3-5 reach IN PROGRESS. At that point this file becomes the launch-day-runbook.
  • Who's the launch sponsor / decision-maker? Different from "user / matt"? Affects who signs off on cutover.
  • Soft launch vs hard cutover? Hard cutover is simpler operationally but risky; soft launch (parallel writes for a week) is safer but requires the old system to keep accepting writes for longer.