You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs(cli): document the two ipc messages and the parent-spawn pattern
Adds a `Waiting for the boot from a parent process` section to the `os dev`
page: what each message means, the spawn-with-ipc snippet, the asymmetry
(`os dev` consumes `objectstack:listening` and relays only the settle
message), and what `objectstack:seed-settled` promises — including why it is
sent on suppressed boots with a reason rather than withheld.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TSf4DV7ziu4V5j73e46b7c
`os serve` now announces **`objectstack:seed-settled`** on its existing ipc channel when this boot's seeding has come to rest, and `os dev` forwards it to its own parent process when one holds the channel. A script that spawns a dev server can finally wait for the boot to finish without reading the child's output.
6
+
7
+
`✓ Server is ready` is true about the HTTP server and says nothing about the app. Seeding races a soft budget (`OS_INLINE_SEED_BUDGET_MS`, default 8s) and past it finishes in the background, so the banner can be a minute ahead of the seed's own result — measured downstream at **82 seconds of silence after the banner, then 120 `ERROR` lines**. The same command on the same corpus settles before the banner on a machine where the seed fits its budget, so the defect is invisible on exactly the boxes that would have caught it. Everything that distinguishes the two cases arrives on the child's inherited stdio, and reading that costs the boot its TTY.
8
+
9
+
-**The producer is not new.**`@objectstack/runtime` already declares every seed source and settles it at the moment its boot-time write is done, publishing the tally under `@objectstack/spec`'s `seed-settlement` contract. This is the hop outward: the CLI subscribes to two hooks the kernel already fires and reads a snapshot it already publishes. No service is registered and no tally is mutated — the contract is read-only by design.
10
+
-**Sent once, and never before `objectstack:listening`.** Seeding that settles during `runtime.start()` is latched and released after the bound port is published, so a parent that waits for the listening message and only then listens for the settle cannot miss it.
11
+
- ⛔ **Keyed on `inFlight`, not `pending`.** Multi-tenant replay and `skipSeedData` register a seed source and deliberately never run it, keeping `pending` above zero for the life of the process. A `pending`-keyed message would never be sent on those boots, and its absence would be indistinguishable from a boot still writing — the same ambiguity this closes, one level up. Those boots get the message with `suppressed` reasons attached instead, so a consumer can say *why* no rows landed.
12
+
-**Failure settles too.** A seed that failed has still come to rest; withholding there would recreate the hang. `ok` is a verdict on the per-source counts the boot recorded, and the message carries those counts.
13
+
-**The over-budget banner no longer omits seeding.**`Seeds:` is fed by outcomes recorded when a load *finishes*, so past the budget the row was ABSENT and the transcript was byte-identical to an app that declares no seeds — which is how the defect hid. It now reads `pending — N sources still writing`, with a line saying seeding continues in the background; suppressed sources are named rather than reported as pending.
14
+
15
+
⛔ An ipc channel is **not** made a requirement of either command: `process.send` is undefined under an ordinary terminal boot, both sends are no-ops there, and no byte of that transcript changes. Nothing in the existing `objectstack:listening` publication moves.
16
+
17
+
Note that `os dev` consumes `objectstack:listening` itself (it is how the bound-port readout and the MCP connect hint learn the real port) and relays only `objectstack:seed-settled`. Spawn `os serve` directly to receive both in one place.
Copy file name to clipboardExpand all lines: content/docs/deployment/cli.mdx
+61Lines changed: 61 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -234,6 +234,67 @@ audit) lands there instead of the business DB (ADR-0057). Opt out with
234
234
`OS_TELEMETRY_DB=0`, or point it elsewhere (any mode, including `serve`)
235
235
with `OS_TELEMETRY_DB=<path>`.
236
236
237
+
##### Waiting for the boot from a parent process
238
+
239
+
`✓ Server is ready` is true about the **HTTP server**, and deliberately says
240
+
nothing about the app's data. Seeding races a soft budget
241
+
(`OS_INLINE_SEED_BUDGET_MS`, default `8000`); when it runs long the kernel
242
+
starts anyway and the rest of the seed finishes **in the background** — so the
243
+
banner, and anything that waits for it, can be a minute ahead of the seed's own
244
+
result. On a machine where the seed fits its budget the same command settles
245
+
before the banner. Both are normal, and which one you get depends on the box.
246
+
247
+
So a script that spawns a dev server and wants to act **after the boot has come
248
+
to rest** should not wait on the banner, and should not need to read the child's
249
+
output at all. Spawn with an `ipc` channel and wait for a message:
250
+
251
+
| Message | Sent by | Means |
252
+
|---|---|---|
253
+
|`objectstack:listening`|`os serve`| The HTTP server is bound. Carries `{ port, url }` — the port actually bound, which in dev may differ from the one requested. |
254
+
|`objectstack:seed-settled`|`os serve`| Nothing is still seeding. Carries `{ ok, suppressed, sources }`. Sent once per boot, always **after**`objectstack:listening`. |
0 commit comments