test: eliminate SIGPIPE flake in grep-q assertions (fixes intermittent CI) - #101
Merged
Conversation
…t CI)
The Tests CI job failed intermittently — most recently
test-post-ship-state-auto-compact.sh AC-2 — with a FALSE assertion failure.
Root cause: assert_contains / assert_not_contains (duplicated in 5 files) and
many inline checks used `printf '%s' "$haystack" | grep -qF -- "$needle"` under
`set -euo pipefail`. With a large (file-sized) haystack, printf writes in stdio
chunks; grep -q matches early and exits, closing the pipe; printf's next write
gets SIGPIPE ("printf: write error: Broken pipe") and exits 141; pipefail
propagates that as the pipeline status, so the assertion FALSELY FAILS even
though the match result is correct. It is timing-dependent, so it passed on most
runs and broke others (same test code passed on the v10.0.0 and PR #100 runs).
Convert every racy 2-stage `producer | grep -q...` to a pipe-free, SIGPIPE-safe
equivalent (behavior-preserving; grep -q's found/not-found is identical):
printf '%s' "$X" | grep -q... -- "$N" -> grep -q... -- "$N" <<<"$X"
cat FILE | grep -q... "$N" -> grep -q... -- "$N" FILE
CMD | grep -q... "$N" -> h=$(CMD); grep -q... -- "$N" <<<"$h"
323 occurrences across 30 test files, including all 5 assert_contains + 3
assert_not_contains definitions + assert_blocked_message. grep flags
(-qF/-qE/-qiF/-qx) preserved exactly; `--` added before patterns. Left untouched:
non-`-q` grep (grep -c / grep -o, read to EOF — no early-exit race), multi-stage
pipes, and short bounded producers (head -1).
This is a pre-existing flake, independent of the v10.0.0 / swap-headroom changes
(the swap fix only touched the ShellCheck CI job; this touches only tests/).
Verification: tests/run-all.sh ALL TEST SUITES PASSED (every suite's pass count
byte-identical to baseline — test-skill-contracts 915/915, etc.); shellcheck
--severity=warning clean (CI match); only tests/ touched (no agents/hooks/skills).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mw2bH4wbEPeebXsvSG6rWe
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The
TestsCI job failed intermittently with a FALSE assertion failure — most recentlytest-post-ship-state-auto-compact.shAC-2: serial early-exit guarded by the off branch, where the substring was present but the assertion reported FAIL. The CI log showed the tell:Root cause (SIGPIPE under
pipefail):assert_contains/assert_not_contains(duplicated in 5 files) and many inline checks used:With a large (file-sized)
$haystack,printfwrites in stdio chunks;grep -qmatches early and exits, closing the pipe;printf's next write gets SIGPIPE → exits 141 →pipefailmakes the whole pipeline non-zero → the assertion falsely fails even though the match is correct. Timing-dependent, so the same test code passed on the v10.0.0 and PR #100 runs and broke this one.Fix
Convert every racy 2-stage
producer | grep -q…to a pipe-free, SIGPIPE-safe equivalent (behavior-preserving —grep -q's found/not-found is identical, and a here-string never changes a substring/line match):323 occurrences across 30 test files, including all 5
assert_contains+ 3assert_not_containsdefinitions +assert_blocked_message. grep flags (-qF/-qE/-qiF/-qx) preserved;--added before patterns.Left untouched (not racy): non-
-qgrep (grep -c/grep -o, read to EOF — no early-exit race), multi-stage pipes, and short bounded producers (head -1).Independent of the v10.0.0 / swap-headroom changes (the swap fix touched only the ShellCheck CI job; this touches only
tests/).Verification
tests/run-all.shALL TEST SUITES PASSED — every suite's pass count byte-identical to baseline (test-skill-contracts915/915,test-path-consistency145/145,test-ask-guard56/56, …).shellcheck --severity=warningclean (CI match) — the 323 conversions introduce no warning.tests/touched (no agents / hooks / skills). Each edited file independently re-ran green (orbash -nfor the twoclaude -pfiles).🤖 Generated with Claude Code
https://claude.ai/code/session_01Mw2bH4wbEPeebXsvSG6rWe