Repository navigation
feat(gauntlet): name leftover CPU-heavy processes before the layers and on failure (GH #158) - #632
Conversation
…nd on failure (GH #158) A stress run whose cleanup failed under the Bash tool's zsh left 4 yes processes at 100% CPU for ~4 h on 2026-10-25; every push's timing rows failed until someone spotted them. scripts/_lib/stray-procs.sh lists the operator's own processes older than 600 s at 80%+ CPU, with the load, on stderr. run-gauntlet.sh calls it before the layers and again on failure, above the failed-at:/failing: lists so the last line is still the failing list. Warn-only: it never kills and never fails the run. repo-gotchas.md gets the prose half (bash file, explicit PIDs, check for strays after), since the sensor only catches a stray after the fact.
The first live run named a busy headless claude run at 13 min, which is work, not a stray. The strays this targets ran for hours.
…_* env env-vars.md invites setting knobs in settings.json; a raised GAUNTLET_STRAY_MIN_SECS would turn the boundary rows red on that operator's pre-push.
…sensor # Conflicts: # .claude-plugin/marketplace.json # .claude-plugin/plugin.json
…sensor # Conflicts: # .claude-plugin/marketplace.json # .claude-plugin/plugin.json
…sensor # Conflicts: # .claude-plugin/marketplace.json # .claude-plugin/plugin.json
…sensor # Conflicts: # .claude-plugin/marketplace.json # .claude-plugin/plugin.json
|
Fresh-context review: FIX FIRST @ c3e6a1b Two-axis review (mattpocock-skills:code-review). A separate subagent ran each axis against merge-base eef3ec2. Spec (MISSING=0 CREEP=1 WRONG=1)
Standards (HARD=1 JUDGEMENT=3)
All of these except the knobs will be fixed on this branch, followed by a new review. |
…fails Review of c3e6a1b (FIX FIRST): - Spec: an 80%-per-process rule missed the 2026-10-05 flood (289 workers at ~4% each on 12 cores). Processes are now grouped by command name and a group whose %CPU sums to the threshold is named; 43 idle SetStoreUpdateService copies still sum to ~0. - Standards: a failed ps now prints one 'check skipped' line instead of nothing, and a failed load reader prints 'load unknown'. - _sp_num carries the reason it shares with _gl_num. - Tests pin the 80% boundary (80.0 named, 79.9 not), the low-CPU flood, the idle group, and that the first call runs before the layers start.
|
Fresh-context review: FIX FIRST @ 3406046 Second two-axis review (mattpocock-skills:code-review). New subagents ran it against merge-base eef3ec2, without seeing round one. Spec (MISSING=1 CREEP=1 WRONG=1). All three are about the PR text, not the code:
Standards (HARD=1 JUDGEMENT=3)
|
…list Review of 3406046 (FIX FIRST): deleting the failure-path stray_procs_report call left both suites green. The failed-summary test now checks the stub's line is printed, above the failing: list; deleting the call or moving it below the lists fails it. The wiring row reads with grep -m1 instead of | head -1 under pipefail.
|
Fresh-context review: MERGE @ 93e5c20 Third two-axis review (mattpocock-skills:code-review). New subagents ran it against merge-base eef3ec2. Standards (HARD=0 JUDGEMENT=5). Every hard rule checked was clean: home paths, banned commands, no new gate, fail-open sensors that still print a diagnostic,
Spec (MISSING=2 CREEP=1 WRONG=2). Both incident shapes are named from fixture rows:
Both "wrong" items were PR-body claims, now corrected: the check isn't silent on this machine (it names Verdict: MERGE. No hard violation and no code defect remain after three rounds. The open judgement calls are test-seam improvements (the |
Turns the background-flood mistake, which has now happened twice, into a committed check and a committed rule (repeated-mistake doctrine in
CLAUDE.md). Refs #158.Why
Twice, a stress run typed into the Bash tool's zsh left its workers running:
wait $pidsdoes not word-split in zsh.yesprocesses at 100% CPU for about 4 h.kill $(jobs -p)underzsh -cfound no jobs.The load failed every session's timing rows. PR #581's push failed again and again, and it took hours before anyone traced the cause to stray processes.
Prior work checked
failed-at:/failing:lines, which answer scripts/run-gauntlet.sh flaky under concurrent shared-machine load #158's last comment). None of them detects stray processes, and none overlaps this change.run-gauntlet.shload handling.What
scripts/_lib/stray-procs.sh:stray_procs_reportgroups your own processes older thanGAUNTLET_STRAY_MIN_SECS(1800 s) by command name. Any group whose %CPU sums toGAUNTLET_STRAY_MIN_CPU(80) or more is listed on stderr, with its count, summed CPU, oldest age and up to 8 PIDs, plus the load average.SetStoreUpdateServicecopies sum to about 0 and stay quiet.set -ecaller isn't aborted. Ifpsfails, it prints onestray-process check skipped (ps failed)line. If the load can't be read, the warning saysload unknown.[[dd-]hh:]mm:ssetime (macOS has noetimes).run-gauntlet.sh: calls it before the layers start, and again on failure. The second call sits above thefailed-at:/failing:lists, so the last line is still thefailing:list (GH gauntlet: print which test files failed #387).repo-gotchas.md: adds the rule half: run background-load scripts withbash <file>, collectpids+=($!), kill them in atrap ... EXIT, and confirm no strays are left. The check only catches a stray after the fact, so the rule stays.env-vars.md: documents the two thresholds.branching-model.md, branches don't bump the version;merge-pr.shdoes it at merge time.Known limits
clauderun, which is real work, so it went up to 1800 s.Googlefor Chrome. It also merges an app's own processes: 6 Chrome renderers at 15% each are named as one group at 90%. That is still real CPU load on the timing rows, but it isn't a leftover stress run, so the warning says "check them" and never kills anything.Review
c3e6a1b1: FIX FIRST (comment on this PR).psfailure was silent.34060467.34060467: FIX FIRST. The failure-path call had no test, so deleting it left both suites green. Fixed in93e5c204, and that deletion now failstest-gauntlet-failed-summary.sh.93e5c204: 0 hard Standards violations, and the Spec's "wrong" items were all PR-body claims, corrected here.Verified
tests/scripts/test-gauntlet-stray-procs.sh: 21 rows, all fed from fixturepsrows. They cover:yesgrouppsfailure diagnostic andload unknownset -esafety<to<=<to<=psfailuretest-gauntlet-failed-summary.sh: stubs the report with a line that prints, so moving the failure-path call below the lists would fail its "last line is thefailing:list" row.duetexpertd, a macOS system daemon, at 97.9% CPU for about 6 days. That is real CPU load on this machine, and it is why system daemons that run under your uid can show up in the report.audit.shand the gauntlet:audit.shexits 0, and the pre-push gauntlet passed all layers on34060467.