Repository navigation
claude-ops/plugins: cache-content-check.test.sh process-budget probe measures -1 in the Claude Code on the web container #4046
Description
Summary
plugins/claude-ops/skills/plugins/scripts/cache-content-check.test.sh fails 2 of 26 cases in a Claude Code on the web session on this repository:
FAIL: process budget: the trace probe actually counted something
detail: measured -1 — the pid-stamped PS4 did not reach the traced shell, so the ceiling below is vacuous (trace: /var/tmp/cache-content-check.XXXX/case-26/trace-large.log)
FAIL: process budget: a one-install report costs at most 26 process creations
detail: measured -1 (trace: /var/tmp/cache-content-check.XXXX/case-26/trace-large.log)
The probe runs bash -x "$SCRIPT" with PS4='+${BASHPID}+ ' passed through env and counts distinct pids in the trace. The other 24 cases pass, and the checker itself reports correct verdicts. Reproduced on the unmodified origin/main tree at 9e54a75 as well as on a branch.
Root cause
Verified 2026-09-11 against primary sources and reproduced by an independent verifier in this container:
- Bash 4.4 and later rebind an euid-0 shell's
PS4to+at startup, unconditionally, before any startup file runs (variables.c:if (current_user.euid == 0) bind_variable ("PS4", "+ ", 0);; bash CHANGES, 4.4-rc1 item g; CVE-2016-7543). The GNU bash manual does not document this discard. - The Claude Code on the web container runs as uid 0. In it,
env PS4='+${BASHPID}+ ' bash -x -c trueprints+ true; the same command as uid 65534 (viasetpriv) prints+<pid>+ true. PS4set after startup survives: an in-shell assignment, or aBASH_ENVfile that assigns it, produces pid-stamped trace lines as root. Both were reproduced.
So the probe is at fault, not the environment: passing PS4 through the environment cannot reach a root shell on any bash since 4.4, and the repository's tests run as root in this container.
The sibling plugins/claude-ops/skills/plugins/scripts/fleet-state.test.sh (lines 2034 to 2042) runs the identical env-route probe and has no fail-closed floor: its count_creations also returns -1 here and -1 <= <ceiling> passes vacuously, so that suite is silently green in every root container. It is in scope for the same fix.
Fix
- In both test suites, set
PS4after shell startup instead of through the environment: pointBASH_ENVat a temporary file containingPS4='+${BASHPID}+ '(single-quoted so${BASHPID}expands per traced line), or assign it inside the traced shell beforeset -x.BASH_ENVreaches the script under test without sourcing it. The one unstamped+ PS4=...line aBASH_ENVroute adds does not matchcount_creations's^\++[0-9]+\+pattern, so it is harmless. - Give
fleet-state.test.sh's probe the same fail-closed floorcache-content-check.test.shalready has, so a probe that counts nothing fails rather than passing every ceiling.
Acceptance criteria
- Both suites report all cases passing in a Claude Code on the web session on this repository (uid 0) and on Linux CI.
- IF the trace carries no pid-stamped line, THEN each suite's process-budget cases FAIL naming the probe, in both suites, never pass vacuously.
- WHILE the traced shell runs as uid 0, the probe still measures a positive process count.
- The process-budget ceilings themselves are unchanged: a count above the documented per-install budget still fails.
Verification
Run bash plugins/claude-ops/skills/plugins/scripts/cache-content-check.test.sh and bash plugins/claude-ops/skills/plugins/scripts/fleet-state.test.sh as root in a Claude Code on the web session (id -u prints 0) and on Linux CI; scripts/affected-tests.sh --run on the changed files.
Environment
- Claude Code 2.1.263, Claude Code on the web container, Linux 6.18.44, GNU bash 5.2.21, uid 0
scripts/affected-tests.sh --runselectscache-content-check.test.shfor any change underplugins/claude-ops/skills/plugins/, so every such change reports a red suite in this environment until this lands. This issue therefore blocks claude-ops/plugins: sync digest reportsauto_update: nullfor a marketplace whose autoUpdate is false #4042, claude-ops/plugins: sync-run.sh renders the Step 6 report itself; the model appends only reload guidance #4043, claude-ops/plugins: move the stale-records, cache-content, and userConfig probe prose out of the SKILL.md hub #4044, and claude-ops/plugins: sync digest carries per-step timings #4045 (native dependency edges set).
Related
- The probe was added with the cache-content check; the budget it guards is the per-install process count the check's own header documents.
- claude-ops/plugins: sync digest reports
auto_update: nullfor a marketplace whose autoUpdate is false #4042, claude-ops/plugins: sync-run.sh renders the Step 6 report itself; the model appends only reload guidance #4043, claude-ops/plugins: move the stale-records, cache-content, and userConfig probe prose out of the SKILL.md hub #4044, and claude-ops/plugins: sync digest carries per-step timings #4045 all touchplugins/claude-ops/skills/plugins/and are blocked by this issue.
Activity
auto_update: nullfor a marketplace whose autoUpdate is false #4042fix(claude-ops): carry the process-budget probe's PS4 in a BASH_ENV f…