Skip to Content

← All archived intake epics

Intake: demo-tenant

breakdown.md

Breakdown: Demo tenant on the platform

  • scope-slug: demo-tenant · doc: _source/report.md (David's demo-environment recommendation, committed with this cut — there is no Google Doc or scope.md behind it)
  • initiative: Refine the bridge / objective: Q2-2026 Objective 1 — Establish Product-Market Fit with Vendor Partners
  • personas: Admin, CSM, SDM, Expert, Vendor, Customer

What I understood

The client-facing demo is built as a demo tenant on platform.sustentus.comapps/web + packages/services — not as a standalone app on a second database. The platform already is the demo minus data and a friction-free way in: six real persona dashboards, the 11 vendor analytics deep-dives, real streaming onboarding/BRD agents, the full lead→proposal→bid lifecycle, the demo-data seeder and the admin-gated view-as switcher. This batch supplies what's missing: a provisioned, seeded, isDemo-flagged tenant with one user per persona; a client-credible storyline the seeder can reproduce; presenter-grade persona switching over the existing view-as; a purge-and-reseed reset (manual + nightly); canned deterministic mode for the LLM agents; and a scope/licence visualisation over the RBAC engine. Every item hardens the product we sell — nothing is throwaway, and the previously cut standalone-Postgres batch (demo-first) is deleted in the same PR as this cut.

Decisions taken at the cut

Resolved with Jamie on 2026-08-11 (the report's open choices, plus cut-shape decisions):

  • Demo-tenant architecture confirmed — David's recommendation adopted wholesale; the standalone Neon/Postgres/Drizzle demo (demo-first intake) is retired and deleted, not kept as a fallback.
  • Presenter-led access — a real Clerk admin user for the demo tenant signs in and drives the existing view-as switcher. No auth bypass anywhere: the no-login public launcher was considered and explicitly not cut this round (a later candidate once presenter-led demos prove out).
  • The demo tenant lives in production — one deployment, one MongoDB, demo tenant alongside future real tenants. The currently dead ITenant.isDemo flag gets wired as the guardrail: destructive demo operations (purge/reseed) hard-refuse any tenant not flagged.
  • Reset is manual + nightly — an admin-triggered reset for between client sessions plus a scheduled nightly reset so the tenant always opens clean. The seeder is append-only today, so the purge half is new build, not reuse.
  • Optionals: E and G are in, F is out — canned LLM mode (E) and scope-bar/licence visuals (G) are cut as stubs 5–6; the "generate example leads" narrative screen (F) is recorded out of scope this round.
  • The production Mongo→Postgres question stays unbundled, per the report's closing section — nothing in this batch weighs that decision.

Where it sits

The demo tenant demonstrates every service-journey step and feature-role-matrix entity, so the batch is horizontal in effect but narrow in code: it lands on the admin settings surface (demo-data, reset), the tenant model and its guard rails (isDemo), the view-as layer (presenter switching), the cron/ops seam (nightly reset), the onboarding/BRD AI agents (canned mode), and the RBAC/route-policy engine (scope visuals). All six business/roles personas are represented by one seeded user each.

Build order

  1. demo-tenant-foundation — wire isDemo + provision the tenant + one seeded user per persona + first seed run (report A+B) — depends-on: none
  2. demo-seed-storyline — client-credible fictional storyline; fill seed gaps (CSAT) so every headline surface renders populated — depends-on: demo-tenant-foundation
  3. demo-presenter-switching — presenter-grade persona quick-switch over the existing view-as, gated on isDemo (report C) — depends-on: demo-tenant-foundation
  4. demo-reset-ops — purge-and-reseed reset service, admin trigger + nightly cron, session runbook, docs sync (report D) — depends-on: demo-seed-storyline
  5. demo-canned-agents — deterministic canned mode for the onboarding + BRD streaming agents on the demo tenant (report E) — depends-on: demo-tenant-foundation
  6. demo-scope-visuals — scope-bar / licence-report visualisation derived from the RBAC engine (report G) — depends-on: none

Parallelizable

Stub 1 unblocks everything demo-tenant-shaped. Once it lands, stubs 2, 3 and 5 are mutually independent and fully parallel. Stub 4 is the only join on that side: the reset must reproduce the approved storyline, so it waits for 2. Stub 6 touches only the RBAC/route-policy surface and depends on nothing — it is sequenced last by the foundation-first-then-impact tie-break (the demo cannot run without 1–4; it can run without 6). Linear order otherwise follows the presenter's critical path: a populated world (2), a way to walk it (3), a way to keep it clean (4), then the wow-moment hardeners (5, 6).

Out of scope (whole scope)

  • No standalone demo stack: no Neon, no Postgres, no Drizzle, no second database — the demo-first batch is deleted, not deferred.
  • No changes to apps/demo — it remains the pipeline Design stage's prototyping surface only.
  • No auth bypass and no public no-login URL — every demo session is behind Clerk; the relaxed launcher is a possible later batch, not this one.
  • No behaviour change for real (non-demo) tenants — every demo affordance is gated on isDemo: true, and nothing new renders or runs for tenants without it.
  • No what-if simulator — the report retires that myth; a seeded tenant with leads at every stage replaces the single-lead stage-rewind trick.
  • F — the "generate example leads" source-narrative screen — considered, not cut this round.
  • No licence enforcement or billing — scope/licence visuals (stub 6) are read-only reporting.
  • No real prospect/client data anywhere in the demo tenant — fictional only, ever.
  • The production MongoDB→Postgres architecture question — explicitly a separate decision.

Context budget: this cut is report-based (no scope.md); reuse claims in the report were verified against source via one scoped research pass (seeder, view-as, tenancy, auth, isDemo) — findings are cited in the stubs' Notes for Define.

_done/demo-canned-agents.md

Stub: Canned agent mode — deterministic onboarding + BRD for live walkthroughs

  • feature-slug: demo-canned-agents
  • scope: demo-tenant
  • personas: Vendor, Customer
  • initiative: Refine the bridge / objective: Q2-2026 Objective 1 — Establish Product-Market Fit with Vendor Partners
  • depends-on: demo-tenant-foundation
  • sequence: 5 of 6

Problem

Onboarding and BRD/project discovery are the platform's live streaming-LLM wow-moments — and the most fragile part of a scripted walkthrough: latency spikes, nondeterministic wording, or a provider incident mid-pitch. The old apps/demo handled this with canned recordings of these same agents; the platform demo needs an equivalent without forking the features (report E).

Proposed change

A canned, deterministic replay mode for the onboarding and BRD agents, available only on the demo tenant:

  • Recorded transcripts for each flow, replayed through the same streaming UI (useChat + streaming route) so the on-screen experience — token streaming, tool approvals, generated artifacts — is visually indistinguishable from live.
  • Gated on isDemo: real tenants can never hit canned mode; the code path is inert for isDemo: false.
  • Presenter's choice: on the demo tenant the presenter can still run the real LLM path when they want to show it live — canned is the default safety net, not a lock-in.

Acceptance criteria (rough)

  • With canned mode active, the onboarding and BRD flows stream deterministic content with no external LLM call and no dependence on provider latency.
  • The canned experience is visually and interactively equivalent to live mode (streaming cadence, approval steps, artifact output).
  • Tenants with isDemo: false cannot reach canned mode by any input.
  • The presenter can switch a demo-tenant session to the live LLM path.

Out of scope (this feature)

  • Canning any other AI feature (matching, success, recruiter agents) — onboarding + BRD only.
  • Any behaviour change to the live agents for real tenants.
  • New demo content surfaces — this hardens two existing flows.

Notes for Define

Covers report punch-list E (cut decision 2026-08-11: in scope). The ai-feature skill governs how agents are built here (Vercel AI SDK v6 via the AI Gateway, ToolLoopAgent, streaming route + useChat, sanitized TipTap-safe HTML) — read it before designing the replay seam; the AI SDK's mock/test providers may give the replay layer for free at the provider boundary, which would keep the UI and routes untouched. Define chooses the recording format and where transcripts live (fixtures in-repo vs seeded documents), and how "canned vs live" is selected by the presenter (per-session toggle vs per-run choice). Transcripts are storyline content — fictional only, consistent with the stub-2 world, and worth Paul's eyes at sign-off. touches: guess — packages/services/src/ai/**, the onboarding/BRD streaming route(s) and components in apps/web.

_done/demo-presenter-switching.md

Stub: Presenter persona switching — demo-grade view-as, admin gate untouched

  • feature-slug: demo-presenter-switching
  • scope: demo-tenant
  • personas: Admin
  • initiative: Refine the bridge / objective: Q2-2026 Objective 1 — Establish Product-Market Fit with Vendor Partners
  • depends-on: demo-tenant-foundation
  • sequence: 3 of 6

Problem

view-as is built for admin permission-testing, not a client walkthrough: the switcher sits in the sidebar among admin chrome, bare role emulation deliberately renders own-scoped views empty, and moving between six personas mid-pitch takes too many deliberate steps. The report calls friction-free persona switching "the one genuine gap" (report C); the cut resolved it as presenter-led — a real admin drives the existing switcher, and no auth is bypassed.

Proposed change

An isDemo-gated presenter layer over the existing view-as machinery:

  • Curated quick-switch: on the demo tenant, the switcher pins the six storyline persona users by name (faithful user emulation, never bare-role) so the presenter moves admin → any persona → any persona in a click or two.
  • Clean landings: each switch lands on that persona's ROLE_HOME with the storyline world populated; the view-as banner reads demo-appropriately rather than as an internal audit tool.
  • Gate untouched: the admin-only, same-org resolveViewAs authority and its three enforcement points are reused as-is; nothing new renders for isDemo: false tenants.

Acceptance criteria (rough)

  • On the demo tenant, an admin presenter switches between any of the six storyline personas by name in ≤2 clicks, landing on the persona's populated dashboard.
  • All demo switching uses user-emulation — no empty own-scoped views during a walkthrough.
  • Tenants with isDemo: false see exactly today's view-as UI and behaviour.
  • Every switch still writes the view-as audit trail; a non-admin (or forged cookie) still cannot enter view-as anywhere.

Out of scope (this feature)

  • The no-login public launcher — explicitly not this batch (cut decision); do not add any bypass in proxy.ts or the route policies.
  • Weakening resolveViewAs, the admin gate, or the same-org check in any way.
  • Storyline content — persona names come from demo-seed-storyline.

Notes for Define

Covers report punch-list C in its presenter-led shape (cut decision 2026-08-11 — Define does not reopen the no-login option). Verified against source: cookie authority resolveViewAs at apps/web/lib/auth.ts:122, re-enforced at apps/web/proxy.ts:88–95 and apps/web/lib/resolve-app-user-for-tenant.ts:30–53 — reuse, don't touch; switcher UI at apps/web/components/view-as/view-as-switcher.tsx (mounted from the app layout) with enterViewAs / exitViewAs server actions in apps/web/lib/actions/view-as.ts (Zod discriminated union kind: "user" | "role"); the proxy already skips onboarding redirects while emulating. The demo affordance is a presentation of existing capability — expect the diff to be mostly components/view-as/** plus the switcher's data source, keyed off the tenant's isDemo. touches: guess — apps/web/components/view-as/**, apps/web/lib/view-as.ts, apps/web/lib/actions/view-as.ts.

_done/demo-reset-ops.md

Stub: Demo reset — purge-and-reseed, manual trigger + nightly cron, docs brought to truth

  • feature-slug: demo-reset-ops
  • scope: demo-tenant
  • personas: Admin
  • initiative: Refine the bridge / objective: Q2-2026 Objective 1 — Establish Product-Market Fit with Vendor Partners
  • depends-on: demo-seed-storyline
  • sequence: 4 of 6

Problem

The seeder is append-only — its own doc comment says re-running "appends a fresh batch" — so every demo session and every re-seed accumulates leads, and there is no way back to the opening storyline. The report's item D assumes "seeder (re-runnable)" covers reset; verified against source, it does not: the purge half does not exist. Meanwhile the docs site's demo-environment page describes snapshot/reset machinery that was never built, which will mislead any agent or human reading it as spec.

Proposed change

Build the reset and make the docs true:

  • resetDemoTenant service in @sustentus/services: purge every demo-tenant-scoped document the seeder (and demo usage) produces, then re-seed to the approved storyline — converging, not accumulating. Hard-gated on the requireDemoTenant guard from the foundation stub: refuses any tenant with isDemo: false.
  • Identities survive: the Clerk org, the six persona users and their app-user documents are kept — data is purged, people are not.
  • Manual trigger: an admin reset button (confirm dialog) beside the existing demo-data button, for between client sessions.
  • Nightly cron: a scheduled reset via the existing /api/cron + CRON_SECRET pattern so the tenant opens clean every morning.
  • Client-session runbook (before/after a walkthrough) and a docs sync of technical/demo-environment to describe what actually ships.

Acceptance criteria (rough)

  • Two consecutive resets leave identical counts — the world converges on the approved storyline instead of accumulating (lead count constant, no orphaned batch remnants).
  • Reset against any isDemo: false tenant is refused, and the refusal is test-covered.
  • The six persona users and the Clerk org survive a reset and can sign straight back in.
  • The nightly schedule runs unattended; the manual admin trigger works with confirmation.
  • apps/docs technical/demo-environment describes the shipped reset — no surviving references to unbuilt snapshot machinery.
  • Any new env var is added to turbo.json → globalEnv in the same PR.

Out of scope (this feature)

  • Snapshots, backups, or point-in-time restore — reset means purge-and-reseed, nothing more.
  • Generalised tenant-deletion tooling — this only ever operates behind requireDemoTenant.
  • Changing what the seeder generates — the storyline is fixed by demo-seed-storyline.

Notes for Define

Covers report punch-list D, plus the reset half the report assumed already existed. Verified against source (2026-08-11): append-only is by design (packages/services/src/db/services/demo-data/index.ts:49–51; each run stamps an 8-char batch token, line 381); the tiered catalogue portion is idempotent via upsertNamed (line 479) — Define must decide whether purge clears the catalogue or leaves the idempotent upserts to converge. Enumerate purge targets from what the generator writes (leads, proposals, quotes, milestones, invoices, matches, status history, activities, blockers, change control, action items, project messages, expert ratings) plus whatever demo usage writes between resets (CSAT from stub 2, notifications, audit entries — sweep tenant-scoped collections rather than hardcoding a list that rots). The /api/cron self-auth pattern already exists (apps/web/proxy.ts:26–37). Use the db-migration skill if indexes are needed for the purge, server-action for the button, docs-sync at Ship for the docs page. touches: guess — packages/services/src/db/services/demo-data/**, apps/web/app/(app)/admin/**, apps/web/app/api/cron/**, vercel.json, turbo.json, apps/docs/app/technical/demo-environment/.

_done/demo-scope-visuals.md

Stub: Scope & licence visuals — make the RBAC engine visible in a walkthrough

  • feature-slug: demo-scope-visuals
  • scope: demo-tenant
  • personas: Admin, Vendor
  • initiative: Refine the bridge / objective: Q2-2026 Objective 1 — Establish Product-Market Fit with Vendor Partners
  • depends-on: none
  • sequence: 6 of 6

Problem

Role-based scoping is a headline sales point — six personas, deny-by-default routes, permission templates — but it is invisible in a walkthrough: the presenter switches personas and narrates "notice what you can't see" from memory. Nothing on screen shows what a persona can and cannot do, or how licence classes map onto capability (report G).

Proposed change

A read-only scope visualisation derived from the real RBAC engine:

  • Per-persona scope bar / matrix: a screen that renders each persona's effective capabilities from the actual permission templates — not hardcoded marketing copy — so the walkthrough can show the permission model as a first-class feature.
  • Licence-class view: how licence classes group those capabilities, as report-only visualisation (licence classes stay unenforced, as today).
  • Derived, not duplicated: template changes show up without code edits; the screen works on any tenant but is designed to shine in the demo walkthrough.

Acceptance criteria (rough)

  • A screen renders every persona's effective scope from the live permission templates; a template change is reflected without touching the screen's code.
  • Licence classes are visualised as report-only groupings — no enforcement behaviour is added anywhere.
  • The screen is read-only: it changes no RBAC behaviour and grants no capability.
  • The new route has a route-policy entry and is visible to the roles the spec names.

Out of scope (this feature)

  • Licence enforcement, billing, or seat management.
  • Editing roles/permissions from the new screen.
  • Any change to the deny-by-default proxy behaviour or existing route policies beyond adding the new screen's own entry.

Notes for Define

Covers report punch-list G (cut decision 2026-08-11: in scope; F was cut out). Verified against source: route authorization is deny-by-default via getAllowedRoles (apps/web/lib/route-policies.ts, 73 rules), with roles derived by inverting PLATFORM_ROLE_TEMPLATE_DEFAULTS at module load; per-tenant template customisations and per-user overrides apply at the action layer — the visualisation should surface effective scope per persona and be honest about tenant-level customisation. Use the web-route skill when adding the page (a missing route-policy entry silently redirects — the known trap). Which roles see the screen (admin-only vs admin+vendor) is a Define decision; the demo presenter reaches it via view-as either way. Code-independent of the other stubs — sequenced last only by the foundation-first-then-impact tie-break. touches: guess — a new route + components under apps/web/app/(app)/**, apps/web/lib/route-policies.ts, reads from the permission template source in packages/services.

_done/demo-seed-storyline.md

Stub: Demo seed storyline — client-credible world, every headline surface populated

  • feature-slug: demo-seed-storyline
  • scope: demo-tenant
  • personas: Admin, CSM, SDM, Expert, Vendor, Customer
  • initiative: Refine the bridge / objective: Q2-2026 Objective 1 — Establish Product-Market Fit with Vendor Partners
  • depends-on: demo-tenant-foundation
  • sequence: 2 of 6

Problem

The seeder produces a structurally rich world (25 leads across 9 stages, proposals, quotes, milestones, invoices, blockers, ratings) but not a client-credible one: names and numbers are generic, and it has known gaps — it writes no CSAT records (delivery satisfaction is only ExpertRating), so the CSAT deep-dives among the 11 vendor analytics pages render empty. A prospect walkthrough dies on an empty analytics page or a lead called "Test Lead 7".

Proposed change

A storyline pass over the demo-data generator:

  • Fictional companies, people and numbers that read as a coherent story across all six persona dashboards — the same world seen from six angles. Paul owns and signs off the storyline content (consistent with his Design-stage demo sign-off).
  • Fill the seed gaps: generate CSAT records so every CSAT-driven analytics page renders, and audit the other headline demo surfaces (the 11 vendor deep-dives, each persona's dashboard, the project workspace) for any that render empty from seed — fill or flag each.
  • Re-run against the demo tenant so the approved storyline is live.

Acceptance criteria (rough)

  • All 11 vendor analytics deep-dive pages render with data on the demo tenant — CSAT included, none empty.
  • Each of the six persona dashboards shows a coherent, populated slice of the same storyline (same companies, same people, consistent numbers).
  • Storyline content is fictional only and signed off by Paul (sign-off recorded in the PR).
  • The seeder still writes through models with no notifications, emails, or external side effects.

Out of scope (this feature)

  • Purge/reset — the storyline must be reproducible by the seeder, but building the reset is demo-reset-ops.
  • New product features or pages — this stub only feeds existing surfaces.
  • The "generate example leads" narrative screen (report F) — out of scope for the whole batch.

Notes for Define

Covers report punch-list B beyond the first run. Verified against source (2026-08-11): the generator (packages/services/src/db/services/demo-data/index.ts, STAGE_PLAN lines 93–104) imports no CSAT model — the CSAT service/model exist independently (packages/services/src/db/services/csat/, models/csat.ts) and the docs' claim that demo data includes CSAT is drift, not fact. The generator deliberately has no side effects (index.ts:52–53) — keep that property. Seeding stays re-runnable: enrichment must not break populateDemoLeads's append semantics before demo-reset-ops replaces them with purge-and-reseed. Data policy from the cut: fictional data only, no real prospect/client names, ever. touches: guess — packages/services/src/db/services/demo-data/** only.

_done/demo-tenant-foundation.md

Stub: Demo tenant foundation — isDemo wired, tenant provisioned, one user per persona, first seed

  • feature-slug: demo-tenant-foundation
  • scope: demo-tenant
  • personas: Admin, CSM, SDM, Expert, Vendor, Customer
  • initiative: Refine the bridge / objective: Q2-2026 Objective 1 — Establish Product-Market Fit with Vendor Partners
  • depends-on: none
  • sequence: 1 of 6

Problem

The platform has everything a demo needs except a demo tenant to run it in. ITenant.isDemo is declared and indexed but dead — nothing reads or writes it — so there is no guardrail to hang demo-only behaviour on. And the demo-data seeder creates no users: it reuses the tenant's existing customers, experts and CSMs, so a fresh tenant seeds an empty world. Without a flagged tenant and six persona users, none of the rest of the batch can land (report A, B).

Proposed change

Stand up the demo tenant end-to-end on production:

  • Wire isDemo: a tracked way to set it (migration or admin-only script — not a hand-edit) and a guard helper in @sustentus/services (e.g. requireDemoTenant(tenantId)) that destructive demo operations must call and that hard-refuses any tenant with isDemo: false.
  • Provision the demo tenant: a real Clerk organisation, resolved into a tenant document flagged isDemo: true; provisioning steps written down as a runbook.
  • One user per persona: six Clerk users (admin, csm, sdm, expert, vendor, customer) in the demo org, each resolving to an app user in the demo tenant with the right role, each landing on its ROLE_HOME. The admin user is the presenter's login (cut decision: presenter-led).
  • First seed run: populate the tenant via the existing admin demo-data surface so the 25-lead, 9-stage world exists and is attached to the persona users.

Acceptance criteria (rough)

  • The demo tenant exists in production with isDemo: true, set by a tracked migration/script; tenantService exposes the flag.
  • Signing in as each of the six persona users lands on that persona's dashboard with the right role resolved — no /no-role, no onboarding trap.
  • A requireDemoTenant-style guard exists, is unit-covered, and refuses non-demo tenants.
  • The seeder has been run against the demo tenant: leads exist across all nine stages, linked to the persona users.
  • Zero behaviour change for tenants with isDemo: false.

Out of scope (this feature)

  • Purge/reset of seeded data — that is demo-reset-ops.
  • Storyline naming, numbers, and seed-gap fills (CSAT) — that is demo-seed-storyline.
  • Any change to the view-as switcher — that is demo-presenter-switching.

Notes for Define

Covers report punch-list A and the initial run of B. Verified against source (2026-08-11): isDemo is declared + indexed at packages/services/src/db/models/tenant.ts (~lines 53, 86) with no reader or writer anywhere — wiring it is net-new; the seeder's single entry point demoDataService.populateDemoLeads(tenantId, createdBy) (packages/services/src/db/services/demo-data/index.ts:368) creates no users — persona users must exist before the first seed; the admin trigger already ships at apps/web/app/(app)/admin/settings/demo-data (server action populateDemoData, admin-gated via resolveActionContext). Tenants are lazily created from Clerk orgs (apps/web/lib/tenant.tstenantService.upsertFromClerk), so provisioning is "create the org + users in Clerk, sign in once, then flag" — the flagging step is the code change. Use the db-migration and mongoose-model skills for the flag/guard work. Fictional identities only, even for placeholder names — the storyline pass (stub 2) renames, it does not de-realify. touches: guess — packages/services/src/db/{models/tenant.ts,services/tenant/**}, a migration under packages/services/src/db/migrations/, apps/docs runbook page.

_source/report.md

Demo environment: recommendation

Decision: Where should the client-facing demo live? Recommendation: Build it as a demo tenant on platform.sustentus.com (apps/web + packages/services), not as a standalone app on a new Neon Postgres database. Basis: Direct read of both codebases + a screen-by-screen comparison of apps/demo vs apps/web (see the accompanying gap analysis).


Why

The platform already is the demo — minus data and a login-free way in. A screen-by-screen check found the platform already implements five of the six demo areas, usually as the real feature where the demo is a scripted mock:

  • All six persona dashboards (Admin, Vendor/Sales, Customer, Expert, CSM, SDM) — real, server-rendered, same @sustentus/ui design system as the demo.
  • Vendor analytics — the same 11 deep-dive pages (funnel, retention/NRR/GRR, time-to-value, revenue-risk, CSAT…).
  • Onboarding and BRD/project discovery — real streaming-LLM agents; the demo versions are canned recordings of these.
  • Leads → proposals → expert bids — full lifecycle, including a real CSV/XLSX import wizard and real bid/accept/reject flows.
  • Project workspace — real permission-gated sections + levers.

Crucially, the platform already ships a demo-data seeding service (a re-runnable generator that fills a tenant with leads across every stage, plus proposals, quotes, milestones, invoices, blockers, CSAT, etc.) and a view-as persona switcher. These are the two hardest parts of "a demo with realistic data and roles" — and they already exist, in production-grade form.

The one genuine gap is friction-free, no-login persona switching for a prospect walkthrough. The platform's view-as is admin-gated and Clerk-tied. That's the piece worth building — and it reuses the existing switcher.

One myth to retire: the demo's workspace has no "what-if" simulator — no sliders, no forecast model. Its stage strip just rewinds a single seeded lead through the stages because the demo only has one lead. A seeded tenant (real leads at every stage) removes the need for that trick entirely. A true what-if simulator would be net-new on either architecture.


What we'd build (demo-tenant punch list)

# Task Reuses Effort
A Demo tenant + one seeded user per persona tenant model, user seed 1–3 d
B Populate it with the existing demo-data seeder already built <1 d
C Friction-free persona switching for the demo tenant (presenter auto-login as admin + exposed view-as, or a relaxed demo-tenant launcher) view-as 1–3 d
D Reset runbook between client sessions seeder (re-runnable) <1–2 d
E (optional) Canned mode for onboarding + BRD so live demos don't depend on LLM latency onboarding/BRD agents 1–3 d
F (optional) "Generate example leads" source-narrative screen seeder 1–3 d
G (optional) Scope-bar / licence-report visualisations RBAC engine <1–3 d

Core (A–D): ~1–1.5 weeks AI-assisted. None of it is throwaway — every item hardens the product we already sell.


Why not the standalone Neon Postgres demo

It would rebuild capability the platform already has (demo-data seeding, persona switching, tenancy, six dashboards) in a third data technology (alongside JSON mocks and MongoDB), and — per the proposal's own risk table — the eventual Postgres→MongoDB port is a "rewrite," rated high-impact, not a couple of days. That defers the hardest, most uncertain work to the worst possible moment: when the first paying customer is waiting. The demo would also not be the product we sell, so winning a deal triggers a scramble to rebuild on the platform under maximum pressure.

The "greenfield is faster for AI" point is real but local: it's faster to write, not faster to deliver, because it duplicates a working system. And the platform's Mongo layer isn't AI-hostile — it's typed Mongoose models with tracked migrations, a seed system, and the demo-data service already in place for an agent to work against.


Two questions to reconcile before committing to Postgres

  1. The proposal's own risk table rates the future Mongo port as a rewrite / high-impact — how does that square with "a couple of days after we have paying customers"?
  2. The platform already has a demo-data seeder, a view-as persona switcher, and multi-tenancy — what specifically can't those do that justifies standing up a second database?

Separate decision, don't bundle it

Whether production should eventually move from MongoDB to Postgres is a legitimate architecture question — but it should be decided on its own merits, not adopted as a side effect of getting a demo working this month.