preview-db-seedrun.md03_define/output/spec.mdStub 1 (preview-db-wiring, merged as #799) made migrate-preview migrate a real database and fail
red when it can't. That fixes the schema half only: db:seed — the idempotent baseline every
environment needs — has never run in CI at all. A preview database can therefore be perfectly
migrated and still functionally empty, so a preview URL renders half-broken for reasons that have
nothing to do with the PR under test, and the reviewer can't tell the two apart. This is stub 2 of 3
in the preview-db-migrations batch, and the last piece of generic baseline before stub 3 layers
the demo storyline on top — the merge-time confidence that Refine the Bridge / 2026-Q2 Objective 3
(Validate Technical Infrastructure & Payout Flow) is asking previews to provide.
Run the existing baseline seed as part of the existing preview migration job — no new script, no change to what gets seeded.
Seed baseline data step in migrate-preview, after Run migrations, running
pnpm --filter @sustentus/services db:seed with the same environment the migration step already
resolves. The install closure (pnpm install --filter @sustentus/services...) already covers
tsx and the script's imports, so no install change.|| true. A seed failure fails the job red with an
::error:: naming the resolved database. And because Run migrations exits non-zero on failure,
a failed migration means the seed step never runs — nothing seeds against a half-migrated schema.sed -E 's#mongodb(\+srv)?://…#mongodb://<redacted>#g').workflow_dispatch preview run from stub 1 is the
same job, so a dispatched catch-up seeds as well as migrates. No separate wiring.migrate-production is not touched. Whether production seeding belongs in CI is a separate
decision; this run does not smuggle it in.apps/docs preview-database page (created by stub 1) rather than
adding a second page — what the seed covers, what it deliberately doesn't (below), and how to read
the counts. Written via docs-sync at Ship, in this PR.packages/services/scripts/db-seed.ts: its docstring claims the
script seeds "statuses", which it does not (see below). The runbook this run writes must not repeat
that, and the docstring is the thing it would be written from. Comment text only — no behaviour,
no change to what is seeded.src/db/seed/reference-data.ts.
Statuses are not seeded: they live in packages/services/src/db/workflows/workflows.json
and are read in-memory (src/db/services/status/), so nothing status-driven ever depended on this
step. The stub's "statuses, action types" phrasing tracks the stale docstring being corrected
above, and its rough criterion about "status-driven surfaces (lead lifecycle)" is replaced below by
one this change can actually deliver.Tenant defaults: 0 created, 0 already present is the correct and expected reading
on this run; once stub 3 lands, the next preview migration fills that tenant's defaults with no
further change here. The criteria below are written against that reality, not against a hoped-for
non-zero count.migrate-preview runs db:seed after db:migrate up, against the same resolved preview
database, on both the pull_request and the workflow_dispatch (target: preview) paths.migrate-preview run reports 0 created for reference data with all 13
action types already present — idempotence observed in CI, not assumed.db:seed failure fails the job red (no || true), with an ::error:: naming the resolved
database.db:migrate up fails, the seed step does not run.migrate-production is unchanged — its jobs, steps, guards and triggers are byte-for-byte
identical to main.preview-database runbook page in apps/docs covers the seed step: what it seeds, that
statuses come from workflows.json and are not seeded, that tenant defaults apply only to
tenants that already exist, and how to read the counts.preview-demo-storyline).migrate-production.db:seed seeds — no new reference data, no new tenant defaults, no change to
seedDatabase()'s behaviour or its create-if-missing semantics. The one edit inside
packages/services is the docstring correction named above.workflows.json into the database, or seeding them.pull_request path filter; the dispatch catch-up remains the answer to a lagging
preview DB.Context budget: no scope.md or design notes exist for this scope — the preview-db-migrations
breakdown records a conversation-based cut with doc: none, so Define proceeds from the stub, as its
contract allows. The initiative/objective came from the stub rather than from
business/{initiatives,okrs}, and no feature-role-matrix/service-journey page was read: the
breakdown places this batch in dev-infra with no product-surface behaviour change. Overrun: the seed
implementation was read (scripts/db-seed.ts, src/db/seed/*) beyond "a few targeted greps",
because the stub's description of what the baseline seed contains turned out to be wrong and two
acceptance criteria depended on it.
04_build/output/notes.mdfeat: preview-db-seed — seed the preview database after every migration.github/workflows/db-migrate.yaml: a Seed baseline data step appended to migrate-preview,
after Run migrations. Runs pnpm --filter @sustentus/services db:seed with the environment the
job already resolves, set -o pipefail, no || true. Its summary block mirrors the migration
step's shape — outcome line, the script's per-group N created, M already present counts lifted
verbatim by sed, then the raw output in a collapsed block through the same
mongodb(+srv)://… scrub. A non-zero exit emits ::error:: naming the resolved database and
fails the job. The header comment gains a line saying preview seeds as well as migrates, and that
production seeding is a separate decision. Purely additive: 45 insertions, 0 deletions, all in the
preview job and the header.packages/services/scripts/db-seed.ts: docstring correction only — it claimed the script seeds
"statuses", which it has never done. Rewritten to name action types as the global group and to say
outright that statuses live in workflows.json and are read in-memory. No behaviour change.apps/docs/.../preview-database/page.mdx: a The baseline seed section (what runs, when, the
create-if-missing guarantee, the two-group table) with the two easy misreadings called out —
statuses are not seeded, and tenant defaults need a tenant to exist. Reading the result now
covers both summary blocks and explains what the counts mean across consecutive runs; the
catch-up section notes a preview dispatch re-seeds too.apps/docs/.../ci-cd/page.mdx, apps/docs/.../database/page.mdx: kept in step — the
migrate-preview bullet and the summary paragraph now mention the seed step and its failure
semantics; the Seeding section says CI runs it for preview and not for production.No tests: the diff is workflow YAML, comments and MDX. Nothing here is unit-assertable, and the
acceptance criteria are observed on this PR's own Migrate preview database check.
migrate-preview runs db:seed after db:migrate up on both paths — the step is inside
migrate-preview, whose if: already admits pull_request and workflow_dispatch with
target: preview; nothing path-specific was added, so the dispatch route gets it for free.sed -n -E …p lifts
Reference data: and Tenant defaults: from the script's stdout into the summary as bold
rows next to the migration block.::error:: naming the database — no || true;
status is captured and re-raised after the summary is written, same pattern as migrations.db:migrate up fails, the seed step does not run — the migration step's exit "$status"
fails the job, and a failed step skips the rest by default. No extra guard needed.sed -E 's#mongodb(\+srv)?://[^[:space:]"]*#…#g' scrub is applied to seed.log.migrate-production unchanged — git diff main on the workflow is 45 insertions and 0
deletions, none of them inside that job; parsing the YAML confirms its step list is identical.The interesting criteria are observed, not read. This PR touches packages/services/** and
the workflow file, so it matches the pull_request path filter and migrate-preview runs on it —
the new step exercises itself. Read the check's job summary for criteria 2, 3 and 6, and re-run
the job once for criterion 3's second-run reading.
First run on this PR (green,
job 94485792224),
against sustentus-preview:
Reference data: 3 created, 10 already present
Tenant defaults: 168 created, 2 already present
Both numbers contradict what this section predicted before the run, and the correction is the interesting part:
src/db/seed/reference-data.ts), so ordinary preview traffic had already produced 10 of them
organically. The seed filled the three that no code path had happened to touch. Nothing is
wrong; the "13 created on a virgin database" figure only ever applied to a database nothing had
run against.Criterion 3 is the one still open. Re-run the job; the second run should report 0 created for both groups, with 13 reference-data rows and 170 tenant-default rows already present. A non-zero second reading is a real failure — it would mean the seed is not converging.
Worth eyeballing the rendered job summary, not just the log: the counts reach it through a sed
match on the script's stdout, so a future change to those log lines' wording would silently drop
them from the summary while the step still passes.
One stale comment left alone deliberately. packages/services/src/db/seed/index.ts carries the
same "statuses, action types" phrasing this run corrected in scripts/db-seed.ts. The spec bounded
the packages/services edit to that one file, so it was not touched — a one-line follow-up, not a
defect in this diff.
The step name Seed baseline data is new; the check name is still Migrate preview database,
so nothing in branch protection or the stub-1 runbook's references needs changing.
05_verify/output/verify.mdcomplexity: trivial), run locally because CI's Claude review is
gated on vars.ENABLE_CLAUDE_REVIEW == 'true' and its check reported skipped on this PR —
no findings.$GITHUB_STEP_SUMMARY, and production-readiness covered it against the live run (below).Evidence is the Migrate preview database check on this PR's own commits: the diff touches
packages/services/** and the workflow file, so it matches the pull_request path filter and the
new step exercises itself against sustentus-preview. No signed-in surface exists in this diff, so
there is no operator half to wait on — stated plainly rather than left implied.
db:seed runs after db:migrate up on both the pull_request and workflow_dispatch
(target: preview) paths — the step sits inside migrate-preview, whose if: already admits
both events; nothing path-specific was added (agent, traced in diff + observed on the PR's own
pull_request run)ecf3d8f:
Reference data: 0 created, 13 already present / Tenant defaults: 0 created, 170 already present, in 16s. Idempotence seen in CI, not assumed (agent)db:seed failure fails the job red with an ::error:: naming the database — traced: no
|| true, status captured and re-raised after the summary is written (agent, code path)db:migrate up fails the seed step does not run — the migration step's exit "$status"
fails the job and a failed step skips the rest (agent, code path)MONGODB_URI is a GitHub secret and renders *** in logs and
summaries alike, and the block applies the same mongodb(+srv):// scrub as the migration step.
The observed output contained no connection string at all (agent + production-readiness)migrate-production unchanged — verified programmatically, not by eye: the text from
migrate-production: to EOF is 4555 bytes on both origin/main and HEAD and compares
byte-equal (production-readiness)preview-database runbook covers the seed, the statuses caveat, the tenant-defaults
caveat and how to read the counts — rendered on the docs preview (agent)ROUTE_POLICIES surface in the diffFixed on branch (commit dc3ac2b) — all three are errors in this run's own prose, not in its
behaviour. Two of them are the same mistake I had already corrected once in 04_build/notes.md
and failed to carry back into the artifacts that ship:
0 created, 0 already present. The first run created 168 tenant-default rows and the latest reports 170 already
present — roughly 10 tenants exist in sustentus-preview today. Rewritten as the conditional it
should always have been, and to say that a jump after a new tenant or seed entry is expected.3 created) demonstrates a fourth: action types auto-create on first use, so
the first seed only fills gaps. Replaced with the three explanations and the actual
worth-investigating case.connectDB never sets
autoIndex: false, so compiling the four models the seed needs also builds their declared
indexes against the preview database. Benign and pre-existing — the app runtime does the same on
every boot — but it is the first time CI does it, and index/migration lockstep is exactly what
preview exists to expose. One honest line added rather than a behaviour change.Accepted, recorded not fixed:
tenant-defaults.ts:119-152, nested
loops, two awaits per row). 170 rows ≈ 16s inside a 10-minute job timeout that also covers install
and migration. Fine now; it becomes a question as the demo-storyline work adds tenants. Fix when
it bites: one find per collection per tenant, or a bulkWrite of upserts. Out of scope here —
the spec explicitly excludes changing what db:seed does.sustentus-preview do not revert with it.
No migration is involved and the rows are harmless idempotent baseline data, so there is genuinely
nothing to undo — but that should be said out loud, not assumed.For intake, not this run:
packages/services/src/db/seed/index.ts:13-14 still carries the "statuses, action types" phrasing
this run corrected in scripts/db-seed.ts. Deliberately out of the spec's bounded scope.packages/services/src/db/migrations/1784200000000-view-as-audit-indexes.ts:6 claims the
ViewAsAudit model "registers with autoIndex off (house idiom)" — that model never sets it, and
the only schema that does is user.ts. The house idiom is documented but not implemented.git diff main...HEAD in this checkout reports 162 files / ~11k insertions: local main is stale
(67e3353) while the branch was cut from origin/main (f1c703b). The reviews were run against
origin/main...HEAD — 9 files, 312 insertions. Anyone re-running Verify here should use
origin/main as the base, or they will review a batch of already-merged work as if it were this PR.
Context budget: within the Inputs table. The production-readiness pass read beyond it by design —
that is its job — into src/db/seed/**, db/connection.ts and the model files, to check
boundedness and tenant scoping against source rather than against the spec's claims.
06_ship/output/investor-update.mdWho it's for: Admin — anyone testing a preview URL What shipped: Preview migrations now run the baseline seed too, so preview databases carry reference data and tenant defaults. Why it matters: Refine the Bridge / Q2 Objective 3, Validate Technical Infrastructure & Payout Flow — a green preview check finally means a testable preview.
The first run filled 168 missing rows; the next reported 0 created.
Dig deeper: <merged-PR URL>
06_ship/output/release.md6b4e0ce — Quality ✅, Migrate preview database ✅ (third consecutive run),
Audit database ✅, advisory pipeline checks ✅; Migrate production database correctly skipped
on a pull_request event. No check went red at any point in this run.workflows.json, tenant defaults need a tenant) and a rewritten Reading the
result; technical/development/ci-cd and technical/development/database updated so neither
still describes migrate-preview as migration-only. Verified complete: those three are the only
pages in apps/docs that mention the changed reality.#product-update by the mergedb:seed runs after db:migrate up on both the pull_request and workflow_dispatch
(target: preview) paths — one step inside migrate-preview, which already admits both eventsReference data: 0 created, 13 already present / Tenant defaults: 0 created, 170 already present::error:: naming the database — no || truemigrate-production unchanged — byte-equal from migrate-production: to EOF against
origin/main (4555 bytes both sides)Verify's production-readiness pass found three factual errors in this run's own prose — two of them
the same mistake already corrected once in the build notes and not carried back into the artifacts
that ship. All three were fixed on branch in dc3ac2b before the merge; the detail is in
05_verify/output/verify.md.
Two items were accepted rather than fixed, and are worth carrying forward: the seed's cost is linear
in tenant count and fully sequential (170 rows ≈ 16s today, a question once the demo-storyline work
adds tenants), and rollback is asymmetric — reverting the workflow does not remove the rows already
written to sustentus-preview. Nothing needs undoing there; the rows are idempotent baseline data.
Left deliberately out of scope, for intake: src/db/seed/index.ts still carries the "statuses,
action types" phrasing corrected here in scripts/db-seed.ts, and
migrations/1784200000000-view-as-audit-indexes.ts claims an autoIndex house idiom that only
user.ts actually implements.