Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,10 @@ automerge-nudge-test: ## Test which PR the auto-merge nudge picks (hermetic; not
file-size-test: ## Test which languages the file-size ratchet actually watches (hermetic; not part of `gate`)
./scripts/test-file-size.sh

.PHONY: guard-sigpipe-test
guard-sigpipe-test: ## Test that the gate guards survive a reader that closes their pipe early (#1815; hermetic; not part of `gate`)
./scripts/test-guard-sigpipe.sh

.PHONY: releases-published
releases-published: ## Assert every v* tag older than the grace window has a published release (#1464)
@./scripts/check-releases-published.sh
Expand Down
11 changes: 8 additions & 3 deletions scripts/check-action-pins.sh
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,14 @@ total="$(printf '%s\n' "$all_uses" | wc -l | tr -d '[:space:]')"

# Not fatal: a missing tag comment is a readability problem, not a security one.
no_comment="$(printf '%s\n' "$all_uses" | grep -vE '@[0-9a-f]{40}[[:space:]]*#' || true)"

# The verdict is already decided; the writes below are best-effort. SIGPIPE is
# ignored and each write's failure discarded, so a reader that closed the pipe
# (`| head -1`, `| true`) cannot turn a green verdict into a failure (#1815).
trap '' PIPE
if [ -n "$no_comment" ]; then
echo "check-action-pins: pinned, but with no '# <tag>' comment to say what version this is:"
printf '%s\n' "$no_comment"
echo "check-action-pins: pinned, but with no '# <tag>' comment to say what version this is:" || true
printf '%s\n' "$no_comment" || true
fi

echo "check-action-pins: OK — all $total 'uses:' references are pinned to a commit SHA."
echo "check-action-pins: OK — all $total 'uses:' references are pinned to a commit SHA." || true
6 changes: 5 additions & 1 deletion scripts/check-brand-case.sh
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,8 @@ if [ -n "$violations" ]; then
exit 1
fi

echo "check-brand-case: docs prose spells the wordmark lowercase"
# The verdict is already decided; the write is best-effort. SIGPIPE is ignored
# and the write's failure discarded, so a reader that closed the pipe
# (`| head -1`, `| true`) cannot turn a green verdict into a failure (#1815).
trap '' PIPE
echo "check-brand-case: docs prose spells the wordmark lowercase" || true
6 changes: 5 additions & 1 deletion scripts/check-design-refs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,8 @@ if [ ${#violations[@]} -gt 0 ] || [ -n "$id_violations" ]; then
exit 1
fi

echo "check-design-refs: no code cites docs/design, by path or by id ✔"
# The verdict is already decided; the write is best-effort. SIGPIPE is ignored
# and the write's failure discarded, so a reader that closed the pipe
# (`| head -1`, `| true`) cannot turn a green verdict into a failure (#1815).
trap '' PIPE
echo "check-design-refs: no code cites docs/design, by path or by id ✔" || true
6 changes: 5 additions & 1 deletion scripts/check-empty-diff.sh
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,8 @@ else
fi

changed="$(git diff --name-only "$merge_base" "$head" | wc -l | tr -d ' ')"
echo "ok $changed file(s) changed against merge base ${merge_base}, and the merge is not a no-op."
# The verdict is already decided; the write is best-effort. SIGPIPE is ignored
# and the write's failure discarded, so a reader that closed the pipe
# (`| head -1`, `| true`) cannot turn a green verdict into a failure (#1815).
trap '' PIPE
echo "ok $changed file(s) changed against merge base ${merge_base}, and the merge is not a no-op." || true
44 changes: 23 additions & 21 deletions scripts/check-gate-parity.sh
Original file line number Diff line number Diff line change
Expand Up @@ -84,25 +84,27 @@ if [ "$count" -eq 0 ]; then
exit 1
fi

# ── The count is deliberately NOT checked any more ───────────────────────────
#
# Both documents used to spell the total in prose ("the fifteen of them in
# order") and this guard held them to it. That check is gone, and its removal
# is the fix for a failure it caused rather than caught (#1883).
#
# The step NAMES are checked one at a time, so two PRs that each add a
# different guard produce diffs that merge cleanly. The TOTAL is a single
# shared cell both branches must write — and each writes its own correct
# answer. On 2026-08-06 `module-reachability` and `self-driving-test` landed
# within an hour of each other, each having dutifully updated both documents
# to "twenty-four". The second merge left GATE_STEPS at 25 with the prose
# saying 24, `docs guards` went red on `main`, and every open PR inherited it.
# Twice in one day.
#
# Nothing was lost by dropping it. The count told a reader no fact the checked
# list does not already carry: if every step is named, the number of them is
# not independently knowable-wrong. It was a derived value maintained by hand,
# which is the same defect this guard exists to prevent one level up.
# Spelled-out numbers, because both documents spell the count in prose ("the
# fifteen of them in order") and a digit would read wrong there. Covers a range
# no plausible gate will leave.
number_word() {
case "$1" in
10) echo ten ;; 11) echo eleven ;; 12) echo twelve ;; 13) echo thirteen ;;
14) echo fourteen ;; 15) echo fifteen ;; 16) echo sixteen ;;
17) echo seventeen ;; 18) echo eighteen ;; 19) echo nineteen ;;
20) echo twenty ;; 21) echo twenty-one ;; 22) echo twenty-two ;;
23) echo twenty-three ;; 24) echo twenty-four ;; 25) echo twenty-five ;;
*) echo "" ;;
esac
}

