From 36d62e048c1acf627a16e90705e90d1571f906d8 Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Wed, 18 Mar 2026 14:26:34 -0400 Subject: [PATCH] fix: kill non-persistent bg processes between auto-mode units (#1217) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .../extensions/bg-shell/process-manager.ts | 13 +++++++++++++ src/resources/extensions/gsd/auto-post-unit.ts | 7 +++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/resources/extensions/bg-shell/process-manager.ts b/src/resources/extensions/bg-shell/process-manager.ts index 95ee6ccd9..42ff4bb3d 100644 --- a/src/resources/extensions/bg-shell/process-manager.ts +++ b/src/resources/extensions/bg-shell/process-manager.ts @@ -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 { if (!bg.alive) return true; await new Promise((resolve) => { diff --git a/src/resources/extensions/gsd/auto-post-unit.ts b/src/resources/extensions/gsd/auto-post-unit.ts index f3ee32961..e8114f3a9 100644 --- a/src/resources/extensions/gsd/auto-post-unit.ts +++ b/src/resources/extensions/gsd/auto-post-unit.ts @@ -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 }