Agent Process Tracking, Audit, And Cleanup
Agents own every process they spawn - dev servers, test/watch runners, automation browsers - and must keep an accurate inventory of what is running. Cleanup is a consequence of the audit, not the whole job.
Coding agents routinely background long-running processes: a pnpm dev server to view a change, a vitest/jest run to check a suite, a Playwright/Chrome instance to test a page. The failure mode is that the agent finishes the task and never stops them. Because each Cursor/Claude/Codex session spawns its own, they pile up. The worst offenders are test runners that hang instead of exiting — they sit at ~100% CPU indefinitely, and a few of them saturate the machine.
The fix is a lifecycle discipline, enforced the same way as the Doctor Pattern: track what you start, audit what is running, then clean up only what the audit proves is yours and no longer needed. It is the Agent Ethos "leave no mess" principle applied to the process table.
The ownership rule
You started it, you stop it. Concretely:
- Before starting another localhost/dev server, check the process ledger and active listeners so one task does not create six ports.
- Record every long-running command you start: PID, PGID, port, purpose, command, cwd, and repo root.
- Keep exactly the one dev server the user is actively using, and any run whose output you are still reading.
- Kill duplicate or abandoned dev servers, finished or hung test/watch runs, and browser pages/contexts you opened.
- Never touch the editor/IDE, the agent runtime, the user's interactive shell, or shared MCP servers.
The ledger owner is scripts/process-ledger.sh inside the Browser Testing Skills skill. It writes local state to ~/.cache/agent-processes/ledger.tsv and keeps stop dry-run by default. This turns process work from "infer ownership from ps after the fact" into "know what is running, why it exists, and which process group owns it." Agent Broom packages the same machinery as a portable CLI + SKILL.md repo for other harnesses. Source: SKILL.md, 2026-06-26; Kevin-Liu-01/agent-broom commit 1bab484c38a3061826cbb93cec591c5d786a48db
Why kill by process group
A test command is a tree: pnpm test → node → vitest → fork worker. The CPU is held by the leaf worker, not the pnpm parent. Killing the top PID orphans the worker, which keeps running. Sending the signal to the process group (kill -TERM -<PGID>) takes the whole tree down at once. Use SIGTERM first for a clean shutdown; SIGKILL only for stragglers. Cap nothing — these are local processes, not retries.
Browsers: close, don't nuke
There are two distinct things, and agents conflate them:
- The MCP server (
playwright-mcp,chrome-devtools-mcp) — launched and reused by the IDE. It powers the browser tools for every active session. Killing it breaks tooling for you and any concurrent agent. It is protected. - The browser the automation drives (headless Chrome under
ms-playwright/mcp-chrome). Close the page/context through the browser tool when the task is done; an orphaned Chrome with a dead parent is yours to clean up.
The discipline: close what you open via the tool; only signal-kill a browser process you spawned and orphaned. Do not free memory by killing MCP servers.
Discovery surfaces
- Cursor terminals folder — per-project metadata for every agent terminal (
pid,cwd,command,running_for_ms):head -n 10 ~/.cursor/projects/*/terminals/*.txt. The fastest way to see what is running and for how long. ps— portable CPU/ownership truth:ps -Ao pid,ppid,pgid,%cpu,etime,command. Sort by%cputo find the hogs; walkppidto find who spawned it (extension-host or a Cursor shell = agent-spawned).
Machine-wide dev cleanup
Repo-local cleanup is not enough when the leak is outside the current repo:
orphaned MCP servers, Flutter/Dart/FVM processes, adb logs, Gradle/Kotlin daemons,
iOS simulators, Crashpad dumps, or IDE crash reporter settings. The local
devclean.sh helper follows the DevClean shape: safe orphan audit by default,
explicit --deep for heavy daemons, --optimize for crash reporters/background
agents, and --disk for global dev caches and project artifacts. It is dry-run
unless --apply is passed. Source: ImL1s/devclean, 2026-06-26
One difference is intentional: generic crashpad_handler processes are not
treated as safe orphan kill targets. On macOS they include normal Codex, Cursor,
Chrome, Slack, Discord, and Electron crash reporters with PPID=1. The safe
local policy keeps Crashpad cleanup in --optimize where the agent can review
the exact files/settings first.
Build & cache artifacts (the disk dimension)
The same "agents leave a mess" pattern shows up on disk, not just in the process table.
Builds and caches are regenerable but unbounded: a single .next is routinely
hundreds of MB, .turbo and node_modules/.cache grow without limit, and stale build
output occasionally causes "works after a clean rebuild" bugs. Clean them when disk is
low, when a build misbehaves, or as end-of-session hygiene — they cost nothing to
regenerate. Targets: .turbo, .next, .vite, dist/build/out/.output,
*.tsbuildinfo, .eslintcache, coverage, test-results, playwright-report,
node_modules/.cache, and pnpm store prune for the global store. The rule that keeps
this safe: only delete regenerable artifacts, and never a path that still holds
git-tracked files — so cleanup can't destroy source. The clean-artifacts.sh helper
reports sizes first and only acts on --clean.
The Aiden Bai cache artifact makes the disk dimension concrete: the screenshot shows a 40 GB repo footprint where .turbo/ alone accounts for 36 GB, while actual source directories are about 1.1 GB. The operational lesson is not "delete random large folders"; it is "measure first, then reclaim only regenerable cache." Use pnpm turbo daemon clean or remove .turbo when it is safe, and keep clean-artifacts.sh as the auditable repo-local path because it reports sizes and skips git-tracked files. Source: X/@aidenybai and local image artifact, 2026-07-03
Enforcement
| Surface | Where | Role |
|---|---|---|
| Always-on Cursor rule | config/cursor/rules/process-hygiene.mdc |
Injected every session, every repo |
| Script entrypoint | skills/productivity/cleanup-terminals-browsers/scripts/agent-hygiene.sh |
Track -> audit -> classify -> cleanup -> verify through deterministic commands |
| Portable package | Agent Broom / skills/productivity/cleanup-terminals-browsers/references/agent-broom/ |
Full upstream CLI + skill package for cross-harness reconstruction |
| Dev-machine cleanup | skills/productivity/cleanup-terminals-browsers/scripts/devclean.sh |
Devclean-style safe orphans, deep daemons, optimize mode, and disk cleanup |
| Skill router | skills/productivity/cleanup-terminals-browsers/SKILL.md |
Thin wrapper that tells agents when to run the script and how to extend it |
| Master agent doc | AGENTS.md § Process & Resource Hygiene |
Cross-agent (Cursor, Claude Code, Codex) |
| Project docs | per-repo AGENTS.md |
Repo-specific notes (e.g. a suite known to hang) |
The helper scripts default to dry-run behavior and are reachable through agent-hygiene.sh.
process-ledger.sh list is the normal inventory command. process-ledger.sh stop only reports recorded live process groups unless passed --kill. devclean.sh audits safe orphans by default and only acts with --apply; --deep, --optimize, and --disk are explicit broader modes. audit-processes.sh kills only by
category (tests/dev/browser) behind a hardcoded protect-list for IDEs and MCP
servers; clean-artifacts.sh deletes only known regenerable artifacts and skips any
git-tracked path — so an agent cannot take down its own tooling or destroy source.
Anti-patterns
- Leaving
vitest/jestwatchers running "in case" — they hang and pin cores. - Starting another
localhost:3000server before checking the ledger and listeners. - Forgetting to record the PID/PGID and then guessing ownership during cleanup.
- Treating process hygiene as cleanup-only. The durable value is the live inventory.
kill <PID>on thepnpmparent only, orphaning the worker that holds the CPU.- Killing
playwright-mcp/chrome-devtools-mcp/ the IDE to "free memory." - Treating every
crashpad_handlerwithPPID=1as safe to kill; many are normal active app helpers on macOS. kill -9first instead of SIGTERM-then-SIGKILL.- Re-running a command that never exits, stacking more 100%-CPU workers on top of the hung ones.
Timeline
- 2026-07-03 | Added the build-cache size artifact:
.turbo/can dominate repo disk usage (36 GB of a 40 GB footprint) while source stays much smaller, so cleanup should be measured, regenerable-only, and script-backed. Source: X/@aidenybai, 2026-03-29 - 2026-06-30 | Added Agent Broom as the portable CLI + skill packaging layer for this process-hygiene stack. The local cleanup skill remains the canonical owner; the full Agent Broom repo is vendored under its references for source comparison and new-agent bootstrapping. Source: Kevin-Liu-01/agent-broom commit
1bab484c38a3061826cbb93cec591c5d786a48db - 2026-06-26 | Reframed agent process hygiene as tracking, audit, and cleanup. The ledger is explicitly a live inventory of running agent-owned processes, with cleanup as a visible downstream action only when the audit proves ownership and staleness. Source: User correction, 2026-06-26
- 2026-06-26 | Made the implementation script-first:
agent-hygiene.shis the single entrypoint, while the skill remains a thin router. Future domains should be added as script subcommands, following the dry-run/report pattern from kibotu's macOS cleanup gist. Source: User correction and gist, 2026-06-26 - 2026-06-26 | Added devclean-style machine cleanup: safe orphaned dev processes, explicit deep daemon cleanup, optimize mode, and disk cleanup. The local implementation is dry-run by default and avoids generic Crashpad process killing after dry-run showed active Codex/Cursor/Chrome helpers matched that pattern. Source: User correction and ImL1s/devclean, 2026-06-26
- 2026-06-26 | Added the process ledger discipline: agents must check/list active localhost servers before starting another one, record long-running commands they start, and clean up recorded process groups dry-run-first.
audit-processes.shnow reports localhost listeners. Source: User request, 2026-06-26 - 2026-06-05 | Incident that motivated this page: in the
ariadnerepo, threevitestfork workers from earlier agent sessions were each pinned at ~100% CPU for 10h28m, 10h36m, and 10h52m (≈300% total), plus a fourth hung run spawned during cleanup. Thepnpm devserver was healthy at 0%. Killing the four test process groups by pgid (SIGTERM) dropped 1-minute load from ~7 to ~4 and left the dev server untouched. The leftoverplaywright-mcp/chrome-devtools-mcpservers (9–14h old) were live IDE-managed connections at ~0% CPU and were left alone. Codified into thecleanup-terminals-browsersskill +process-hygiene.mdcalways-on rule. Source: Kevin, 2026-06-05