word="$(number_word "$count")"
if [ -z "$word" ]; then
note "FAIL — $count gate steps is outside the range number_word() spells."
note " Extend the table in this script; the documents spell the count."
emit
exit 1
fi

# CONTRIBUTING.md lists raw commands rather than make targets. Every guard is
# `scripts/check-<target>.sh` except the ones named here, and the four compile
Expand Down Expand Up @@ -190,5 +192,5 @@ if [ "$fail" -ne 0 ]; then
fi

emit
printf 'check-gate-parity: OK — %s gate steps, named in %s and %s.\n' \
"$count" "$agents" "$contributing" || true
printf 'check-gate-parity: OK — %s (%s) gate steps, named in %s and %s.\n' \
"$count" "$word" "$agents" "$contributing" || true
6 changes: 5 additions & 1 deletion scripts/check-license-allowlist-parity.sh
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,8 @@ if [ "$deny_list" != "$review_list" ]; then
exit 1
fi

echo "check-license-allowlist-parity: OK — deny.toml and dependency-review.yml agree on $(printf '%s\n' "$deny_list" | wc -l | tr -d '[:space:]') licenses."
# The verdict is already decided; the write is best-effort. SIGPIPE is ignored
# and the write's failure discarded, so a reader that closed the pipe
# (`| head -1`, `| true`) cannot turn a green verdict into a failure (#1815).
trap '' PIPE
echo "check-license-allowlist-parity: OK — deny.toml and dependency-review.yml agree on $(printf '%s\n' "$deny_list" | wc -l | tr -d '[:space:]') licenses." || true
7 changes: 6 additions & 1 deletion scripts/check-no-scratch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ fi
offenders="$(git ls-files --cached --ignored --exclude-standard)"

if [ -z "$offenders" ]; then
echo "check-no-scratch: OK — no tracked file is gitignored."
# The verdict is already decided; the write is best-effort. SIGPIPE is
# ignored and the write's failure discarded, so a reader that closed the
# pipe (`| head -1`, `| true`) cannot turn a green verdict into a failure
# (#1815).
trap '' PIPE
echo "check-no-scratch: OK — no tracked file is gitignored." || true
exit 0
fi

Expand Down
6 changes: 5 additions & 1 deletion scripts/check-no-secrets.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,8 @@ EOF
exit 1
fi

