fix(scripts): validate the precompute gate's base ref in the parent shell - #3395
Conversation
…hell Target discovery ran inside `mapfile -t targets < <( ... )`, and the base-ref branch's `exit 2` terminated only that process-substitution subshell. `mapfile`'s own status reflects the read, and a process substitution's exit code is not propagated, so the parent saw nothing but an empty `targets`, scanned zero files and exited 0. An invalid base ref (a typo'd branch, a shallow clone missing the ref) thus read as "nothing to gate". The error text did reach stderr, but the exit code CI gates on said success: a silent pass in exactly the situation the validation was written to catch, which is the #1513 shape. `git rev-parse --verify` now runs in the parent, before the mapfile, so its exit 2 is the script's exit 2. The mode dispatch keeps --all and --paths out of that check, which the new --all case pins against a fixture with no git repository at all. Valid base-ref, --strict base-ref, --paths and the usage paths are unchanged; CI invokes this gate as --all, which never reaches the validation. One deliberate behavior change beyond the issue: a bare `--` (undocumented, let through by the parser and never a working paths separator) previously printed the same error and exited 0. It now exits 2 with it. Closes #3377 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 1m 41s —— View job Claude's Code Review
Scope: Verdict: No blocking issues found. This is a small, well-scoped fix. What I checked:
Not independently verified: the Bash tool in this review lane didn't have permission to execute the test suite or No inline comments posted — nothing rose to the high-signal bar for this lane. |
|
Claude finished @kyle-sexton's task in 55s —— View job Security review
No security issues found. This PR touches only Reviewed for security-relevant surface and found none:
This falls under the skip-gate condition of "no security-relevant surface after reading the diff" — it's a shell-scripting correctness fix (subshell exit-code propagation), not a vulnerability or a fix for one. |
|
Last security-reviewed head: |
|
Claude has reviewed this PR 1 time. The lane skips further automatic reviews after 5; deleting this comment resets the count. |
Summary
scripts/check-skill-precompute-compose.sh <base-ref>treated an invalid baseref as a clean pass.
Target discovery ran inside
mapfile -t targets < <( ... ). The base-refbranch validated the ref and, on failure, printed an error and ran
exit 2--but that exit terminated only the process-substitution subshell.
mapfile'sown status reflects the read, and a process substitution's exit code is not
propagated, so the parent saw nothing but an empty
targets, scanned zerofiles, printed
No skill SKILL.md files in scope — nothing to gate.andexited 0.
A typo'd branch or a shallow clone missing the ref therefore read as "nothing
to gate". The error text did reach stderr, but the exit code CI gates on said
success: a silent pass in exactly the situation the validation exists to catch.
Reproduced on
main:Fix
git rev-parse --verifyis hoisted into the parent shell, ahead of themapfile, so itsexit 2is the script's exit 2. Thecdat the top of thescript already put us at the repo root, so nothing about the check moves. The
now-redundant
base="$first"indirection goes with it, and the mode dispatchkeeps
--alland--pathsout of the check.After:
One deliberate behavior change beyond the issue: a bare
--is let through bythe argument parser as
firstand was never a working paths separator. Itpreviously printed the same "not a valid commit" error and exited 0; it now
exits 2 with it. That is the same correction, applied to the same wrong exit
code.
Verification
Two new cases in
scripts/check-skill-precompute-compose.test.sh:failure is unambiguously about the bogus ref, not an empty HEAD), asserting
rc 2 and naming rc 0 explicitly as the scripts: check-skill-precompute-compose treats an invalid base ref as a pass because exit 2 dies inside a process substitution #3377 shape;
--allagainst a fixture with no git repository at all, which pins the modedispatch that the hoisted validation now depends on.
Results:
PASS=13 FAIL=0.check-skill-precompute-compose.shreverted toHEAD, the base-refcase fails with
FAIL: an invalid base ref passed SILENTLY (rc=0), the #3377 shape(
PASS=12 FAIL=1). The--allcase passes pre-fix by design.scripts/affected-tests.sh --runon both changed files selectsscripts/check-skill-precompute-compose.test.shand nothing else, and isgreen (
All 1 selected suites passed or were skipped.).HEAD) still exits 0,and the existing suite's
--paths,--strict --paths, bare base-ref,--strict <base-ref>, no-args,--help,-hand--strict-alone cases allstill pass.
--all(ci.yml:1493), which never reaches thevalidation, so no CI lane changes behavior.
shellcheck --rcfile .shellcheckrcandscripts/check-shell-portability.sh --pathson both files: clean. (shfmt -dreports one pre-existing diff at
--strict) STRICT=1; shift ;;, untouchedhere and unrelated to this change; shfmt is not a CI gate.)
Related
Closes #3377
Found by the batch-simplify sweep on
claude/code-tidying-batch-simplify-s7ljbiand deliberately left unfixed there because that sweep was behavior-preserving.
🤖 Generated with Claude Code