demo-seed-storylinerun.md03_define/output/spec.mddemo-tenant-foundation stood up the flagged demo tenant and ran the first seed, so the platform
now renders — but not as a story a prospect would believe. Every field is drawn at random from a
pool (pick/randInt, demo-data/index.ts:343–361): a lead's title, description, requirements,
catalogue path and budget have nothing to do with each other or with the account they sit on, and
two runs never produce the same world. Worse, the world has no past. Every record is written at
Date.now() (only dueDate is offset), and the tenant has exactly one customer account, so the
vendor analytics — which window on createdAt/paid dates and compare against the prior window —
have one bucket, one account and nothing to trend. And the generator imports no CSAT model at all
(the docs page's claim that demo data includes CSAT is drift), so every CSAT-driven read returns
empty: the vendor CSAT, health, risk, retention and customers pages, plus sdm-coverage,
sdm-strength, sdm-market-signals, expert-workbench and the matching quality scores.
A walkthrough that dies on an empty analytics page is the failure mode this whole batch exists to remove. This advances Refine the bridge / Q2-2026 Objective 1 — Establish Product-Market Fit with Vendor Partners: the demo is the product, and the product has to look like a business.
A storyline pass over the demo-data generator, in five functional pieces.
Replace random selection with a fixed authored dataset — the tables in Storyline content below
are the source of truth. Every engagement's account, title, description, requirements, catalogue
path, value and dates are authored, not drawn. Math.random() leaves the generator entirely; the
only non-determinism left is Mongo's ObjectIds and the wall-clock the relative dates hang off.
This is what makes demo-reset-ops able to reproduce the approved storyline rather than a
fresh roll of the dice.
The generator reads its users from the tenant and today finds one of each, so "top customers", per-account health and retention cohorts are single-row. It gains one new responsibility: upsert-by-name creation of the nine non-persona customer company records and the five non-persona expert records (the same idempotent pattern the tiered catalogue already uses), so a re-run reports 0 created rather than duplicating the world.
The six Clerk-provisioned persona users are not created or renamed by the seeder — a rename
would be overwritten by tenantService.upsertFromClerk on the next sign-in. Their storyline
identities are set in Clerk as a runbook step, and the seeder matches them by email and weaves
them into the story: demo+customer@apoyar.eu is Anneke Vos at Meridian Foods Group (the anchor
account), demo+expert@apoyar.eu is Ivo Marchetti, demo+csm@apoyar.eu is Sofia Almeida,
demo+sdm@apoyar.eu is Ben Hartley, demo+vendor@apoyar.eu is Calderon Technology Partners,
demo+admin@apoyar.eu is Alex Rendell.
Keep the 25-lead, nine-stage current pipeline (STAGE_PLAN is unchanged in shape) and add a
closed-engagement tail of 36 engagements spread across the last 18 months, denser in the
recent months so the default last-30-days window is populated and has a prior window to compare
against. Each tail engagement carries its lead, status history, quote, milestones, paid invoice,
expert rating and CSAT, all dated to its close month — history rows and money dates included, not
just the lead. Backdating is relative (months-ago offsets), so the world stays current on every
reseed.
Import the CSAT model and write a record for every engagement that reached survey_sent or
beyond — the 2 survey and 2 completed leads in the current pipeline, and all 36 tail
engagements bar the four deliberate exceptions that carry paid revenue and no response (so
revenueAwaitingCSAT is a real number, not zero). Scores are authored per engagement on the
model's 1–10 scale, with the sub-scores (communicationEffectiveness, timelineAdherence,
valueForMoney, technicalExpertise, …) varying around the overall rather than duplicating it.
The unique index is {tenantId, lead, customer} with isDeleted: false, so one record per
engagement.
Walk the 11 vendor deep-dives (/vendor/{revenue,csat,renewals,retention,delivery,metrics,funnel, risk,pipeline,customers,health}), the vendor dashboard, the six persona dashboards
(/{admin,csm,sdm,expert,vendor,customer}/dashboard) and the project workspace on the seeded
tenant, at both the default window and last-12-months. Record every surface in a fill-or-flag
table in the PR. A surface that renders empty for want of data is filled here; one that
renders empty for want of product code is flagged, and gets an intake stub in
pipeline/intake/demo-tenant/ rather than being built in this run. Then re-run the seed against
the demo tenant so the signed-off storyline is live.
Fictional throughout — no real prospect or client names, ever. Paul reviews and signs off these tables; the sign-off is recorded on the PR before the Spec-approved tick.
Calderon Technology Partners is a Benelux/UK technology services vendor. It sells delivery capacity it does not itself hold, and uses Sustentus as the bridge: customer demand in, matched experts out, delivery tracked to invoice. Sofia Almeida (CSM) owns the customer relationships, Ben Hartley (SDM) owns delivery, Alex Rendell administers the tenant.
| Account | Sector | Country | Primary contact | Role |
|---|---|---|---|---|
| Meridian Foods Group | Food manufacturing | NL | Anneke Vos | Operations Director |
| Aldercroft Insurance | Insurance | UK | Ruth Delaney | Head of Digital |
| Volta Mobility | EV charging | DE | Jonas Brandt | CTO |
| Harbourline Logistics | Freight & logistics | BE | Pieter Claes | IT Manager |
| Kestrel Energy | Renewables | NL | Lieke de Wit | Programme Manager |
| Orbis Pharma Services | Pharma CRO | BE | Camille Renard | Head of Systems |
| Bramble & Co Retail | Specialty retail | UK | Marcus Ainsley | Digital Director |
| Fennfield Health | Private healthcare | IE | Siobhan Kerr | CIO |
| Saltmarsh Utilities | Water utility | UK | Dev Raghunathan | Head of IT |
| Tanner Rowe Legal | Legal services | UK | Elaine Tanner | Managing Partner |
Meridian Foods Group is the anchor: it is the signable customer persona, the largest account by revenue, and appears in the current pipeline, the tail and the in-flight delivery work, so the customer dashboard and the vendor dashboard show two views of one relationship.
| Expert | Specialism |
|---|---|
| Ivo Marchetti | ERP & integration lead (persona) |
| Priya Raman | Data platform & analytics |
| Tomas Lindqvist | Security & compliance (SOC 2, ISO) |
| Grace Obi | Accessibility & front-end |
| Daniel Fournier | Commerce & payments |
| Marta Nowak | Identity & access management |
STAGE_PLAN counts are unchanged (3/3/3/3/4/3/2/2/2). Each slot is now a named engagement.
| Stage | Account | Engagement | Value |
|---|---|---|---|
| pending | Meridian Foods Group | Warehouse management system replacement | 120k |
| pending | Volta Mobility | Charge-point telemetry data platform | 95k |
| pending | Tanner Rowe Legal | Matter-management system migration | 48k |
| backlog | Harbourline Logistics | Freight visibility portal | 78k |
| backlog | Bramble & Co Retail | Store stock-accuracy programme | 64k |
| backlog | Kestrel Energy | Asset performance reporting | 71k |
| bidding | Aldercroft Insurance | Claims intake automation | 105k |
| bidding | Orbis Pharma Services | GxP document workflow rebuild | 88k |
| bidding | Saltmarsh Utilities | Field-engineer mobile app | 67k |
| quoting | Fennfield Health | Patient portal accessibility remediation | 52k |
| quoting | Meridian Foods Group | Supplier onboarding integration | 84k |
| quoting | Volta Mobility | Payments & roaming settlement integration | 97k |
| in_progress | Meridian Foods Group | ERP migration to cloud — phase 2 | 142k |
| in_progress | Aldercroft Insurance | Policy data warehouse build | 118k |
| in_progress | Harbourline Logistics | Customs API integration | 73k |
| in_progress | Kestrel Energy | SCADA data pipeline modernisation | 91k |
| delivered | Bramble & Co Retail | Headless commerce replatform | 109k |
| delivered | Orbis Pharma Services | Identity & access management rollout | 86k |
| delivered | Saltmarsh Utilities | Billing reconciliation automation | 63k |
| survey | Fennfield Health | Clinical scheduling integration | 57k |
| survey | Volta Mobility | Driver app accessibility audit | 34k |
| completed | Meridian Foods Group | ERP migration to cloud — phase 1 | 128k |
| completed | Tanner Rowe Legal | Document e-signature rollout | 42k |
| qualified_out | Bramble & Co Retail | In-store AR pilot | — |
| qualified_out | Saltmarsh Utilities | Drone survey trial | — |
The two qualified_out engagements carry their reason in the lead: no budget in the current year
(Bramble) and outside the service catalogue (Saltmarsh).
Closed is a months-ago offset from the seed run, so the history stays current on every reseed.
CSAT is the authored overall score on the model's 1–10 scale; — means paid with no response
yet (this is what makes revenueAwaitingCSAT non-zero).
| # | Account | Engagement | Closed | Value | CSAT |
|---|---|---|---|---|---|
| 1 | Meridian Foods Group | Supplier EDI onboarding wave 3 | M-1 | 96k | 9 |
| 2 | Aldercroft Insurance | Claims dashboard rollout | M-1 | 84k | 8 |
| 3 | Harbourline Logistics | Port terminal API connector | M-1 | 72k | 9 |
| 4 | Saltmarsh Utilities | Meter-read exception handling | M-1 | 58k | 6 |
| 5 | Volta Mobility | Roaming settlement reconciliation | M-2 | 88k | 9 |
| 6 | Kestrel Energy | Turbine telemetry ingestion | M-2 | 76k | 8 |
| 7 | Bramble & Co Retail | Checkout performance hardening | M-2 | 62k | — |
| 8 | Fennfield Health | Appointment reminder service | M-2 | 39k | 9 |
| 9 | Orbis Pharma Services | eTMF integration | M-3 | 81k | 9 |
| 10 | Meridian Foods Group | Cold-chain sensor feed | M-3 | 54k | 8 |
| 11 | Saltmarsh Utilities | Outage notification service | M-3 | 47k | 7 |
| 12 | Aldercroft Insurance | Broker portal SSO | M-4 | 69k | 8 |
| 13 | Tanner Rowe Legal | Client intake automation | M-4 | 44k | 7 |
| 14 | Harbourline Logistics | Customs declaration validator | M-4 | 58k | 9 |
| 15 | Volta Mobility | Tariff engine rebuild | M-5 | 93k | 9 |
| 16 | Bramble & Co Retail | Loyalty data migration | M-5 | 51k | 8 |
| 17 | Kestrel Energy | Grid-export reporting | M-6 | 67k | 8 |
| 18 | Fennfield Health | Referral routing rules | M-6 | 42k | 9 |
| 19 | Meridian Foods Group | Production planning integration | M-7 | 88k | 9 |
| 20 | Orbis Pharma Services | Lab result ingestion | M-7 | 63k | 8 |
| 21 | Saltmarsh Utilities | Asset register cleanse | M-8 | 49k | 6 |
| 22 | Aldercroft Insurance | Fraud-signal enrichment | M-8 | 77k | 8 |
| 23 | Harbourline Logistics | Fleet telematics dashboard | M-9 | 61k | 9 |
| 24 | Tanner Rowe Legal | Matter billing exports | M-9 | 38k | 7 |
| 25 | Volta Mobility | Charge-point provisioning API | M-10 | 84k | 9 |
| 26 | Bramble & Co Retail | Product data syndication | M-10 | 56k | — |
| 27 | Kestrel Energy | Maintenance scheduling | M-11 | 58k | 8 |
| 28 | Meridian Foods Group | Warehouse label printing | M-11 | 41k | 9 |
| 29 | Fennfield Health | Consent capture rebuild | M-12 | 47k | 8 |
| 30 | Orbis Pharma Services | Audit trail hardening | M-12 | 72k | 9 |
| 31 | Aldercroft Insurance | Legacy claims decommissioning | M-13 | 66k | 7 |
| 32 | Harbourline Logistics | Depot scheduling pilot | M-14 | 35k | 8 |
| 33 | Meridian Foods Group | Recipe management migration | M-15 | 59k | 8 |
| 34 | Saltmarsh Utilities | Field app phase 1 | M-16 | 44k | — |
| 35 | Volta Mobility | Site survey portal | M-17 | 48k | 9 |
| 36 | Tanner Rowe Legal | Knowledge base rebuild | M-18 | 31k | — |
The tail totals ≈2.2M closed over 18 months, and it is authored to make three narrative beats visible on the analytics pages rather than leaving them flat:
LOW_CSAT_CUTOFF — so it surfaces as a genuine
"Failing CSAT" row on the risk and health pages instead of a table of green.Math.random(): two seed runs against an equivalent empty tenant
produce identical accounts, engagement titles, descriptions, catalogue paths, values and
CSAT scores, differing only in ObjectIds and the wall-clock the relative dates hang off.survey_sent except the four authored
exceptions, one per lead, with sub-scores that vary around the overall.vendorService's CSAT-driven reads return non-zero on the demo tenant: avgCSAT,
revenueWeightedCSAT and revenueAwaitingCSAT are all non-zero, and at least one account
is flagged Failing CSAT.pipeline/intake/demo-tenant/.populateDemoLeads still appends rather than clearing.demo-reset-ops. A consequence to accept meanwhile: because seeding stays append-only, a second
run against an already-seeded tenant duplicates the engagement graph (the accounts and catalogue
upsert, the leads do not). Until demo-reset-ops lands, the demo tenant is seeded once.vendorService, sdm-* and
expert-workbench are read as-is; if a page stays empty with correct data, that is a flag.isDemo, and nothing
changes for a tenant that seeds today.view-as switcher (demo-presenter-switching), canned LLM mode
(demo-canned-agents), scope visuals (demo-scope-visuals).apps/docs/app/technical/demo-environment/page.mdx carries two pieces of drift
this run makes visible — it claims seeded data includes CSAT (true only after this run), and its
Login Credentials section lists demo+customer1..10@apoyar.eu accounts that the foundation
runbook never provisioned and this run does not need (the nine extra accounts are company
records, not sign-ins). Ship's docs-sync corrects both in this PR.demo@apoyar.eu inbox. Worth a separate /pipeline bug — flagged,
not touched here.04_build/output/notes.md4e4c72a deterministic swappable storyline + CSAT + backdated tail;
<runbook> persona identities in the provisioning runbookpackages/services/src/db/services/demo-data/storyline/ (new): the swap point. types.ts
is the contract, default.ts is the authored Calderon world from the spec's tables,
index.ts exports the active storyline, default.test.ts asserts its invariants.packages/services/src/db/services/demo-data/index.ts: the generator keeps the mechanics
and holds no content. Math.random() is gone entirely — variation now comes from an FNV-1a
hash of each record's own identity (spread/at/slice), so the same storyline produces the
same world every run. Adds CSAT writes, the backdated closed-engagement tail, and
upsert-by-email creation of the storyline's customers and experts. populateDemoLeads takes an
optional storyline argument, defaulting to the active one.apps/web/.../demo-data-button.tsx: the confirm dialog claimed "It does not create any
users", which this change makes false — reworded, and the summary gained CSAT responses,
closed engagements, and customers/experts created.apps/docs/.../demo-environment/page.mdx: the runbook's persona step now carries the
six storyline identities as a table, with the reason they are set in Clerk rather than by the
seeder (upsertFromClerk would undo a rename on next sign-in).The storyline is a data module behind a type, not content threaded through the generator. To
change the demo world: author a module satisfying Storyline and re-point activeStoryline in
storyline/index.ts — or pass one to populateDemoLeads(tenantId, createdBy, storyline) for a
one-off. The generator has no company names, people, titles, values or scores in it, so a swap
touches no logic. demo-reset-ops inherits this: it reseeds whatever storyline is active.
resolveUser throws if a storyline persona is
missing rather than inventing one — a missing persona is a provisioning error, and inventing a
user would produce a half-anonymous world that looks seeded but cannot be signed into.vendor when it is unset, because the
vendor analytics attribute revenue through it and Clerk provisioning does not set it. It never
overwrites an existing value.timestamps only sets createdAt when unset, so an
explicit createdAt survives. Verified against the installed Mongoose 8 source
(lib/helpers/timestamps/setDocumentTimestamps.js: the guard is
!doc.$__getValue(createdAt)), not assumed — the whole tail depends on it. The same function
seeds updatedAt from createdAt on insert, so backdated rows are consistent on both fields.Math.random() in the generator; two runs of a storyline produce identical content —
grep over demo-data/** returns only the comment explaining the change.resolveUser finds before
it creates, so a second run reports 0 created.survey_sent except the storyline's four
authored exceptions, one per lead, sub-scores varying ±1 around the overall.populateDemoLeads still appends.vendorService's CSAT-driven reads return non-zero on the demo tenant — needs the
seed run; the data that feeds them is written, but the assertion is against a live tenant.Six criteria are open and all six need the same thing: the seed run against the demo tenant, and
then someone looking at it. That is Verify's DoD smoke pass plus the ops half of the run — the
same shape demo-tenant-foundation had. Nothing is blocked on code.
Read from source, not from a running tenant. Every row is a claim to check during Verify, not a result.
| Surface | Reads | Now fed by |
|---|---|---|
/vendor/dashboard |
envelope, funnel, top customers, health, TTV, cohorts | tail leads + paid invoices + CSAT across 18 months |
/vendor/revenue |
getRevenueRetention, getRevenueFlow |
backdated paid invoices |
/vendor/csat |
csatByRevenue off the envelope |
new CSAT records |
/vendor/health |
getCustomerHealth |
CSAT + per-account paid revenue |
/vendor/risk |
at-risk rows, Failing CSAT flag |
Saltmarsh's 6/7/6 (3.2 normalised, under 4.0) |
/vendor/retention |
getRevenueRetention, getRetentionCohorts |
repeat accounts across consecutive windows |
/vendor/renewals |
declining/lapsed accounts | Tanner Rowe's decline, nothing closed since M-4 |
/vendor/customers |
per-customer aggregation | 10 accounts with varied revenue |
/vendor/funnel |
windowed leads incl. rejected | 25 live + 36 tail leads, spread by createdAt |
/vendor/delivery |
intake→delivered timings | tail spans (60–120 days, deterministic) |
/vendor/pipeline |
open pipeline | the 25 live engagements |
/vendor/metrics |
metricDictionaryService.grid() |
not seed-driven — renders regardless; expected to be unaffected |
demo-reset-ops; the demo tenant
should be seeded once.default.test.ts asserts the storyline's referential integrity and its three narrative beats.
It does not exercise the generator — that needs the integration tier, which the config does not
have (CONVENTIONS.md → Testing: don't improvise a tier).demo+customer1..10@apoyar.eu accounts
that do not exist, and commits plaintext passwords including the shared inbox. The spec routes
the first to Ship's docs-sync; the second is flagged for a separate /pipeline bug.Context budget: within band. One extra read outside the Inputs table — the vendor analytics pages
and vendorService — to establish which surfaces the CSAT gap actually empties, since the spec's
claim needed confirming against source before building to it.
05_verify/output/verify.mdcomplexity: complex; the CI Claude review is not enabled —
ENABLE_CLAUDE_REVIEW is unset, so its check run reports skipped). 4 findings, all 4 fixed
on branch. One was a release-blocking defect in this run's own diff.The code review found the defect that mattered most, and it was mine: the backdated tail landed
a full window early. monthsAgo(1, …) resolved to now − 30d − [0..27]d, so every month-1
engagement fell outside the last-30-days window the vendor analytics default to. The current
window would have read empty and the documented positive deltas would have rendered negative —
precisely the failure the tail exists to prevent, in the feature whose entire purpose is that the
windows are populated. Nothing in the storyline data could have fixed it, and no test would have
caught it, because the semantics were never pinned.
Fixed in ecb410e: month n now spans days [(n−1)×30, n×30), so month 1 is the current window.
The date helpers moved to demo-data/deterministic.ts — a pure module with no database imports —
and deterministic.test.ts now pins the window semantics, the no-future-dates property and the
month ordering. CSAT is dated on close rather than three days after, because the most recent
engagements now close today and a positive offset would have produced future-dated responses.
Fixed on branch:
ecb410e) — as above. Release-blocking; found by review, not by
CI, and not by me.ecb410e) — user resolution ran after
the catalogue upserts, so a tenant without the storyline's personas kept ~70 catalogue rows from
a run that then threw. There is no transaction, so ordering is the only guard; resolution now
runs before any write.ecb410e) — vendorId undefined meant every
expert rating was skipped and no quote, invoice or CSAT carried an attribution, while the run
still reported success: a world that looks seeded and reads as zero. Both are now required and
fail loudly, like the customer and expert personas.3d3bdfb) — the seed is ~1,600 sequential round-trips inside a
synchronous server action that inherited the platform default duration. Server actions inherit
from the route that invokes them, so maxDuration = 300 is declared on
admin/settings/demo-data/page.tsx. This matters more than a slow button: the writes are
append-only with no transaction, so a timeout mid-tail can only be duplicated, not resumed,
until demo-reset-ops lands the purge.3d3bdfb) — tenantId_email_role_unique is not partial
on isDeleted, so an archived storyline user was hidden from the lookup and then collided on
create. resolveUser now revives through the raw collection, the same shape upsertNamed
already used one function above it.3d3bdfb) — the four survey/completed scores were
hash-derived in a 7–9 band, so no storyline could express an unhappy live engagement. That is a
hole in the swap point this module claims to be. StorylineEngagement.csat is now optional-with-
fallback and the four scores are authored in default.ts.3d3bdfb) — the page described only "demo leads", with no
mention of the backdated history, the users, the CSAT responses, or that there is no undo.Accepted / carried forward:
demo-reset-ops, no transaction, and a re-run
appends rather than resumes. Spec accepts this; the readiness audit's point is that the PR
didn't say so where an operator would see it. Now stated here and in the admin copy: run it
once, against the demo tenant only, and verify before announcing.maxDuration = 300 assumes the Vercel plan allows it. The repo's next-highest is 120
(api/brd/chat). If the plan caps lower, the value silently clamps — confirm before the run.admin/demo-data/actions.ts revalidates
/admin/demo-data, which is now only a redirect stub, so the page the admin is actually on is
never revalidated after seeding. actions.ts is unchanged in this diff.HTTP 200 at
web-git-claude-demo-seed-storyline-pipeline-41es39-sustentus.vercel.app, redirecting to
/sign-in?redirect_url=%2F (agent)Math.random() remains in the generator — grep over demo-data/** returns only the
comment explaining its removal (agent)deterministic.test.ts;
storyline referential integrity and the three narrative beats by default.test.ts; both
green in the Quality workflow's Run tests step (agent, via CI)Not demonstrable on the preview — and the reason is structural, not an omission. The remaining
criteria all assert the state of the demo tenant, whose six persona users live in Clerk against
production. The preview deploys against the preview database, which has no such tenant, so
signing in there demonstrates nothing about this storyline. demo-tenant-foundation had the same
shape and resolved it the same way: the ops half ran against production.
Awaiting the operator (each needs the seed run against the demo tenant):
ROLE_HOME (operator)avgCSAT, revenueWeightedCSAT and revenueAwaitingCSAT non-zero; at least one account
flagged Failing CSAT (operator)pipeline/intake/demo-tenant/ for
anything flagged (operator + agent)Timing the first seed run and recording the number on the PR is worth doing — it is the only
evidence that maxDuration = 300 is enough, and the cost of being wrong is a half-seeded tenant
with no purge.
Context budget: within band. One deliberate overrun — the vendor analytics services and the Mongoose timestamp helper were read outside the Inputs table, to confirm the window arithmetic the tail depends on rather than assume it. That read is what surfaced the release-blocking defect.
06_ship/output/investor-update.mdWho it's for: Anyone running a demo — all six persona dashboards What shipped: Demo data is now an authored storyline with a backdated history and satisfaction scores, so every vendor analytics page renders populated. Why it matters: Refine the Bridge / Q2 Objective 1 — Establish Product-Market Fit with Vendor Partners: the demo is the product.
Two runs produce the same world; swapping it is a data change.
Dig deeper: https://github.com/sustentus/sustentus/pull/795
06_ship/output/release.mdQuality Project success on the head commit; Review diff against CONVENTIONS.md
skipped (ENABLE_CLAUDE_REVIEW unset) and Migrate production database skipped, both by designapps/docs/app/technical/demo-environment — the runbook's persona-identity table
(Build) and, here, the whole Populating Demo Leads section: what the seed writes and in what
order, the backdated tail and its 30-day windows, the CSAT records, determinism replacing
Math.random(), a new Swapping the storyline subsection, and the warning that the run has no
undo and no transaction#product-update on merge (2026-08-12) — .github/workflows/ship-note.yamlThe note's Dig deeper line was filled before the merge, not after. The workflow fires on the
merge and emails the file the merge brought in, so a link filled afterwards would never reach the
inbox. The PR URL is stable across the squash and there is no changelog entry to link, so there was
nothing the merge needed to reveal first.
Established by the diff, verified in Verify:
Math.random(); two runs against an equivalent empty tenant produce
identical content — verified by grep over demo-data/** (only the comment explaining its
removal remains) and pinned by deterministic.test.tsresolveUser, which Verify also hardened to revive
a soft-deleted match rather than collide on the unique indexisPersona on the storyline account/expert typessurvey_sent bar the authored exceptions, sub-scores
varying around the overall — createCSATtechnical/demo-environment, with the note that a storyline swap must update itNot established by the diff — all seven need the same thing, the seed run against the demo tenant, whose personas live in Clerk against production. The preview deploys against the preview database and has no such tenant, so nothing on the preview could have demonstrated them. Verify recorded this as structural, not an omission, and it does not change on merge:
avgCSAT, revenueWeightedCSAT and revenueAwaitingCSAT non-zero, with at least one account
flagged Failing CSATpipeline/intake/demo-tenant/ per flagged
surfaceThe code shipped; the storyline is not live until that run happens. Two things to settle first,
both carried from Verify: maxDuration = 300 assumes the Vercel plan allows it (the repo's
next-highest is 120 — it clamps silently if not), and the run is one-way, with no purge until
demo-reset-ops. Time the first run and put the number on the PR — it is the only evidence 300 is
enough, and the cost of being wrong is a half-seeded tenant with nothing to purge it.
Also carried forward, for intake rather than this release: admin/demo-data/actions.ts revalidates
/admin/demo-data, now only a redirect stub, so the page the admin is actually on is never
revalidated after seeding. Unchanged in this diff.
Context budget: within band. No overrun — the OKR and initiative pages were read for the ship note's tie-in, both named by the Inputs table.