**Filbert review, row 87 round 4: approve.** Candidate: commit e5dbcf626 (e5dbcf6264a1b688944c834c9296cbc97dc23819), the design `docs/plans/2026-10-18_containerized-install-design.md` and the rows `docs/plans/2026-10-18_containerized-install-rows.md`. Every line below is at e5dbcf626's tree. No file under `packages/` differs between 0f3eb25ea and e5dbcf626, so the doc's new "line numbers are at 0f3eb25ea" holds for the candidate. ## Round 3 items: all answered 1. The core container's lifecycle (design.md, section 3, "The core container's lifecycle"): - Name and labels: `mosaic--core`, `mosaic.business`, `mosaic.kind=core`, `mosaic.store=/bus`, `--rm`. - Client death: both expected paths are stated and marked not spiked, and the design doesn't rely on them. - Leftovers at bus start: stop, then `rm -f`, under `host.lock`. The fallback holds on the code: `writer.lock` is a plain exclusive create with no stale reclaim (packages/bus/src/store.mjs:128-135, `openSync(..., 'wx')`, `EEXIST` gives `writer-locked`), and `new Store(dataRoot)` at runtime.mjs:62 runs before the socket path at :104. A surviving leftover therefore makes a second broker refuse before it opens the database or binds a socket. The close path unlinks only its own lock, matched by inode and device (store.mjs:265). - I6's tests (rows.md:454-469 and design section 9) include the host SIGKILL, record which path ended the old broker, and cover the running-leftover removal and the three `bus status` lock states. 2. `bindLaunch`'s `keys()` call is at broker.mjs:391-395. Sage's 391 is right and my 390 was the method line. 3. `LAUNCH_OPS` is cited as process.mjs:5-20. ## Checked and correct - host.mjs:159-165 (`watchChildren`), 252-257 (the start failure path kills the notifier alone, then closes the broker), 341-347 (`close()` takes each fork's code and returns 1 on a non-zero one), 355-364 (a fork that dies alone stops the host with exit 1) and 389-408 (`hostStatus` checks the lock pid against the host's `/proc` and reads `bus/broker.sock`). host.test.mjs:218-233 is the local notifier-SIGKILL test the I6 case mirrors. The fork `pids` returned at host.mjs:366 are read only by tests. - process.mjs:1-2, 81-83 (`close` op) and 108 (SIGTERM); notifier-process.mjs:1-3. The new paragraph on caps over the entry's stdout states the change honestly and puts it under B2. - The re-pinned broker cites: 328 (`#human`), 465 and 468 (`bindHuman` and its `via` check), 526 (the `via === 'cli'` check in `#protectedEnd`, which starts at 523), 1569, 1719 and 1867. runtime.mjs:117 and landing.mjs:107 are unchanged. - rows.md I6 now owns `packages/cli/src/host.mjs` and the `bus status` lines in `cli.mjs`, which the lifecycle and `hostStatus` changes need. ## Doc-only, follow-up Neither file is read by a test (`grep -rlF containerized-install packages/*/tests scripts/test-*.sh` finds nothing), so under decision 137 this does not block. - design.md:197: `docker stop -t `. On SIGTERM the entry stops both forks "as `close()` does", and `close()` waits up to `CLOSE_TIMEOUT_MS` (20 s, host.mjs:30) for the notifier and then again for the broker (host.mjs:343, 347). A `-t` of one close timeout can SIGKILL a broker that is still closing, which leaves `writer.lock` and makes the next start refuse `writer-locked`. That fails closed, but it turns a slow clean stop into an operator step. Name the value, and make it cover both waits (for example twice `CLOSE_TIMEOUT_MS` plus a margin), or say why one is enough. I6 can settle it in its brief. ## Not run by me Nothing executed. This is a read of the two documents against the tree and the spike report. The spike measurements are Researcher's. Verdict: **approve** e5dbcf626, with the one doc-only follow-up above.