fix(showcase): seed all five projects through the FSM + surface seed outcomes in the boot banner (#3415)#3435
Merged
Merged
Conversation
…ed outcomes in the boot banner (#3415) The project seed wrote terminal statuses directly on insert, but project_status_flow gates inserts to initialStates: ['planned'] — so 4/5 projects, 9/10 master-detail tasks and 3/3 memberships were rejected on every boot, silently: SeedLoader's logs sit under the default warn level AND inside serve's boot-quiet stdout window. Fixture: seed projects in three phases. Phase 1 inserts all five as 'planned' (explicitly — seed inserts do not apply select defaults) with mode 'ignore' so replays leave walked rows untouched; phases 2-3 then walk them along legal transitions (planned→active→on_hold/completed), doubling as a live FSM demo. A new 'completed → active' reopen transition (a real PM affordance — cancelled could already be revived) keeps the walk legal on replay. Fresh boot: 5 projects across four statuses, 10 tasks, 3 memberships, zero rejections; replay preserves walked states. Surfacing: AppPlugin stashes per-boot seed counters on the kernel ('seed-summary'); the serve banner prints 'Seeds: X inserted · Y updated · Z skipped', escalating to a yellow '⚠ … N REJECTED' line when rows dropped — before the fix that line read '114 inserted · 30 REJECTED', after it reads '130 inserted · 6 updated'. Gates: seed.test.ts now statically replays every FSM-gated object's seed phases (two rounds — fresh boot AND replay) against initialStates and transitions; format.seed-summary.test.ts covers the banner contract. Both proven red against the old fixture / a dead-end terminal state. Known pre-existing follow-up (#3434): mode:'insert' datasets (memberships) duplicate on every replay boot — masked until now because the rows never inserted at all. Closes #3415 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 30 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
This was referenced Jul 24, 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 free
to 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 #3415 — both halves: the self-contradictory fixture AND the surfacing gap that let it ship silently.
Fixture: walk the FSM instead of violating it
project_status_flowgates inserts toinitialStates: ['planned']; the seed wrote terminal statuses directly, so 4/5 projects, 9/10 tasks (master-detail), 3/3 memberships were rejected on every boot. Now the seed runs in three phases (same-object datasets execute in declaration order — stable topological sort):planned, explicitly (seed inserts do not apply select defaults — the Accounts seed documents this), withmode: 'ignore'so replays leave walked rows untouched.planned → activefor the four that move on.active → on_hold(Compliance Audit) andactive → completed(Legacy Sunset).The walk doubles as a live demo of the state machine the seed used to violate. A new
completed → activereopen transition (a real PM affordance —cancelledcould already be revived viaplanned) keeps the replay walk legal instead of dead-ending;initialStatesis untouched, so the #3165 entry gate is exactly as strong as before.Surfacing: a Seeds line in the boot banner
SeedLoader's own logs are double-swallowed (info < default warn level, and serve's boot-quiet stdout window). Following the
seededAdmin/automation-summary precedent: AppPlugin stashes per-boot counters on the kernel (seed-summaryservice, accumulating across apps), serve reads them back afterrestoreOutput(), and the banner prints:Seeds: 130 inserted · 6 updated(dim)⚠ Seeds: 114 inserted · 30 REJECTED — run with OS_LOG_LEVEL=info to see each reason(yellow)That yellow line is literally what the old fixture prints under this banner — it could never have shipped broken with the line in place.
Verification
active×2 / on_hold / completed / planned), 10 tasks, 3 memberships; bannerSeeds: 130 inserted · 6 updated— the 6 updates are exactly the FSM walk. Before the fix on the same runtime: 1/1/0 and⚠ 114 inserted · 30 REJECTED.3 inserted · 123 updated · 10 skipped).seed.test.tsstatically replays every FSM-gated object's seed phases — two rounds, fresh boot AND replay — againstinitialStates/transitions(red on the old fixture and red without the reopen edge);format.seed-summary.test.tscovers the banner contract (3 cases).@objectstack/runtimeand@objectstack/clisuites pass.Known pre-existing follow-up
#3434 —
mode: 'insert'datasets (memberships) duplicate on every replay boot (3→6). Masked until now because the rows never inserted at all; needs compositeexternalIdor an explicit key, filed separately.Console UI assets aren't present in a fresh worktree (
objectui:refreshartifact), so browser-level kanban verification ran on DB counts + the earlier dogfood evidence that the board renders correctly the moment status-matched rows exist; this PR touches no renderer code.🤖 Generated with Claude Code