feat: reap stale headless-browser process trees at the top of the maintenance tick #389
No reviewers
Labels
No labels
Agent
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No assignees
2 participants
Notifications
Total time spent: 1 minute 9 seconds
Due date
igor
1 minute 9 seconds
No due date set.
Dependencies
No dependencies set
Reference
joshtronic/igor!389
Loading…
Reference in a new issue
No description provided.
Delete branch "agent/388-maintenance-reap-stale-headless-browser-chrome"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What this PR does
lib/browser-reap.sh: pure predicatebrowser_reap_select_victims(age + signature match, protectsclaude/node) plus the effectfulbrowser_reap_sweepthat snapshotsps, selects victims, kills each victim's whole process tree, and logs one line per victimbrowser_reap_sweepintodo_maintenance_tick(bin/tick.sh), called first and unconditionally, ahead of the weekly per-repo eligibility loopbin/test-browser-reap.sh: unit tests for the predicate against a mock ps table (stale/fresh headless_shell, stale non-browser incl. a node build, stale chrome with--headless, the harness's own claude/node processes, threshold boundary, multi-row selection) -- exercises selection only, never a realps/killThreshold is hardcoded at
BROWSER_REAP_STALE_SECS=3600(60 min) per the issue's "strong opinions" convention -- no env knob.claude/nodebasenames are always protected, independent of any cmdline signature match, so the reaper can never touch the harness's own driver process.Test plan
bash bin/check-sync.sh(==make test) passes, including the newbin/test-browser-reap.shshellcheck lib/browser-reap.sh bin/test-browser-reap.sh bin/tick.shclean (one pre-existing SC2015 note intick.shat an unrelated line, not introduced by this change)-gethreshold comparison to-gt, confirmed the boundary test caught it, then reverted and re-ran greenheadless_shell/chromeprocess older than 60 minutes gets SIGKILLed (with its child tree) on the next maintenance tick, and that the victim line shows up in the log/logwatchCloses #388
🤖 Review —
COMMENT(automated)CI for
4d3b046e: successAdds a host-level stale-headless-browser reaper (
lib/browser-reap.sh) wired to run first/unconditionally indo_maintenance_tick, with a well-tested pure selection predicate. The change is in scope (~200 lines, one issue), the checklist matches the diff, and CI is green. My hesitation is that this is an unconditional SIGKILL of process trees on a live production host, and the effectful half of the code is untested — worth a human's eyes before merge.Checklist honesty: verified — every checked box maps to real diff content.
browser_reap_sweepis genuinely the first statement indo_maintenance_tick; the unchecked box is the manual live-host step (honestly left unchecked). Good.Strengths
browser_reap_select_victimsis a clean pure predicate with solid unit coverage: staleness boundary (3599 vs 3600), signature vs. basename matching, protectedclaude/node, multi-row selection. The mutation-test note (-ge→-gt) is a nice touch.Things a human should weigh (not clear blockers)
browser_reap_sweep(thepssnapshot, thegrep -E "^[[:space:]]*${pid}[[:space:]]"row re-lookup for logging, theread -r _ _ etimes cmd) and_browser_reap_kill_tree(recursion +kill -9) have zero coverage. These are the parts that actually destroy processes on the host. Understandable that you don't want tokillin CI, but the grep-back and tree-walk are non-trivial and unverified._browser_reap_is_protectedgates selection, but_browser_reap_kill_treeSIGKILLs the entire descendant tree unconditionally. If a selected browser ever has anode/claudechild (reverse of the normal topology), it would be killed despite being "protected." Low probability, but the protection guarantee is narrower than the comment implies._browser_reap_kill_treeruns, a reaped-then-recycled PID could belong to an innocent process. Inherent to any reaper and the 1h-stale window makes it unlikely, but it's an unconditionalkill -9so worth acknowledging.Follow-up suggestion: consider a dependency-injected
ps/kill(or a dry-run mode emitting the kill commands) so the sweep + tree-walk can be unit-tested against the same mock table the predicate already uses. That would close the untested-effectful-path gap without touching real processes.No security issues, no scope creep, CI green — routing to a human rather than blocking, because the concrete concerns are judgment calls on a destructive operation rather than definite defects.
Independent review by the harness on
claude-opus-4-8(effort: high). The human reviewer is requested once Igor has reviewed; a human still merges.