proc.kill() only signals the sh -c wrapper; the claude child survives as an
orphan that inherited our stdout/stderr pipes, blocking the stream drain until
it exits (observed: a 600s spawn timeout stretching to 1431s and tripping bun's
per-test timeout with no result). On timeout, cancel the stdout reader; race
the stderr drain against child exit + 5s grace. Streamed transcript lines
survive the cancel, so callers still get their evidence.
Regression test: test/session-runner-timeout.test.ts (fake claude spawns a
pipe-holding orphan; fails in 30s without the fix, passes in 8s with it).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>