Repository navigation
Description
Summary
A Python process started by a Claude reviewer's Bash tool kept running for about 4 days after its review run ended. It was using a full CPU core and had been reparented to launchd (PPID 1). Its working directory was the reviewer's workbench checkout, which had already been deleted. Neither cr's process-group kill nor its claude bg job cleanup could reach it.
Timeline (from the reviewer's Claude session transcript)
- The reviewer runs
python3 - <<'EOF' ... EOFagainstpackage-lock.jsonin its workbench repo. The script has an infinite loop:base.rsplit('/node_modules/', 1)[0]never changes a top-levelnode_modules/<pkg>path. - After 120s, Claude Code moves the command to a background task.
- The reviewer notices and runs
...; pkill -f 'python3 -'. - Claude Code reports the background task as
failedwith exit code 144, but only the wrapper shell died. On macOSpython3execs.../Python.app/Contents/MacOS/Python -, so the case-sensitivepython3 -pattern never matched the real interpreter. - The interpreter is reparented to PID 1. Claude Code considers the task finished, and the review completes normally.
- The workbench is deleted, but the process keeps running until it is killed by hand about 4 days later.
Why cr's existing safeguards miss it
llm.LaunchProcessusesSetpgidand kills-pgid, but only when the context is cancelled. Claude Code's Bash tool shells lead their own process groups, as the existingsubprocessWaitDelaycomment says. Locally, the tool shell's PGID is its own PID, not theclaudePGID.cleanupClaudeBGJobrunsclaude bg stoponly whenresultErr != nil. Even then, it would only stop jobs Claude Code still tracks, and this one was already markedfailed.- The data-lifecycle orphan sweep and the workbench deletion after a successful review only remove directories, never processes.
Suggested fix (defense in depth)
Before deleting a reviewer workbench, or at the end of a reviewer task, find and kill any process whose cwd is inside that workbench. A simple way is lsof -t +D <workbench> on darwin/linux or /proc/*/cwd on linux. The processes cr launches are the only ones expected to run there, so this is safe to do and catches everything that escapes the process group, whatever launched it.
A related upstream Claude Code issue is that a background task is marked finished while its descendants are still alive. cr can't rely on that being fixed.
Environment
- cr 0.10.302 (fb9759a)
- Claude Code 2.1.269 / 2.1.270, background reviewer mode
- macOS 15 (Darwin 24.6), Python 3.11
Activity
zzwong commentedon Sep 16, 2026
Upstream Claude Code tracking for the underlying behavior:
pkill -fhits the task's wrapper shell, task reportedfailed/144, real process keeps running).pkill -f/pgrep -fmatch the Bash tool's wrapper argv, which is why the task's wrapper died and exited 144.As of Claude Code 2.1.273 no changelog entry fixes this, so the cr-side cleanup proposed above (kill processes whose cwd is inside the workbench before deleting it) is still needed. It also covers other harnesses that leave processes running.