Files
nntrivi2001 e2d516f199 docs(lanes): design F3c — ccam lanes gc (E)
Scopes lane-gc.sh down to the two pieces that match CCAM's actual
architecture (orphan Playwright MCP reap, oversized-log capping).
Drops auto-removing stale worktrees by age — that's exactly the kind
of automatic destructive action this repo's own CLAUDE.md forbids
(destroy always goes through the three-check guard, never automatic).
State archiving and scratch-debris sweep don't apply either (different
storage architecture; CCAM doesn't generate those files).
2026-08-05 17:38:04 +07:00

3.3 KiB

F3c — ccam lanes gc: orphan MCP reap + log capping

Status: approved 2026-08-05. Roadmap it belongs to: E (found auditing E1-F3b for Shipyard parity).

Problem

Shipyard's lane-gc.sh does 5 things. Two match CCAM's actual architecture; three don't (see Decisions). No CCAM equivalent exists for either of the two that do.

Scope

In scope:

  1. Reap orphaned Playwright MCP processes. A .playwright-mcp-scoped process whose parent Claude Code session died gets reparented to pid 1 (init). Live sessions' MCP processes keep their real parent and are left alone. Kill the orphan's whole process tree (a browser process it spawned would otherwise also leak).
  2. Cap oversized hook logs. LANES_ROOT/.state/lane<slot>/logs/*.log (written by runHook) grow unbounded across many boots/hooks; cap anything over 10MB to its last 2MB, in place (write-then-replace, not a truncate-while-open, so a concurrent writer's fd stays valid).

Explicitly out of scope (architecture mismatch, not an oversight):

  • Auto-removing stale worktrees by age. This is the piece that actually conflicts with a hard CCAM rule: "Never rm -rf a lane... destructive lane operations go through worktree.js/lanes.js, never a raw filesystem delete." An age-based automatic worktree removal is exactly the kind of automatic destructive action this repo's own CLAUDE.md forbids. ccam lanes reset|remove|purge already exist for a human to call explicitly.
  • Feature-state archiving. Shipyard's version moves flat JSON state files to an archive dir. CCAM's lane/feature state lives in the SQLite DB (lane_features table, from subsystem B) — there's no flat-file equivalent to archive.
  • Scratch-debris sweep (scratch-lane*.log, manual-*.png, state.bak.*). These are artifacts of Shipyard's own scripts (lane-add.sh writing scratch-lane*.log, manual QA screenshots named that way, state.sh's own backup rotation). CCAM doesn't generate any of these — nothing to sweep.

Design

ccam lanes gc [--dry-run] — no lane argument; this scans the whole machine's CCAM footprint, not one lane. Pure local operation (process listing + file I/O), no HTTP route — same shape as ccam skills install.

  • Orphan reap: pgrep -f "<LANES_ROOT>.*\.playwright-mcp" to find candidate pids (scoped to CCAM's own LANES_ROOT, same safety scoping Shipyard's own pattern used — an adopted lane's cwd living outside LANES_ROOT is a known gap, same one Shipyard's own numbered-lane-dir pattern had). For each match, ps -o ppid= -p <pid>; ppid === 1 → orphaned, kill its process tree (pgrep -P recursively, then SIGKILL each, leaves not children first — same shape as Shipyard's kill_tree).
  • Log cap: walk LANES_ROOT/.state/lane*/logs/*.log; for any file over 10MB, read its last 2MB and rewrite the file in place.
  • --dry-run prints what would happen without doing it (same flag Shipyard's version has).

Verify

Manual smoke check (no automated test for OS process listing/killing — this is inherently environment-dependent, same reasoning routes with no HTTP harness already use): run with --dry-run against the current machine's real state and confirm the output looks sane (no crash, no false-positive kill of a live process — verify a currently-live lane's MCP process, if any, does NOT appear in the orphan list).