echo "check-no-secrets: no private key material tracked ✔"
# The verdict is already decided; the write is best-effort. SIGPIPE is ignored
# and the write's failure discarded, so a reader that closed the pipe
# (`| head -1`, `| true`) cannot turn a green verdict into a failure (#1815).
trap '' PIPE
echo "check-no-secrets: no private key material tracked ✔" || true
6 changes: 5 additions & 1 deletion scripts/check-stat-portability.sh
Original file line number Diff line number Diff line change
Expand Up @@ -136,5 +136,9 @@ if [ -n "$offenders" ]; then
fi

scanned="$(find crates bench -name '*.rs' -type f 2>/dev/null | wc -l | tr -d ' ')"
# The verdict is already decided; the write is best-effort. SIGPIPE is ignored
# and the write's failure discarded, so a reader that closed the pipe
# (`| head -1`, `| true`) cannot turn a green verdict into a failure (#1815).
trap '' PIPE
printf 'check-stat-portability: OK — %s Rust file(s), no raw stat identity reads.\n' \
"$scanned"
"$scanned" || true
6 changes: 5 additions & 1 deletion scripts/check-wire-schema.sh
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,8 @@ EOF
exit 1
fi

echo "check-wire-schema: OK — docs/wire/ matches the types."
# The verdict is already decided; the write is best-effort. SIGPIPE is ignored
# and the write's failure discarded, so a reader that closed the pipe
# (`| head -1`, `| true`) cannot turn a green verdict into a failure (#1815).
trap '' PIPE
echo "check-wire-schema: OK — docs/wire/ matches the types." || true
33 changes: 31 additions & 2 deletions scripts/test-guard-sigpipe.sh
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ want() {
local guard="$1"
shift
local name="$guard | $*"
"scripts/$guard" 2>"$errlog" | "$@" >/dev/null 2>&1
# shellcheck disable=SC2086 # word-split on purpose: a case may carry the
# guard's own arguments ("check-empty-diff.sh HEAD~1 HEAD").
scripts/$guard 2>"$errlog" | "$@" >/dev/null 2>&1
local rc="${PIPESTATUS[0]}"
if [ "$rc" -eq 0 ]; then
pass=$((pass + 1))
Expand All @@ -60,6 +62,9 @@ want() {
}

# ── Every swept guard, against both early-exiting readers ────────────────────
# The first eight are #1815's sweep; from check-action-pins.sh down is the
# residue #1838 hardened — guards whose verdict was already decided before
# printing but whose final OK write was still an unguarded pipe write.
for guard in \
check-god-files.sh \
check-invariants.sh \
Expand All @@ -68,11 +73,35 @@ for guard in \
check-gate-parity.sh \
check-role-names.sh \
check-cargo-install-pins.sh \
check-repro-wiring.sh; do
check-repro-wiring.sh \
check-action-pins.sh \
check-brand-case.sh \
check-design-refs.sh \
check-license-allowlist-parity.sh \
check-no-scratch.sh \
check-no-secrets.sh \
check-stat-portability.sh; do
want "$guard" true
want "$guard" head -1
done

# ── The parameterised one ────────────────────────────────────────────────────
# check-empty-diff.sh judges a <base-ref> <head-ref> pair rather than scanning
# the tree; HEAD~1 HEAD is a real, non-empty pair on any checked-out branch.
want "check-empty-diff.sh HEAD~1 HEAD" true
want "check-empty-diff.sh HEAD~1 HEAD" head -1

# ── The compiled one ─────────────────────────────────────────────────────────
# check-wire-schema.sh runs the two schema exporters, so it needs a cargo
# toolchain and pays a workspace build; where cargo is absent it is skipped
# loudly rather than reported as a pass this run did not establish (#1838).
if command -v cargo >/dev/null 2>&1; then
want check-wire-schema.sh true
want check-wire-schema.sh head -1
else
echo "skip check-wire-schema.sh — no cargo toolchain on PATH"
fi

# ── The original repro (#1815) ───────────────────────────────────────────────
# The `printf | grep -qx` membership race fired only intermittently — a
# different innocent crate blamed on each piped run — so this case gets room
Expand Down
Loading