mirror of
https://github.com/dtzp555-max/ocp.git
synced 2026-07-21 21:15:09 +00:00
Folds in the independent review's remaining nit — and, in proving the nit's fix, uncovers
two defects in the test suite itself.
## The nit (latent M1b, second costume)
`_cancelBooting` kills BY NAME, but the tmux session only EXISTS once `bootPane` has run —
and `bootPane` is queued on a microtask. So a caller doing `refill()` then `drain()` in the
SAME synchronous block leaves `_cancelBooting` with nothing to kill (a no-op); it bumps the
generation, and the boot microtask then CREATES the session, succeeds, and — under the old
bare `return` on a stale generation — walked away from a LIVE authenticated `claude` that
nothing owns. Reproduced:
reverted: drain() kills nothing (no session yet) -> boot creates it -> ORPHAN: ['p1']
fixed : drain() kills nothing (no session yet) -> boot creates it -> boot kills it -> []
Not reachable from any current call site, so this is defense-in-depth — but ADR 0008 and the
reap-tick comment in server.mjs BOTH explicitly contemplate a boot-time pre-warm, which is
exactly the shape that reaches it. Killing an already-dead session is a harmless no-op, so
the fix is idempotent whichever way the race lands.
## Defect 1 in the suite: async tests were never awaited (44 of them)
Writing the regression guard exposed this. `test()` called `fn()`, got a promise back, and
IMMEDIATELY printed ✓ and incremented `passed` — without awaiting it. For all 44 tests written
as `test("...", async () => {...})`:
- ✓ meant "did not throw SYNCHRONOUSLY", not "passed";
- a failed assertion escaped as an unhandled rejection, crashing the process (CI stays red on
the non-zero exit) but never being COUNTED — so the summary could print "0 failed" and be wrong.
The suite's headline number was therefore not evidence for ANY async test, including this PR's own
M1a/M1b guards. `test()` now settles an async body before counting it, and the summary awaits them.
## Defect 2, exposed the instant defect 1 was fixed: a false guard
`"a boot that resolves AFTER a drain kills its own pane ... no orphan process left behind"` asserted
`killed.length === 1` — i.e. that kill was CALLED once. But `_cancelBooting`'s kill-by-name on a
not-yet-existent session is a NO-OP that still increments that counter. So "kill was called once" and
"a live session is orphaned" were both true at the same time: a test named for the absence of an
orphan was passing while the orphan was present. Now asserts LIVENESS (`live.size === 0`) — the only
honest question.
## Evidence
fix present : 295 passed, 0 failed, exit 0
fix reverted: 293 passed, 2 failed <- BOTH liveness guards fire (the old kill-count guard did not)
Also: `dropped`'s doc comment now lists `cancelled` (a cancelled in-flight boot lands there via _drop).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VqgWJcjxrjjL9L9SkpZyXR