Uh oh!
There was an error while loading. Please reload this page.
Seed the demo: the product working on first boot - #75
Merged
Conversation
`pnpm dev` on an empty database now opens on a running system rather than five empty grids — a three-level business-unit tree, thirteen people, a twenty-item role catalog across three position codes, thirty-one duties and six months of dispatched history. History is produced by the dispatcher's own planner (`planDispatch`) rather than by a second period walk, so every period key is the engine's spelling by construction and "standing duties hold zero tasks" is structurally impossible to violate rather than merely absent from the fixture. `last_update_at` is written by a second `mode: 'update'` seed pass, per #32 / PR #64 — an insert can never carry it, and without that pass the "Not moving" view is empty while the seed reports success. Fixes#7 Claude-Session: https://claude.ai/code/session_01SqkTcrxUFci7nqXdbBSe2p Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Keeps every non-designated row inside the fortnight while putting real values in the 7-to-14-day band, so the dashboard's nested >7d / >14d / >30d tiles read 6 / 3 / 2 rather than 3 / 3 / 2. Also corrects the fan-out comment after #72: the reason a seeded assignment does not fan out is the loader's own skipTriggers, not an unbound trigger. Claude-Session: https://claude.ai/code/session_01SqkTcrxUFci7nqXdbBSe2p Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
os-warren
marked this pull request as ready for review
September 1, 2026 09:12
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Sep 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#7
pnpm devon an empty database now opens on a system that is obviously working: a three-level business-unit tree, thirteen people, a twenty-item role catalog across three position codes, thirty-one duties and 186 tasks covering six months of history plus everything currently in flight. 459 rows,errored: 0.The two decisions worth reviewing
History comes out of the dispatcher's own planner, not a second period walk.
demo-history.tscallsplanDispatchtwice — once with a six-month backfill window, once as today's scheduled run — and unions the drafts on(duty, owner, period_key). The card's hardest rule (period keys must come from the engine, never hand-typed) is then satisfied structurally rather than by discipline, and two invariants fall out of the shape instead of being asserted on top of it:planForDutyrefuses them by form before it reads anything else.SKIPSis exported so the test asserts the mechanism, not just that no such row happens to exist.not_active).The seeded rows are literally what the dispatcher would have produced — same keys, same due dates, same lead windows — so a real dispatch run over these duties reproduces them and the unique index absorbs it.
Four task datasets, and the last two are the half that fails silently. Per #32 / #64:
completed_atrides along on a system-context insert,last_update_atcannot —beforeInsertstamps it unconditionally and lifecycle hooks run on the seed path. It takes a second pass inmode: 'update'carrying only that column. Skip it and every row reads as touched at boot: "Not moving" is empty, and the seed reports complete success.The two insert datasets use different external ids, deliberately: dispatched rows are keyed
(duty, owner, period_key)— the dispatch identity itself — because a subject repeats across every period of its duty; assignment fan-out rows are keyed(subject, owner)because they have noperiod_keyat all, and a composite containing a blank collapses to an empty key that matches nothing and re-inserts on every boot.What the screens show
Measured through the running app's REST API as the logged-in dev admin, not inferred from the fixture:
visible_fromanddue_dateEvery
{current_user_id}view is scoped toDev Admin, the accountobjectstack devlogs you in as — a demo whose data belongs entirely to twelve other people renders four screens blank. That row is seeded carrying its natural key and nothing else, so on a real boot the loader's no-op-replay check skips it rather than updating a live credential-bearing account. Verified: email andsys_accountuntouched, 13 users not 14.On a genuinely fresh
.objectstack/data, this app now has no loginable account.plugin-authseeds the dev admin only on a zero-user DB and does it onkernel:ready, which fires after the app seed — so thirteen seeded people pre-empt it, permanently.sign-in401s andbootstrap-statussayshasOwner: true, so the console offers no first-admin flow either.It is a platform gate that asks the wrong question (any
sys_userrow, rather than any credential-bearing account) and it is not fixable fromsrc/data/. Filed upstream as objectstack-ai/objectstack#14157 with both measurements, and tracked here as #73. If you have runpnpm devin a worktree before, the admin is already there and everything just works — that is the path this was verified on, end to end.Verification
All four gates green at
e671ea1, run after the final commit:pnpm validateexit 0 (one expected warning — the hierarchy-security provider, which AGENTS.md documents as this repo's expected state) ·pnpm typecheckexit 0 ·pnpm testexit 0,Test Files 20 passed (20) / Tests 536 passed (536)·pnpm buildexit 0,Artifact: dist/objectstack.json (276.0 KB).test/seed.test.ts(25 tests) boots a real kernel withskipSeedData: falseand reads the rows back from the database — the failures this card exists to prevent (an owner natural key that resolved to nothing, a strippedcompleted_at, alast_update_atoverwritten at boot) all report success on the fixture side.Ablation. Removed both
mode: 'update'datasets from the barrel; predicted the stagnation assertions go red and nothing else. Confirmed on disk before running by the injected marker plusgit diff --stat(1 insertion, 2 deletions), restored by atrap … EXIT INT TERMwith the restore verified as an emptygit diff HEAD. Observed exactly the prediction:Tests 3 failed | 22 passed (25)— "Not moving has 2-3 rows", "stagnation is NOT lateness", and "the replay leaves the stalled rows stalled". The other 22 stayed green, which is the point: without that pass the seed still loads perfectly and only the product's headline signal goes quiet.Idempotence is exercised through the platform's own loader with the boot's own config, asserting
totalInserted === 0andtotalRecords === every declared row, so "inserted nothing" cannot be an empty run.Also filed
maybeSeedDevAdmingates on "any sys_user row", but runs atkernel:ready— so any app that seeds people permanently loses theobjectstack devlogin objectstack#14157 — the dev-admin gate, above.git clone && pnpm devhas no loginable account — make the demo seed opt-in, default off #73 — the duly-side decision: wait for the platform fix, or document the two-step boot.sys_user.primary_business_unit_iddirectly and leavessys_business_unit_memberempty — the platform owns that column and recomputes it #74 (finding) — the seed writessys_user.primary_business_unit_iddirectly, which the platform documents as a projection ofsys_business_unit_member. It lands and survives a real boot, but a future recompute would blank it.Scope
src/data/**andtest/seed.test.tsonly. No change toobjectstack.config.ts,src/objects/,src/hooks/,src/jobs/,src/dashboards/,src/flows/orAGENTS.md. Rebased twice mid-flight, onto #66 (cadence fields are now form-scoped — the two standing catalog items carry none of the five) and onto #72 (record_changeis bound now; a seeded assignment still does not fan out, because the loader's ownskipTriggersexempts it, which is a better reason and a stable one).No real company, person or regulation is named anywhere: the domain is under RFC 2606's reserved
.example, and every citation is an invented internal policy number. Nothing in the seed ranks or compares item counts between people.Generated by Claude Code
Generated by Claude Code