Background terminals can settle a live process after a failed signal
Problem
The background terminal manager treats every ChildProcess error event as a spawn failure. However, a process that has already started can emit error when a later signal operation fails, without exiting.
Currently that event marks the entry exited/failed and closes its full-log streams. The actual process may still run and produce output, while consumers have already received completion. Because the listener uses once, another such error also has no remaining manager listener.
Reproduction
Tested against main f6b49ae on Node 22.22.3, Windows. A real TerminalManager/ManagedRuntime starts a Node process that waits on an owned release file. After it prints a readiness marker, the test temporarily makes Node's native kill binding return libuv EPERM and calls the real child.kill("SIGTERM") method. Node emits kill EPERM; the child remains alive.
This is controlled fault injection at the native signal binding, not a claim that an ordinary Windows session naturally reproduced an OS permission denial.
Observed before the fix:
{"signalAccepted":false,"alive":true,"status":"failed","errorText":"kill EPERM","errorListenersBefore":1,"errorListenersAfter":0}
Expected: retain a running entry and error listener until real exit/close, preserve the bounded diagnostic, and capture output generated after the failed signal. A genuine spawn failure must still settle failed without exposing its errno as an exit code.
Scope
The affected owner is extensions/background-terminals/src/manager.ts. Its direct-signal fallback uses child.kill, so this is a valid lifecycle event contract. This report does not ask to change signal escalation, taskkill budgets, process-tree authority, tool schemas, or the shared process helper.
Related historical issue #408 / PR #412 concerns a different boundary: whether a timed-out taskkill helper permits a direct-shell fallback. This report concerns how the manager classifies an error emitted by the already-spawned child.
Background terminals can settle a live process after a failed signal
Problem
The background terminal manager treats every ChildProcess
errorevent as a spawn failure. However, a process that has already started can emiterrorwhen a later signal operation fails, without exiting.Currently that event marks the entry exited/failed and closes its full-log streams. The actual process may still run and produce output, while consumers have already received completion. Because the listener uses
once, another such error also has no remaining manager listener.Reproduction
Tested against main f6b49ae on Node 22.22.3, Windows. A real TerminalManager/ManagedRuntime starts a Node process that waits on an owned release file. After it prints a readiness marker, the test temporarily makes Node's native kill binding return libuv EPERM and calls the real
child.kill("SIGTERM")method. Node emitskill EPERM; the child remains alive.This is controlled fault injection at the native signal binding, not a claim that an ordinary Windows session naturally reproduced an OS permission denial.
Observed before the fix:
{"signalAccepted":false,"alive":true,"status":"failed","errorText":"kill EPERM","errorListenersBefore":1,"errorListenersAfter":0}Expected: retain a running entry and error listener until real exit/close, preserve the bounded diagnostic, and capture output generated after the failed signal. A genuine spawn failure must still settle failed without exposing its errno as an exit code.
Scope
The affected owner is
extensions/background-terminals/src/manager.ts. Its direct-signal fallback useschild.kill, so this is a valid lifecycle event contract. This report does not ask to change signal escalation, taskkill budgets, process-tree authority, tool schemas, or the shared process helper.Related historical issue #408 / PR #412 concerns a different boundary: whether a timed-out taskkill helper permits a direct-shell fallback. This report concerns how the manager classifies an error emitted by the already-spawned child.