v1.40.0.2 fix(browse): Cmd+Q on managed Chromium stops triggering supervisor respawn
Summary
Three `browser.on('disconnected')` handlers in `browse/src/browser-manager.ts` exited with non-zero codes for every disconnect — including user-initiated Cmd+Q. Process supervisors that consume our exit code (gbrowser's `gbd` HealthMonitor) treated this as a crash and respawned the whole stack on exponential backoff. Result: user closes the browser, window pops back 1s later, then 2s, then 4s.
This PR adds `resolveDisconnectCause(browser)` that reads the underlying ChildProcess's `exitCode` + `signalCode` (waiting up to 1s for the exit event if `disconnected` fires first). The three handlers consume it:
- `launch()` (headless): clean → 0, crash → 1 (was always 1)
- `launchHeaded()` (headed): clean → 0, crash → 2 (preserved). `onDisconnect()` cleanup still fires in both cases.
- `handoff()` (mid-session re-launch): clean → 0, crash → 1.
Crash-recovery is preserved: SIGSEGV / SIGKILL / OOM / non-zero exits all still exit non-zero and supervisors restart with backoff. Only the clean Cmd+Q path is new.
Test plan
- [x] `bun test browse/test/browser-manager-unit.test.ts` — 9 tests pass (7 new for resolver + 2 existing)
- [x] `bun test browse/test/browser-manager-custom-chromium.test.ts` — still green
- [ ] Manual: macOS, launch headed Chromium via `bun run dev`, Cmd+Q the window, verify CLI exits 0 (`echo $?`)
- [ ] Manual: SIGKILL the Chromium PID, verify CLI exits 2 (headed) so supervisor still restarts
Related
- Companion gbrowser-side PR (this branch): https://github.com/garrytan/gbrowser/pull/23
🤖 Generated with Claude Code
Bundled into #1629 as the v1.42.1.1 fix wave, along with PR #1617 and an additional exit-code propagation fix that Codex caught: this PR's launchHeaded() disconnect handler computes exitCode correctly but server.ts:684 hardcoded activeShutdown?.(2), dropping the resolved 0 before it could reach the shutdown pipeline. The bundled PR widens BrowserManager.onDisconnect to accept exitCode, threads it through launchHeaded (this.onDisconnect(exitCode)), and updates the server.ts hookup to (code) => activeShutdown?.(code ?? 2). New regression test pins the full propagation path.
Leaving this PR open until #1629 merges so we have a working fallback. Will close with a final "merged via #1629" once landed.
Superseded by v1.42.2.0 (#1629). Closing per the #1642 follow-up note.