fix: kill non-persistent bg processes between auto-mode units (#1217)

Background processes spawned during one task (e.g., Vite dev servers
for browser-based verification) were not cleaned up when the unit
completed. The orphaned server kept the port bound, causing the next
unit's dev server launch to fail or conflict. This led to stuck-loop
anomalies, cost spikes from timeout recovery retries, and port conflicts.

Added killSessionProcesses() to bg-shell process-manager — kills all
alive, non-persistent processes using SIGTERM. Called in auto-post-unit
after pruneDeadProcesses(). Processes with persistAcrossSessions: true
are preserved.

Fixes #1209 (orphaned processes part; the subagent bundled-extension-paths
bug is already fixed on main since a2a701b1)
This commit is contained in:
Tom Boucher 2026-03-18 14:26:34 -04:00 committed by GitHub
parent 0bce301cba
commit 36d62e048c
2 changed files with 18 additions and 2 deletions

View file

@ -375,6 +375,19 @@ export function cleanupAll(): void {
processes.clear();
}
/**
* Kill all alive, non-persistent bg processes.
* Called between auto-mode units to prevent orphaned servers from
* keeping ports bound across task boundaries (#1209).
*/
export function killSessionProcesses(): void {
for (const [id, bg] of processes) {
if (bg.alive && !bg.persistAcrossSessions) {
killProcess(id, "SIGTERM");
}
}
}
async function waitForProcessExit(bg: BgProcess, timeoutMs: number): Promise<boolean> {
if (!bg.alive) return true;
await new Promise<void>((resolve) => {

View file

@ -202,10 +202,13 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
}
}
// Prune dead bg-shell processes
// Prune dead bg-shell processes and kill non-persistent live ones.
// Without killing live processes between units, dev servers spawned during
// one task keep ports bound, causing conflicts in subsequent tasks (#1209).
try {
const { pruneDeadProcesses } = await import("../bg-shell/process-manager.js");
const { pruneDeadProcesses, killSessionProcesses } = await import("../bg-shell/process-manager.js");
pruneDeadProcesses();
killSessionProcesses();
} catch {
// Non-fatal
}