Problem
Long-lived Claude Code work on this machine accumulates orphaned CC-spawned
processes until new sessions stall on startup. Measured 2026-07-28, the
machine was carrying:
claude CLI sessions .......... 16
MCP server processes ......... 368 .mcp.json declares 9
16 x 9 = 144 expected
368 actual = 2.5x leak
total hung (>30m, <5s CPU) ... 841
A targeted sweep reclaimed 313 processes, all on SIGTERM, zero needed
SIGKILL, which is the tell that they were idle wrappers and not busy work.
BEFORE AFTER DELTA
MCP processes 328 → 82 -246 -75%
total processes 1,387 → 1,070 -317
load (1m) 6.30 → 5.97
hung ork eval test 4 → 0
hung CC hooks ~6 → 0
Duplicated servers were part of it: both mcp-server-memory and
server-memory variants were leaking, and context7 had two variants too.
Why this is an ork issue, not just machine hygiene
It is the shared root cause behind at least two open bug reports that each look
like an independent hang:
Both carry the same signature: sleeping indefinitely at essentially zero CPU.
Run standalone on an idle machine, both complete normally (the build finished in
35 seconds; the guard in under 30 seconds, rc=0, 14 passed).
The mechanism that fits every observed case: a child orphaned when its parent
session dies keeps writing to a pipe nobody drains, and blocks forever once the
64 KB buffer fills.
bash tests/evals/test-dead-generation-guard.sh 0:00.00 cpu · 20h, 27h
bash ~/.claude/hooks/session-start-context.sh 0:00.00 cpu · 18h
bash ~/.claude/hooks/cmux-ci-pills.sh 0:00.01 cpu · 18h
bash scripts/build-plugins.sh 0:00.22 cpu · 6h28m
Four unrelated scripts, each healthy in isolation. That is one condition, not
four bugs. Fixing #3180 and #3190 individually would add timeouts to two of
these and leave the other two exposed.
Proposal
Add an orphan-process health check to doctor:
- Count live
claude --session-id processes and MCP server processes.
- Compare against
declared servers x live sessions. Report the ratio.
- List CC-spawned processes over a threshold age with near-zero cumulative CPU.
- Report only. Do not offer to kill from inside a hook.
⚠️ Targeting is the hard part, and the naive filter is dangerous
A first attempt used "older than 30 minutes and under 5s CPU" against all
processes. That heuristic describes every well-behaved background app: the
dry run caught Slack, Wispr Flow, Docker vmnetd, Core Audio Driver and autofsd,
and its own safety assertion reported 38 live claude CLIs inside the kill list.
It was never piped to kill.
Two further display bugs made the dry run look wrong when the targeting was
actually fine:
simple x88 was a mislabel. Those were
uv tool uvx --index .../simple --from yg-mcp-*, i.e. genuine servers. A
sed 's|.*/||' had grabbed a word out of a URL.
- "74 GUI/system processes" came from grepping
/Library/ unanchored, which
matches ~/Library/pnpm/... node binaries.
The check must therefore target by process family (MCP server command
patterns, CC hook and test shells), never by age plus idleness alone, and must
anchor its paths. The verification regex has to be identical to the targeting
regex: a first pass omitted the modelcontextprotocol literal and reported 6
false unknowns.
Acceptance
doctor reports the MCP-server-to-session ratio and flags a leak above a
documented threshold.
- The check asserts zero live
claude --session-id processes in anything it
labels reclaimable.
- Path matching is anchored; targeting and verification share one pattern.
- Findings are reported, never auto-killed.
Problem
Long-lived Claude Code work on this machine accumulates orphaned CC-spawned
processes until new sessions stall on startup. Measured 2026-07-28, the
machine was carrying:
A targeted sweep reclaimed 313 processes, all on SIGTERM, zero needed
SIGKILL, which is the tell that they were idle wrappers and not busy work.
Duplicated servers were part of it: both
mcp-server-memoryandserver-memoryvariants were leaking, andcontext7had two variants too.Why this is an ork issue, not just machine hygiene
It is the shared root cause behind at least two open bug reports that each look
like an independent hang:
npm run buildhangs at step 7.5, zero children, no networktest-dead-generation-guard.shhangs at section 4Both carry the same signature: sleeping indefinitely at essentially zero CPU.
Run standalone on an idle machine, both complete normally (the build finished in
35 seconds; the guard in under 30 seconds, rc=0, 14 passed).
The mechanism that fits every observed case: a child orphaned when its parent
session dies keeps writing to a pipe nobody drains, and blocks forever once the
64 KB buffer fills.
Four unrelated scripts, each healthy in isolation. That is one condition, not
four bugs. Fixing #3180 and #3190 individually would add timeouts to two of
these and leave the other two exposed.
Proposal
Add an orphan-process health check to
doctor:claude --session-idprocesses and MCP server processes.declared servers x live sessions. Report the ratio.A first attempt used "older than 30 minutes and under 5s CPU" against all
processes. That heuristic describes every well-behaved background app: the
dry run caught Slack, Wispr Flow, Docker vmnetd, Core Audio Driver and autofsd,
and its own safety assertion reported 38 live claude CLIs inside the kill list.
It was never piped to
kill.Two further display bugs made the dry run look wrong when the targeting was
actually fine:
simplex88 was a mislabel. Those wereuv tool uvx --index .../simple --from yg-mcp-*, i.e. genuine servers. Ased 's|.*/||'had grabbed a word out of a URL./Library/unanchored, whichmatches
~/Library/pnpm/...node binaries.The check must therefore target by process family (MCP server command
patterns, CC hook and test shells), never by age plus idleness alone, and must
anchor its paths. The verification regex has to be identical to the targeting
regex: a first pass omitted the
modelcontextprotocolliteral and reported 6false unknowns.
Acceptance
doctorreports the MCP-server-to-session ratio and flags a leak above adocumented threshold.
claude --session-idprocesses in anything itlabels reclaimable.