An MCP host that kills the launcher chain within the server's first ~100ms
while keeping the stdio pipes open (config probe, cancelled request, startup
timeout; Rust hosts that kill a child without dropping its stdio handles) left
the server orphaned: it booted already reparented to init, so the PPID
watchdog's "ppid changed" baseline was captured as 1 and could never fire, and
stdin never EOF'd. The process lingered — idle, ~30MB — until the host itself
exited, accumulating one per abandoned launch (the pile-up reported in #1185).
Reproduced on released 1.2.0/macOS: SIGKILL the launcher at +50ms → permanent
orphan; at +150ms the old late baseline had already run and reaped it.
Three-part fix:
- Capture process.ppid at the earliest line of the CLI entry (early-ppid.ts)
and use it as every watchdog baseline, shrinking the blind window to the few
ms before our first JS runs.
- Thread the real host pid down the bundled path: the npm shim and the
standalone sh launcher set CODEGRAPH_HOST_PPID (an outer launcher's value
wins), so the watchdog polls the host directly. Previously only the
--liftoff-only relaunch set it, leaving the entire npm/standalone install
base with hostPpid=null.
- Never-initialized backstop (startup-handshake.ts): a serve --mcp that
receives no MCP traffic for CODEGRAPH_STARTUP_HANDSHAKE_TIMEOUT_MS (default
15min, 0 disables) shuts down — the catch-all for a kill landing in the
residual pre-JS window. Disarmed on the first byte, so a quiet-but-live
session is never touched.
Also scrub CODEGRAPH_HOST_PPID from the detached daemon's env — it has no host,
and a stale pid must not leak into anything it spawns.
Validated end-to-end on the built bundle: the +50ms early-kill orphan is now
reaped while the host still holds the pipes open, and all six normal
lifecycle paths (clean close, SIGTERM/SIGKILL child, host exit/SIGKILL,
fd-holding adversarial host) stay clean. New coverage in
startup-handshake.test.ts, mcp-startup-orphan.test.ts, and npm-shim.test.ts.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>