From 36c41055e24feeb6c06a671c4761c5962756a68f Mon Sep 17 00:00:00 2001 From: Test Date: Sat, 29 Aug 2026 09:33:47 +0000 Subject: [PATCH] =?UTF-8?q?fix(browse):=20Chromium-crash=20exit=20is=20dae?= =?UTF-8?q?mon-only=20=E2=80=94=20embedded=20launches=20never=20kill=20the?= =?UTF-8?q?ir=20host?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit handleChromiumDisconnect unconditionally process.exit()ed. Correct for the standalone daemon (its supervisor/user must notice); suicidal when a TEST launches BrowserManager in-process: a mid-suite Chromium death exited the whole bun shard with no terminal summary — the exact truncation class the strict runner flags (observed live: CI shard 1 on eb233299 died at cache-concurrent-refresh right after a daemon-spawning gate test; with this fix the same pairing runs to completion and REPORTS instead of dying). The standalone entrypoint opts in via markDaemonProcess() under server.ts's import.meta.main gate — the same embedder contract its signal handlers already use (gbrowser phoenix keeps its own handlers). Embedded contexts now get the disconnect log line and continue. Co-Authored-By: Claude Fable 5 --- browse/src/browser-manager.ts | 24 ++++++++++++++++++++++-- browse/src/server.ts | 6 +++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/browse/src/browser-manager.ts b/browse/src/browser-manager.ts index 60c478842..5a3800718 100644 --- a/browse/src/browser-manager.ts +++ b/browse/src/browser-manager.ts @@ -186,12 +186,32 @@ export async function resolveDisconnectCause(browser: Browser | null): Promise<' } /** - * Headless `launch()` disconnect handler. Exits 0 on clean user-quit, 1 on - * crash. Inlined into the launch() body via a one-line dispatch so + * Exit-on-disconnect is DAEMON-ONLY semantics. The standalone server + * entrypoint opts in via markDaemonProcess() (under its import.meta.main + * gate, same contract as its signal handlers); embedders — gbrowser + * phoenix, and every test that launches a BrowserManager in-process — + * must never have a Chromium crash process.exit() their HOST. Observed + * live before this flag: a test-launched browser died mid-suite and the + * exit(1) killed the whole bun shard with no terminal summary (the + * truncation class the strict runner exists to catch). + */ +let daemonProcess = false; +export function markDaemonProcess(): void { + daemonProcess = true; +} + +/** + * Headless `launch()` disconnect handler. In the standalone daemon: exits 0 + * on clean user-quit, 1 on crash. Embedded contexts get the log line only. + * Inlined into the launch() body via a one-line dispatch so * browser-manager's flow stays grep-friendly. */ export async function handleChromiumDisconnect(browser: Browser | null): Promise { const cause = await resolveDisconnectCause(browser); + if (!daemonProcess) { + console.error(`[browse] Chromium disconnected (${cause}) in an embedded context — host process continues.`); + return; + } if (cause === 'clean') { console.error('[browse] Chromium closed cleanly (user-initiated quit). Server exiting (0).'); process.exit(0); diff --git a/browse/src/server.ts b/browse/src/server.ts index a31d3c83a..f0823cc97 100644 --- a/browse/src/server.ts +++ b/browse/src/server.ts @@ -13,7 +13,7 @@ * Port: random 10000-60000 (or BROWSE_PORT env for debug override) */ -import { BrowserManager } from './browser-manager'; +import { BrowserManager, markDaemonProcess } from './browser-manager'; import { handleReadCommand, hasOutArg } from './read-commands'; import { handleWriteCommand } from './write-commands'; import { handleMetaCommand } from './meta-commands'; @@ -1394,6 +1394,10 @@ async function handleCommand(body: any, tokenInfo?: TokenInfo | null): Promise activeShutdown?.()); // SIGHUP (terminal hangup): with handleSIGHUP:false at the three launch