You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Follow-up to #139 (fixed by #182). That PR addressed bug 2 — a failed teardown no longer crashes the run post-merge, degrading to a worktree-teardown-degraded journal event instead. This issue tracks the underlying bug 1: the race itself. A process spawned inside a session can outlive the session and keep writing into the worktree while git is deleting it.
Mechanism
_integrate_unit runs ~2s after session-end. Nothing on that path verifies the session's process tree is dead — only its tmux window:
tmux kill-window SIGHUPs the pane's process group, but a backgrounded job (pytest &, a subagent-spawned runner) sits in its own process group and never receives it; nohup/disown/double-forked children escape entirely.
Tolerable but real: a worktree-teardown-degraded warning, a leftover dir under the gitignored run dir (reclaimed by trim/clean), and in the worst case a straggler still consuming resources or writing into a reclaimed path long after the story completed. On the #139 report this fired on 2 of 2 backend stories in one sweep, so it is not rare under test-heavy workloads.
Proposed direction
Extend the #157 verified-kill seam rather than adding a new mechanism:
Only then let the engine proceed to merge + teardown.
Platform notes: descendant walk needs win32 parity through the process-host seam (pgrep -P has no direct equivalent; #157's fake-child/kill-0 test pattern applies). The opencode_http adapter keeps its own teardown copy and needs the same treatment separately — its sessions have no tmux window at all, making the process-tree wait the only teardown signal there.
Acceptance sketch
With a session that backgrounds a writer outliving its turn (the #139 repro), teardown blocks up to teardown_grace_s until the writer is dead, the worktree removes cleanly, and no worktree-teardown-degraded event fires.
Follow-up to #139 (fixed by #182). That PR addressed bug 2 — a failed teardown no longer crashes the run post-merge, degrading to a
worktree-teardown-degradedjournal event instead. This issue tracks the underlying bug 1: the race itself. A process spawned inside a session can outlive the session and keep writing into the worktree while git is deleting it.Mechanism
_integrate_unitruns ~2s after session-end. Nothing on that path verifies the session's process tree is dead — only its tmux window:tmux kill-windowSIGHUPs the pane's process group, but a backgrounded job (pytest &, a subagent-spawned runner) sits in its own process group and never receives it;nohup/disown/double-forked children escape entirely.GenericAdapter.kill) only escalates to pid-level force-kills when the window outlivesteardown_grace_s. In the Worktree teardown race crashes the run after a successful merge #139 fingerprint the window dies on the first probe, sowindow_pane_pidsis never harvested and descendants are never checked. A session that ends "cleanly" is exactly the one whose stragglers go unnoticed.CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1in the claude profile ([BUG] Dev session that yields its turn to await a background Agent (sub-agent) tool is ended by the Stop handler, stranding the sub-agent #109) prevents claude's harness-managed background tasks specifically — it does not cover other adapters, nor processes the CLI's shell detached on its own.Consequence (post-#182)
Tolerable but real: a
worktree-teardown-degradedwarning, a leftover dir under the gitignored run dir (reclaimed by trim/clean), and in the worst case a straggler still consuming resources or writing into a reclaimed path long after the story completed. On the #139 report this fired on 2 of 2 backend stories in one sweep, so it is not rare under test-heavy workloads.Proposed direction
Extend the #157 verified-kill seam rather than adding a new mechanism:
window_pane_pidsplus their descendant tree before the first kill strike, while the window is provably alive (same pid-reuse safety argument session-end journaled 2h19 after session_timeout_min fires when the session is wedged inside a tool call #157 already established — harvesting after death risks signalling reused pids, and orphaned descendants reparent away from the pane and become unfindable).teardown_grace_s; force-kill stragglers through the process host.Platform notes: descendant walk needs win32 parity through the process-host seam (
pgrep -Phas no direct equivalent; #157's fake-child/kill-0 test pattern applies). Theopencode_httpadapter keeps its own teardown copy and needs the same treatment separately — its sessions have no tmux window at all, making the process-tree wait the only teardown signal there.Acceptance sketch
With a session that backgrounds a writer outliving its turn (the #139 repro), teardown blocks up to
teardown_grace_suntil the writer is dead, the worktree removes cleanly, and noworktree-teardown-degradedevent fires.