From c77ef50aba38e360fce20ec83a37ee02b8076327 Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Tue, 13 Oct 2026 18:02:57 -0500 Subject: [PATCH] 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 --- docs/plans/2026-10-13_landing-follow-ups.md | 69 +++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/docs/plans/2026-10-13_landing-follow-ups.md b/docs/plans/2026-10-13_landing-follow-ups.md index 001cba498..19c939e10 100644 --- a/docs/plans/2026-10-13_landing-follow-ups.md +++ b/docs/plans/2026-10-13_landing-follow-ups.md @@ -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-/` + +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.