fix(scripts): fail a bare read_list --comments with rc 2 instead of looping - #3390
Conversation
…ooping `read_list::into <arr> <file> --comments` with no mode value hung forever. The option arm consumed its value with `shift 2 || true`; when `--comments` is the LAST argument, bash's `shift 2` shifts nothing and returns non-zero, `|| true` swallowed that, `$#` stayed at 1, and the `while (($# > 0))` loop reprocessed `--comments` indefinitely. The function's existing rc-2 "mode is required" branch was therefore unreachable on that path. Shift only what is actually present so the loop drains and a bare `--comments` falls through to that existing error, the same answer `--comments ''` already gave. No caller in the repo hits this today; the cost of leaving it was that a future caller which forgot the mode value got a silent, unbounded stall in a hook or CI instead of a usage error. The regression test runs the missing-value invocation under `timeout` and distinguishes rc 124 from any other wrong answer: without the timeout a regression would not fail the suite, it would hang it. Closes #3363 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 1m 46s —— View job Claude finished reviewing this PR
SummaryThis is a small, well-scoped fix: I traced the fix by hand against both call shapes:
The new test ( No correctness issues found. No inline comments to post — the change is internally consistent and the shift arithmetic is exhaustively covered by the trace above. Note on verification: this review lane's Bash tool declined to execute |
|
Claude finished @kyle-sexton's task in 33s —— View job Security review complete
No security issues found. This PR changes only the internal option-parsing loop in There is no user-controlled input, no injection surface (command/SQL/path/template), no credential or token handling, no authorization logic, and no GitHub Actions workflow file touched. The change is a pure argument-parsing correctness fix scoped to a sourced shell library used internally by other repo scripts — no security-relevant surface in this diff. |
|
Last security-reviewed head: |
|
Claude has reviewed this PR 2 times. The lane skips further automatic reviews after 5; deleting this comment resets the count. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6f9e5ed290
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
`timeout` is coreutils, not POSIX, and a stock macOS userland does not ship it: there the invocation returns 127 and fails a correct library. scripts/check-shell-portability.sh names macOS as the one platform no runner here covers, so a CI-green test is not evidence this one works for a developer on a Mac. Replaced with a hand-rolled `sleep`/`kill`/`wait` watchdog. Same assertion, same bound, POSIX-only tools. A killed probe reports 128+SIGKILL (137) rather than 124, so the hang branch keys off `>= 128`. Three details that are load-bearing rather than incidental, each recorded at its line: the watchdog shell is SIGKILLed (SIGTERM would be deferred until its foreground `sleep` returned, costing the suite five seconds on the happy path); it is killed rather than left to fire against an already-reaped pid, so there is no pid-reuse hazard; and its output is redirected to /dev/null, without which the orphaned `sleep` holds this suite's stdout open and any reader consuming the suite through a pipe blocks for the full five seconds. Measured: 0.27s piped when the library is correct, 5.3s and FAIL=1 against the pre-fix library. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 2m 23s —— View job Claude finished the code-review lane for this PR
SummaryThis PR now has two commits. The first ( The second commit (
I did not find any new correctness issues in either commit. No inline comments to post. Verification note: as in the prior review pass, this lane's Bash tool declined to run |
|
Claude finished @kyle-sexton's task in 53s —— View job Security review complete
No security issues found. The commits since the last security-reviewed head ( This watchdog operates entirely on local subprocess PIDs captured via |
…3402) ## Summary Follow-up to #3390, which merged while this was in flight. That PR bounded the `read_list::into` hang probe with a hand-rolled watchdog, and SIGKILLs the watchdog shell on the **passing** path. Git Bash (MSYS2) announces that kill to the suite's stderr: ``` scripts/lib/read-list.test.sh: line 141: 963299 Killed ( sleep 5; kill -9 "$probe_pid" 2> /dev/null ) ``` Linux bash stays quiet either way, and CI runs `scripts/lib/read-list.test.sh` only on `ubuntu-24.04` (`ci.yml:1234`, the `plugin-gate` job). So the noise is invisible to every lane and visible to every contributor who develops on Windows, which is why it survived the original PR's Linux-only verification. ## Fix `disown "$watchdog_pid"` drops the watchdog from the job table before it is killed. Bash only announces jobs it is tracking, so the notice goes away without changing the kill, the bound, or the assertion. The probe's own kill is still announced, deliberately. It only fires on the path where this case is already reporting `FAIL ... rc=137`, and there a line naming the killed process reads as diagnosis rather than noise. ## Verification - Git Bash (MSYS2), the platform that showed the defect: `PASS=30 FAIL=0` with stderr **empty**, both piped and unpiped. Before this change the same invocation put the `Killed` line on stderr. - Linux: `PASS=30 FAIL=0` in 0.28s, stderr empty. Unchanged from #3390. - Assertion still bites: against `main`'s pre-#3390 library the case reports `FAIL: a bare --comments HUNG (watchdog killed it, rc=137)` / `PASS=29 FAIL=1`. - `shellcheck --rcfile .shellcheckrc`, `shfmt -d`, and `scripts/check-shell-portability.sh --paths`: clean. - `scripts/affected-tests.sh` maps the file to its own suite (R1 self). ## Related Follows up #3390 / #3363. No linked issue: this is a same-day repair of a cross-platform defect in code that PR just landed, caught by re-running its suite on Git Bash rather than only on Linux. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Summary
read_list::into <arr> <file> --commentswith the mode value missing hungforever instead of returning the rc 2 the function already had a branch for.
The option loop's
--commentsarm consumed its value withshift 2 || true(
scripts/lib/read-list.sh:70). When--commentsis the last positional,bash's
shift 2with only one positional left shifts nothing and returnsnon-zero;
|| trueswallowed that failure, so$#stayed at 1 andwhile (($# > 0))reprocessed--commentsindefinitely. The rc-2"mode is required" branch below it was unreachable on that path.
No in-repo caller hits this today. The cost of leaving it was that any future
caller which forgot the mode value got a silent, unbounded stall in a hook or
in CI rather than a usage error.
Fix
scripts/lib/read-list.shshifts only what is actually present:The loop now drains to
$# == 0, and a bare--commentsfalls through to theexisting
--comments is required (inline|leading); there is no defaulterrorwith rc 2, which is the same answer
--comments ''already gave. Deliberatelyreusing that branch rather than adding a second message: the two inputs are the
same mistake and should not diverge in wording.
Verification
New case in
scripts/lib/read-list.test.shruns the missing-value invocationunder a bound and asserts rc 2. The bound is the load-bearing part: without
it a regression does not fail this suite, it hangs it, and an unbounded
stall in CI is worse than a red test.
The bound is a hand-rolled
sleep/kill/waitwatchdog rather thantimeout 5. GNU coreutilstimeoutis absent from a stock macOS userland, andscripts/check-shell-portability.sh:2-12names macOS as exactly the platformno runner here covers, so a
timeout-based test would return 127 and fail acorrect library on a developer's Mac with CI none the wiser. (Raised by
chatgpt-codex-connector on the first push; fixed in cab587f.) Three details in
the watchdog are load-bearing and commented at their lines: the watchdog shell
is SIGKILLed (SIGTERM is deferred until its foreground
sleepreturns, costingfive seconds on the happy path), it is killed rather than left to fire at an
already-reaped pid (no pid-reuse window), and its output goes to
/dev/null(otherwise the orphaned
sleepholds the suite's stdout open and any readerconsuming it through a pipe blocks five seconds -- measured: 7.2s before the
redirect, 0.27s after).
PASS=30 FAIL=0, 0.27s piped.scripts/lib/read-list.shreverted tomain,the suite reports
FAIL: a bare --comments HUNG (watchdog killed it, rc=137) instead of returning 2 (#3363)(
PASS=29 FAIL=1), bounded at 5.3s.scripts/affected-tests.sh --explain scripts/lib/read-list.sh scripts/lib/read-list.test.shmaps both files to suites (no unmapped file).Every suite that sources or names
read-list.shwas run green:read-list30/0,affected-tests47/0,check-changelog-parity84/0,check-docs-only21/0,check-hook-userconfig-argv17/0,check-orphaned-fixtures13/0,check-skill-portability92/0,check-shell-portability336/0.scripts/check-shell-portability.sh --pathson both changed files: clean.shellcheck --rcfile .shellcheckrcandshfmt -don both files: clean.Related
Closes #3363
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