docs: brief the bus host start follow-up (#1605, Dewey 28319 note a and Darkwing's error event)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-13 18:02:57 -05:00
co-authored by Claude Opus 5.5
parent 8b58cb7e47
commit c77ef50aba
@@ -233,3 +233,72 @@ paths the copy holds.
Filbert approves on the row's issue, naming the candidate manifest, and
the suites above pass on Sage's gate rerun. The scripts take effect on
the live setup only at the next copy and re-pin under Mos's go.
## Bus host start: release after the broker's reply, and the child `error` event
### Problem
Issue #1605. Row 106 (#1600, landed 8b58cb7e4) made `startHost` in
`packages/cli/src/host.mjs` give `host.lock` back when the broker's
fork or boot send throws. Two gaps were left, both present before row
106 and both out of its brief.
- Dewey's note a (#1600 comment 28319). After the broker replies, these
steps can still throw: the notifier's `fork` and its `start` send,
`reader.cap` when no reader matches the business (`ready.readers.find`
returns undefined), the `rmSync` of `host.json` and `writeHostState`.
A throw from any of them leaves `host.lock` held and a live broker
with no host. The next start finds the stale lock and moves it aside,
but the orphan broker keeps running.
- Darkwing's point (row 106 build.md). Neither child has an `error`
listener in `startHost`. An asynchronous send failure emits `error` on
the child, and with no listener the process ends before `release()`
runs.
### Owner and reviewer
Owner: Darkwing. Reviewer: Dewey, who reviewed rows 103 and 106 on
this code. Sage picked Dewey over decision 130's default (Filbert) for
continuity, and because Filbert has row 109.
### Files owned
- `packages/cli/src/host.mjs`, `startHost` only
- `packages/cli/tests/host.test.mjs`, the host start cases only
- `agents/darkwing/work/queue-<row>/`
No README change. Row 110 owns `packages/cli/README.md` next.
### What ships
- Every step after the broker's reply and before `startHost` returns
runs under one failure path. That path stops the notifier if one
started, closes the broker the way the notifier failure path already
does, waits for both to end, and calls `release()`. A missing reader
for the business refuses with a `CliError` naming the business,
instead of a TypeError.
- During start, an `error` event on either child is handled: it fails
the start through the same path, never ending the process with the
lock held. The packet says what an `error` after a successful start
does and why. Changing that behaviour is out of scope unless the
current one leaves the lock held.
- Tests: a notifier fork that throws, a notifier send that throws, a
boot reply with no reader for the business, and a `writeHostState`
that throws each leave no `host.lock` and no live broker. A child
`error` during start fails the start and leaves no `host.lock`.
Each new check is shown to fail on a mutant that removes it.
Suites: the cli node suite and every `scripts/test-*.sh`.
### Out of scope
The third-racer window. `stopHost`, `hostStatus`, `host.json`'s
format and the broker itself.
### Gate
Dewey approves on the row's issue, naming the candidate manifest, and
the suites above pass on Sage's gate rerun. Decision 109 notices go to
Mos before and after landing. It goes live only with a new copy, a
re-pin and a bus restart under Mos's go.