v1.0.2 — installer -ShadowClaude flag, README hardening, demo video
First GitHub release. v1.0.0 and v1.0.1 were documentation milestones in the commit log; v1.0.2 is the first cut anyone can git clone --branch v1.0.2 against a stable tag.
What this is
Kernel-enforced cleanup of orphaned Claude Code subprocesses on Windows. Wraps claude.exe in a Win32 Job Object so the OS reaps every child on exit, including crashes. Same kernel mechanism browser sandboxes use to bound renderer and tab lifetime — but no Node.js runtime wires it up for child processes spawned from Claude Code, so this skill does.
Three layers, each composable, each independently tested:
| Tool | Layer | What it does |
|---|---|---|
tools/cc-procs.ps1 |
Visibility | Read-only inventory: PID, parent, age, memory, classification, orphan flag. No kill capability. |
tools/cleanup-orphans.ps1 |
Cleanup | Terminates strict-orphan subtrees per ~/.reap/config.json. Dry-run by default. |
tools/claude-jobbed.ps1 |
Prevention | Win32 Job Object wrapper. Kernel terminates the entire CC tree on wrapper exit. |
Trust and scope
About 642 lines of PowerShell across tools/ and hooks/, plus 345 lines of tests. No network calls, no telemetry, no registry access, no Windows services. The default install is a guaranteed no-op until a ~/.reap/config.json opts in. Uninstall is three commands.
Full per-surface access scope (network, file reads, file writes, registry, process kills, shell profile, claude.exe, background services) with greppable verify commands lives in the README's Auditable section. Threat model, capability boundaries, and vulnerability reporting in SECURITY.md.
What's in v1.0.2
install-reap.ps1 -ShadowClaudeflag — opt-in switch that redefines plainclaudeas a PowerShell function delegating toclaude-jobbed.ps1. Function form (notSet-Alias) because PowerShell resolves Functions before PATH at parse time, so the function wins overclaude.exe. Without the flag, you have to typeclaude-jobbedevery time you want protection.- README rewritten for senior-dev posture — PowerShell-only callout (
cmd.exehas no$PROFILEso the wrapper is bypassed), three-layer component table, explicit safety invariants, prose tightened to match the seriousness of the kernel mechanism it documents. - 58-second demo video at
docs/demo.mp4. Shows the orphan MCP count dropping to zero onclaude.exeexit, with cleanup driven by the kernel's Job Object close — not by application code. - 22 unit assertions plus 1 functional test passing on Windows 11 build 26200. 9 ms measured reap latency for the functional Job Object test.
Why this exists
Claude Code spawns 40-60 child processes per session (MCP servers, plugins, LSPs, hooks). On Windows, those children frequently outlive the parent. The leak class is well-documented in the upstream issue tracker — issue states are noted in brackets so the dossier is self-verifying:
- #42169
[OPEN]— "claude.exe accumulates 13+ GB virtual memory, triggers Windows Resource Exhaustion" - #53134
[OPEN]— "[BUG] Windows: MCP servers spawned twice at startup (directMcpHost + LocalMcpServerManager)" - #28126
[CLOSED · NOT_PLANNED]— "[BUG] Task tool subagents spawn duplicate MCP servers and leak~/.claude/tasks/directories on Windows" - #40667
[CLOSED · DUPLICATE]— "MCP server processes leak on host after subagent/session termination" - #32304
[CLOSED · DUPLICATE]— "[Bug] Memory leak: claude.exe grows to 21GB+ on Windows during normal workflow with sub-agents"
Windows ships the OS-level primitive. Per Microsoft Learn:
A job object allows groups of processes to be managed as a unit. [...] Operations performed on a job object affect all processes associated with the job object.
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE extends that: when the last handle to the job is closed, the kernel terminates every member process. There is no application code path that can leak. Linux has the equivalent in prctl(PR_SET_PDEATHSIG) plus cgroups; macOS has the equivalent semantics. Windows ships the primitive too — Node.js just doesn't wire it up.
This is structured concurrency as Nathaniel J. Smith framed it in 2018: child task lifetimes bounded by their parent, enforced by the runtime, not by application discipline. Application discipline is what produced the leaks in the first place.
Prior art
TheStack-ai/zclean—npx zclean, cross-platform reactive cleanup for AI coding tools (Claude Code, Codex, Cursor). Same problem class, different layer: zclean kills zombies after they form.claude-jobbed.ps1(Layer 3 here) prevents zombies from forming in the first place by binding the process tree to a Win32 Job Object so the kernel reaps on parent exit. The two are complementary — zclean cleans up the past, the Job Object wrapper bounds the future.- Browser sandboxes (Chrome, Edge) use
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSEto bound renderer-process lifetime. The mechanism is well-trodden — what's new here is wiring it up for Node-spawned MCP and plugin chains, which no Node.js runtime does on its own.
Install
git clone --branch v1.0.2 https://github.com/ron2k1/claude-code-structured-concurrency `
"$env:USERPROFILE\.claude\skills\structured-concurrency"
& "$env:USERPROFILE\.claude\skills\structured-concurrency\tools\install-reap.ps1" -ShadowClaudeOpen a fresh PowerShell window and confirm:
Get-Command claude
# CommandType=Function (Definition: claude-jobbed @args) -> wrapped
# CommandType=Application -> NOT wrappedVerify
.\tests\test-job-object.ps1 # functional: spawns child, closes job, asserts reaped < 2s
.\tests\test-orphan-detect.ps1 # synthetic: orphan detection + PID-reuse guard via StartTime
.\tests\test-config-loader.ps1 # config: spare-wins-over-kill safety invariantAll three suites must pass before relying on the wrapper. Job Object behavior varies by Windows build; test-job-object.ps1 in particular catches kernel-level edge cases on older builds.
Safety guarantees
cc-procs.ps1never kills. Run it any time.cleanup-orphans.ps1defaults to dry-run. Live kills require both-Forceand a config that opts in. With no~/.reap/config.json, the engine is a guaranteed no-op even with-Force.- The decision flow always runs
spare_classificationsbeforekill_names.claude.exeis classified asclaudeandclaudeis in the defaultspare_classifications, so it cannot be killed even if a user addsnode.exetokill_names. Tested explicitly intests/test-config-loader.ps1. claude-jobbed.ps1is opt-in. Plainclaude.exestill works without the wrapper, just unprotected.
Changelog
| Version | What changed |
|---|---|
v1.0.0 (commit-log only) |
Initial three-layer skill (cc-procs / cleanup-orphans / claude-jobbed) + tests + four config profiles. |
v1.0.1 (commit-log only) |
README adoption-gap warning, 15-question FAQ, dangerous-by-omission framing for the cleanup engine. |
v1.0.2 (this release) |
-ShadowClaude installer flag, README polish + safety-invariant prose, 58-second demo video, SVG architecture diagram, test-config-loader hardening. |
License
MIT. See LICENSE.