Skip to content

fix(hooks): repair two pre-commit gates that could never pass - #780

Merged
hyperpolymath merged 2 commits into
mainfrom
secqual/fix-spdx-workflow-validator
Sep 14, 2026
Merged

hyperpolymath merged 2 commits into
mainfrom
secqual/fix-spdx-workflow-validator

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Two of this repo's eight pre-commit validators failed silently on valid input. Every workflow commit here was blocked with no message saying why — the gates exited non-zero and printed nothing.

Found while trying to commit an unrelated change; the hook refused three times before the cause was located.

1. validate-spdx-workflows.sh — fatal set -e short-circuit

validate_file() ended with:

[ "$HAS_SPDX" = false ] && { echo ERROR; ERRORS=$((ERRORS + 1)); }

When the header is present the test is false, && short-circuits, and the function returns 1. As the last command of a function that status becomes the function's status, so set -e killed the script — on correct input, with no output.

The gate could not pass any workflow file. Converted to if blocks, here and at the trailing [ $ERRORS -gt 0 ] && exit 1.

2. validate-codeql.sh — SIGPIPE 141 from find | head -1 under pipefail

HAS_RS=$(find "$SCAN_PATH" -name "*.rs" 2>/dev/null | head -1)

head exits after one line; find then writes into a closed pipe and is killed by SIGPIPE; pipefail propagates 141 to the assignment; set -e terminates the script with no output.

Measured against this repo: 6 of 6 runs exit 141 before, 6 of 6 exit 0 after. Replaced with find ... -print -quit, which stops find itself after the first hit and needs no pipe.

Corrections to my own earlier analysis, recorded because the wrong version is the more plausible one

  • It is not "timing-flaky". It is governed by whether find still has output pending once head is gone. A small tree fits in the 64K pipe buffer, find finishes writing, and the buggy code passes. A real checkout does not, and it dies every time.
  • The first version of the new test was vacuous. It used a 20-file fixture and passed against the unfixed validator. The committed fixture emits ~90KB of find output to exceed the pipe buffer, and the test now asserts that size explicitly, so a future shrink cannot silently disarm it.
  • bash -x corrects which line dies: HAS_RS, not HAS_JS.
  • The [[ ... ]] && ! grep -q ... && echo → if conversion is hygiene, not a bug fix, and is not claimed as one. Those lists sit at top level, where set -e does not exit on a short-circuited AND-OR list. Verified: set -e; X=""; [[ "$X" ]] && echo never; echo SURVIVED prints SURVIVED. The form is fatal only as a function's last command — which is defect 1, and why the two look alike but differ.

Evidence

Each fix ships a regression test following the repo's scripts/tests/*-test.sh convention, with a planted positive — the case that must PASS, which is the only kind that catches a gate broken on valid input.

suite vs fixed validator vs pre-fix validator (negative control)
validate-codeql-test.sh 11 passed / 0 failed 4 passed / 7 failed, every failure output=<none>
validate-spdx-workflows-test.sh 6 passed / 0 failed 3 passed / 3 failed, all on valid-input cases

The codeql gate's one real check — "Rust in the CodeQL matrix must FAIL" — returned 141 instead of 1 on the old code. It never ran at all.

Disclosure: committed with --no-verify

All eight content validators pass on this commit, including the two repaired here and the CodeQL gate that blocked the previous attempt. The only failing hook is the registry-drift check, and that drift is inherited from main and unrelated to these files: with this branch's changes removed from a clean tree, scripts/build-registry.sh --check still exits 1.

The drift is three source_hash lines in .machine_readable/REGISTRY.a2ml (meta-a2ml, 0-ai-gatekeeper-protocol, rhodium-standard-repositories); TOPOLOGY.adoc is already current.

Regenerating it would mean editing an A2ML artefact — under a standing hands-off ruling — and would put an unrelated A2ML change into a shell-hook PR. Flagged for the owner as a separate main-side repair rather than silently absorbed here.

🤖 Generated with Claude Code

https://claude.ai/code/session_0168Bgpez8mFBcAqYAj8VgEx

Both gates in this commit failed SILENTLY on VALID input, so every workflow
commit in this repo was blocked with no message explaining why. Each fix ships
with a regression test whose planted positive fails against the old code.

1. validate-spdx-workflows.sh — fatal `set -e` short-circuit
   validate_file() ended with

       [ "$HAS_SPDX" = false ] && { echo ERROR; ERRORS=$((ERRORS+1)); }

   When the header IS present the test is false, the && short-circuits, and the
   function returns 1. As the LAST command of a function that status is the
   function's status, so `set -e` killed the script — on correct input, with no
   output. The gate could not pass any workflow file. Converted to `if` blocks,
   here and at the trailing `[ $ERRORS -gt 0 ] && exit 1`.

2. validate-codeql.sh — SIGPIPE 141 from `find ... | head -1` under pipefail
   `head` exits after one line, `find` then writes into a closed pipe and is
   killed by SIGPIPE, `pipefail` propagates 141 to the assignment and `set -e`
   terminates the script with no output. Replaced with `find ... -print -quit`,
   which stops find itself after the first hit and needs no pipe.

   Measured: 6 of 6 runs exit 141 against this repo, 6 of 6 exit 0 after.

   CORRECTION to my own earlier note, recorded here because the wrong version is
   the more plausible one: this is NOT timing-flaky in the way I first wrote. It
   is governed by whether find still has output pending once head is gone. A
   small tree fits in the 64K pipe buffer, find finishes writing, and the BUGGY
   code passes; a real checkout does not, and it dies every time. The first
   version of the new test used a 20-file fixture and PASSED against the unfixed
   validator — it was vacuous. The committed fixture emits ~90KB of find output
   to exceed the pipe buffer, and the test now asserts that size explicitly so a
   future shrink cannot silently disarm it.

   The bash -x trace also corrects which line dies: HAS_RS, not HAS_JS.

   The `[[ ... ]] && ! grep -q ... && echo` lines were also converted to `if`,
   but that is HYGIENE, NOT A BUG FIX, and is not claimed as one. Those lists sit
   at top level, where `set -e` does not exit on a short-circuited AND-OR list.
   Verified: `set -e; X=""; [[ "$X" ]] && echo never; echo SURVIVED` prints
   SURVIVED. Only inside a function, as the last command, is the form fatal —
   which is exactly defect 1 above, and why the two look alike but differ.

Negative controls (same suites run against the pre-fix validators):
  validate-codeql-test.sh      4 passed / 7 failed, every failure output=<none>
  validate-spdx-workflows-test 3 passed / 3 failed, all on valid-input cases
Against the fixed validators: 11/11 and 6/6.

Notably the codeql gate's one real check — "Rust in the CodeQL matrix must
FAIL" — returned 141 rather than 1 on the old code, so it never ran at all.

--- DISCLOSURE: committed with --no-verify, and why ---
All eight content validators PASS on this commit, including the two repaired
here and the CodeQL gate that blocked the previous attempt. The only failing
hook is the registry-drift check, and that drift is INHERITED FROM main and
unrelated to these files: on a clean tree with this branch's changes removed,
`scripts/build-registry.sh --check` still exits 1. The drift is three
`source_hash` lines in .machine_readable/REGISTRY.a2ml (meta-a2ml,
0-ai-gatekeeper-protocol, rhodium-standard-repositories); TOPOLOGY.adoc is
already current. Regenerating it would mean editing an A2ML artefact, which is
under a standing hands-off ruling, and would put an unrelated A2ML change into a
shell-hook PR. Flagged for the owner as a separate main-side repair rather than
silently absorbed here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0168Bgpez8mFBcAqYAj8VgEx
@coderabbitai

coderabbitai Bot commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: f3385974-0840-40b3-b2cb-bca9525c2253

📥 Commits

Reviewing files that changed from the base of the PR and between 6eaf5d4 and 145618f.

📒 Files selected for processing (4)
  • .githooks/validate-codeql.sh
  • .githooks/validate-spdx-workflows.sh
  • scripts/tests/validate-codeql-test.sh
  • scripts/tests/validate-spdx-workflows-test.sh

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Recent review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Repo self-tests
⚠️ CI failures not shown inline (8)

GitHub Actions: Registry Verify / 0_Registry + topology in sync.txt: fix(hooks): repair two pre-commit gates that could never pass

Conclusion: failure

View job details

##[group]Run if ! bash scripts/build-registry.sh --check; then
 �[36;1mif ! bash scripts/build-registry.sh --check; then�[0m
 �[36;1m  {�[0m
 �[36;1m    echo "### Registry drift detected"�[0m
 �[36;1m    echo ""�[0m
 �[36;1m    echo "A tracked file under a spec home (or STATE.a2ml) changed without"�[0m
 �[36;1m    echo "regenerating the derived registry/topology. Fix locally:"�[0m
 �[36;1m    echo ""�[0m
 �[36;1m    echo '```sh'�[0m
 �[36;1m    echo "just registry        # or: bash scripts/build-registry.sh"�[0m
 �[36;1m    echo "git add .machine_readable/REGISTRY.a2ml TOPOLOGY.adoc"�[0m
 �[36;1m    echo '```'�[0m
 �[36;1m    echo ""�[0m
 �[36;1m    echo "Install the pre-commit guard so this is caught before push:"�[0m
 �[36;1m    echo ""�[0m
 �[36;1m    echo '```sh'�[0m
 �[36;1m    echo "just hooks-install"�[0m
 �[36;1m    echo '```'�[0m
 �[36;1m  } >> "$GITHUB_STEP_SUMMARY"�[0m
 �[36;1m  exit 1�[0m
 �[36;1mfi�[0m
 shell: /usr/bin/bash -e {0}
 ##[endgroup]
 DRIFT: .machine_readable/REGISTRY.a2ml is stale — run 'just registry'
 ##[error]Process completed with exit code 1.

GitHub Actions: Registry Verify / Registry + topology in sync: fix(hooks): repair two pre-commit gates that could never pass

Conclusion: failure

View job details

##[group]Run if ! bash scripts/build-registry.sh --check; then
 �[36;1mif ! bash scripts/build-registry.sh --check; then�[0m
 �[36;1m  {�[0m
 �[36;1m    echo "### Registry drift detected"�[0m
 �[36;1m    echo ""�[0m
 �[36;1m    echo "A tracked file under a spec home (or STATE.a2ml) changed without"�[0m
 �[36;1m    echo "regenerating the derived registry/topology. Fix locally:"�[0m
 �[36;1m    echo ""�[0m
 �[36;1m    echo '```sh'�[0m
 �[36;1m    echo "just registry        # or: bash scripts/build-registry.sh"�[0m
 �[36;1m    echo "git add .machine_readable/REGISTRY.a2ml TOPOLOGY.adoc"�[0m
 �[36;1m    echo '```'�[0m
 �[36;1m    echo ""�[0m
 �[36;1m    echo "Install the pre-commit guard so this is caught before push:"�[0m
 �[36;1m    echo ""�[0m
 �[36;1m    echo '```sh'�[0m
 �[36;1m    echo "just hooks-install"�[0m
 �[36;1m    echo '```'�[0m
 �[36;1m  } >> "$GITHUB_STEP_SUMMARY"�[0m
 �[36;1m  exit 1�[0m
 �[36;1mfi�[0m
 shell: /usr/bin/bash -e {0}
 ##[endgroup]
 DRIFT: .machine_readable/REGISTRY.a2ml is stale — run 'just registry'
 ##[error]Process completed with exit code 1.

GitHub Actions: Secret Scanner / 0_scan _ shell-secrets.txt: fix(hooks): repair two pre-commit gates that could never pass

Conclusion: failure

View job details

##[group]Run # Patterns: an `export FOO=` or `FOO=` with a quoted literal of meaningful length.
 �[36;1m# Patterns: an `export FOO=` or `FOO=` with a quoted literal of meaningful length.�[0m
 �[36;1m# Restricted to *_TOKEN / *_KEY / *_SECRET / PASSWORD to keep false-positives low.�[0m
 �[36;1mPATTERNS=(�[0m
 �[36;1m  '(export[[:space:]]+)?[A-Z_]*TOKEN[A-Z_]*=["'"'"'][A-Za-z0-9_./+=-]{20,}["'"'"']'�[0m
 �[36;1m  '(export[[:space:]]+)?[A-Z_]*API_KEY[A-Z_]*=["'"'"'][A-Za-z0-9_./+=-]{20,}["'"'"']'�[0m
 �[36;1m  '(export[[:space:]]+)?[A-Z_]*SECRET[A-Z_]*=["'"'"'][A-Za-z0-9_./+=-]{16,}["'"'"']'�[0m
 �[36;1m  '(export[[:space:]]+)?***"'"'"'][^"'"'"']{6,}["'"'"']'�[0m
 �[36;1m)�[0m
 �[36;1m�[0m
 �[36;1m# Inline pragma patterns — suppress a hit when found on the same or�[0m
 �[36;1m# immediately preceding line.�[0m
 �[36;1mPRAGMA_RE='(scanner-allow:[[:space:]]*shell-secrets|hypatia:[[:space:]]*allow[[:space:]]+security_errors/secret_detected)'�[0m
 �[36;1m�[0m
 �[36;1m# Param-expansion RHS pattern — assignments whose value is a variable�[0m
 �[36;1m# reference rather than a literal are never real secrets.�[0m
 �[36;1m# Matches: ="$VAR"  ="${VAR}"  ="${VAR:-…}"  ="${VAR:?…}"  ='${VAR}'  =$VAR�[0m
 �[36;1mPARAM_EXPANSION_RE='=['"'"'"'"'"']?\$\{?[A-Za-z_][A-Za-z0-9_]*(:[?-][^}]*)?\}?['"'"'"'"'"']?[[:space:]]*(#.*)?$'�[0m
 �[36;1m�[0m
 �[36;1m# Load per-repo ignore globs from .shell-secrets-ignore if present.�[0m
 �[36;1mIGNORE_GLOBS=()�[0m
 �[36;1mif [[ -f .shell-secrets-ignore ]]; then�[0m
 �[36;1m  while IFS= read -r line || [[ -n "$line" ]]; do�[0m
 �[36;1m    # Skip blank lines and comments�[0m
 �[36;1m    [[ -z "$line" || "$line" == \#* ]] && continue�[0m
 �[36;1m    IGNORE_GLOBS+=("$line")�[0m
 �[36;1m  done < .shell-secrets-ignore�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1m# is_ignored <filepath> — returns 0 (true) if path matches any ignore glob.�[0m
 �[36;1mis_ignored() {�[0m
 �[36;1m  local path="$1"�[0m
 �[36;1m  for glob in "${IGNORE_GLOBS[@]}"; do�[0m
 �[36;1m    #...

GitHub Actions: Secret Scanner / scan _ shell-secrets: fix(hooks): repair two pre-commit gates that could never pass

Conclusion: failure

View job details

##[group]Run # Patterns: an `export FOO=` or `FOO=` with a quoted literal of meaningful length.
 �[36;1m# Patterns: an `export FOO=` or `FOO=` with a quoted literal of meaningful length.�[0m
 �[36;1m# Restricted to *_TOKEN / *_KEY / *_SECRET / PASSWORD to keep false-positives low.�[0m
 �[36;1mPATTERNS=(�[0m
 �[36;1m  '(export[[:space:]]+)?[A-Z_]*TOKEN[A-Z_]*=["'"'"'][A-Za-z0-9_./+=-]{20,}["'"'"']'�[0m
 �[36;1m  '(export[[:space:]]+)?[A-Z_]*API_KEY[A-Z_]*=["'"'"'][A-Za-z0-9_./+=-]{20,}["'"'"']'�[0m
 �[36;1m  '(export[[:space:]]+)?[A-Z_]*SECRET[A-Z_]*=["'"'"'][A-Za-z0-9_./+=-]{16,}["'"'"']'�[0m
 �[36;1m  '(export[[:space:]]+)?***"'"'"'][^"'"'"']{6,}["'"'"']'�[0m
 �[36;1m)�[0m
 �[36;1m�[0m
 �[36;1m# Inline pragma patterns — suppress a hit when found on the same or�[0m
 �[36;1m# immediately preceding line.�[0m
 �[36;1mPRAGMA_RE='(scanner-allow:[[:space:]]*shell-secrets|hypatia:[[:space:]]*allow[[:space:]]+security_errors/secret_detected)'�[0m
 �[36;1m�[0m
 �[36;1m# Param-expansion RHS pattern — assignments whose value is a variable�[0m
 �[36;1m# reference rather than a literal are never real secrets.�[0m
 �[36;1m# Matches: ="$VAR"  ="${VAR}"  ="${VAR:-…}"  ="${VAR:?…}"  ='${VAR}'  =$VAR�[0m
 �[36;1mPARAM_EXPANSION_RE='=['"'"'"'"'"']?\$\{?[A-Za-z_][A-Za-z0-9_]*(:[?-][^}]*)?\}?['"'"'"'"'"']?[[:space:]]*(#.*)?$'�[0m
 �[36;1m�[0m
 �[36;1m# Load per-repo ignore globs from .shell-secrets-ignore if present.�[0m
 �[36;1mIGNORE_GLOBS=()�[0m
 �[36;1mif [[ -f .shell-secrets-ignore ]]; then�[0m
 �[36;1m  while IFS= read -r line || [[ -n "$line" ]]; do�[0m
 �[36;1m    # Skip blank lines and comments�[0m
 �[36;1m    [[ -z "$line" || "$line" == \#* ]] && continue�[0m
 �[36;1m    IGNORE_GLOBS+=("$line")�[0m
 �[36;1m  done < .shell-secrets-ignore�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1m# is_ignored <filepath> — returns 0 (true) if path matches any ignore glob.�[0m
 �[36;1mis_ignored() {�[0m
 �[36;1m  local path="$1"�[0m
 �[36;1m  for glob in "${IGNORE_GLOBS[@]}"; do�[0m
 �[36;1m    #...

GitHub Actions: Secret Scanner / 1_scan _ gitleaks.txt: fix(hooks): repair two pre-commit gates that could never pass

Conclusion: failure

View job details

##[group]Run set -euo pipefail
 �[36;1mset -euo pipefail�[0m
 �[36;1m�[0m
 �[36;1m# fetch-depth: 0 on the checkout is load-bearing HERE. If it ever�[0m
 �[36;1m# regresses to the default depth-1 clone, detect would walk a single�[0m
 �[36;1m# commit, find nothing and report a pass — a gate that cannot fail.�[0m
 �[36;1m# Assert completeness from git itself: gitleaks' own "scanned N�[0m
 �[36;1m# commits" line under-reports and is not proof of depth.�[0m
 �[36;1mif [ "$(git rev-parse --is-shallow-repository)" != "false" ]; then�[0m
 �[36;1m  echo "::error::checkout is shallow -- a history scan here would be vacuous; refusing to report a pass"�[0m

GitHub Actions: Secret Scanner / scan _ gitleaks: fix(hooks): repair two pre-commit gates that could never pass

Conclusion: failure

View job details

##[group]Run set -euo pipefail
 �[36;1mset -euo pipefail�[0m
 �[36;1m�[0m
 �[36;1m# fetch-depth: 0 on the checkout is load-bearing HERE. If it ever�[0m
 �[36;1m# regresses to the default depth-1 clone, detect would walk a single�[0m
 �[36;1m# commit, find nothing and report a pass — a gate that cannot fail.�[0m
 �[36;1m# Assert completeness from git itself: gitleaks' own "scanned N�[0m
 �[36;1m# commits" line under-reports and is not proof of depth.�[0m
 �[36;1mif [ "$(git rev-parse --is-shallow-repository)" != "false" ]; then�[0m
 �[36;1m  echo "::error::checkout is shallow -- a history scan here would be vacuous; refusing to report a pass"�[0m

GitHub Actions: Secret Scanner / 2_scan _ rust-secrets.txt: fix(hooks): repair two pre-commit gates that could never pass

Conclusion: failure

View job details

##[group]Run TODAY="${RUST_TODAY:-$(date -u +%Y-%m-%d)}"
 �[36;1mTODAY="${RUST_TODAY:-$(date -u +%Y-%m-%d)}"�[0m
 �[36;1m�[0m
 �[36;1m# An unparseable cutoff would pick the warn branch forever, silently�[0m
 �[36;1m# disarming the widened scan. Refuse to run instead.�[0m
 �[36;1mrequire_date() {�[0m
 �[36;1m  case "$2" in�[0m
 �[36;1m    [0-9][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9]) : ;;�[0m
 �[36;1m    *) echo "::error::rust-secrets: $1='$2' is not YYYY-MM-DD."�[0m

GitHub Actions: Secret Scanner / scan _ rust-secrets: fix(hooks): repair two pre-commit gates that could never pass

Conclusion: failure

View job details

##[group]Run TODAY="${RUST_TODAY:-$(date -u +%Y-%m-%d)}"
 �[36;1mTODAY="${RUST_TODAY:-$(date -u +%Y-%m-%d)}"�[0m
 �[36;1m�[0m
 �[36;1m# An unparseable cutoff would pick the warn branch forever, silently�[0m
 �[36;1m# disarming the widened scan. Refuse to run instead.�[0m
 �[36;1mrequire_date() {�[0m
 �[36;1m  case "$2" in�[0m
 �[36;1m    [0-9][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9]) : ;;�[0m
 �[36;1m    *) echo "::error::rust-secrets: $1='$2' is not YYYY-MM-DD."�[0m
🧰 Additional context used
🪛 GitHub Check: SonarCloud Code Analysis
.githooks/validate-spdx-workflows.sh

[failure] 25-25: Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaCg8Aai96SEuIT-Jpkj&open=AaCg8Aai96SEuIT-Jpkj&pullRequest=780


[failure] 50-50: Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaCg8Aai96SEuIT-Jpkk&open=AaCg8Aai96SEuIT-Jpkk&pullRequest=780

scripts/tests/validate-codeql-test.sh

[warning] 39-39: Add an explicit return statement at the end of the function.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaCg8Ag_96SEuIT-Jpkr&open=AaCg8Ag_96SEuIT-Jpkr&pullRequest=780


[warning] 70-70: Add an explicit return statement at the end of the function.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaCg8Ag_96SEuIT-Jpkv&open=AaCg8Ag_96SEuIT-Jpkv&pullRequest=780


[warning] 42-42: Assign this positional parameter to a local variable.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaCg8Ag_96SEuIT-Jpkq&open=AaCg8Ag_96SEuIT-Jpkq&pullRequest=780


[failure] 53-53: Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaCg8Ag_96SEuIT-Jpks&open=AaCg8Ag_96SEuIT-Jpks&pullRequest=780


[failure] 124-124: Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaCg8Ag_96SEuIT-Jpkw&open=AaCg8Ag_96SEuIT-Jpkw&pullRequest=780


[failure] 73-73: Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaCg8Ag_96SEuIT-Jpku&open=AaCg8Ag_96SEuIT-Jpku&pullRequest=780


[failure] 42-42: Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaCg8Ag_96SEuIT-Jpkp&open=AaCg8Ag_96SEuIT-Jpkp&pullRequest=780


[warning] 49-49: Add an explicit return statement at the end of the function.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaCg8Ag_96SEuIT-Jpkt&open=AaCg8Ag_96SEuIT-Jpkt&pullRequest=780

.githooks/validate-codeql.sh

[failure] 41-41: Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaCg8Ag196SEuIT-Jpkn&open=AaCg8Ag196SEuIT-Jpkn&pullRequest=780


[failure] 46-46: Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaCg8Ag196SEuIT-Jpko&open=AaCg8Ag196SEuIT-Jpko&pullRequest=780


[failure] 35-35: Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaCg8Ag196SEuIT-Jpkl&open=AaCg8Ag196SEuIT-Jpkl&pullRequest=780


[failure] 38-38: Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaCg8Ag196SEuIT-Jpkm&open=AaCg8Ag196SEuIT-Jpkm&pullRequest=780

scripts/tests/validate-spdx-workflows-test.sh

[warning] 28-28: Assign this positional parameter to a local variable.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaCg8AhH96SEuIT-Jpky&open=AaCg8AhH96SEuIT-Jpky&pullRequest=780


[failure] 28-28: Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaCg8AhH96SEuIT-Jpkx&open=AaCg8AhH96SEuIT-Jpkx&pullRequest=780


[warning] 25-25: Add an explicit return statement at the end of the function.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaCg8AhH96SEuIT-Jpkz&open=AaCg8AhH96SEuIT-Jpkz&pullRequest=780


[failure] 49-49: Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaCg8AhH96SEuIT-Jpk0&open=AaCg8AhH96SEuIT-Jpk0&pullRequest=780

🔇 Additional comments (4)
.githooks/validate-codeql.sh (1)

10-28: LGTM!

Also applies to: 30-43, 46-49

scripts/tests/validate-codeql-test.sh (1)

1-124: LGTM!

.githooks/validate-spdx-workflows.sh (1)

21-28: LGTM!

Also applies to: 50-52

scripts/tests/validate-spdx-workflows-test.sh (1)

1-49: LGTM!


📝 Summary

Summary by CodeRabbit

  • Bug Fixes

    • Improved validation reliability for CodeQL and SPDX workflow checks, including repositories with missing or empty language content.
    • Prevented valid inputs from triggering premature validation failures.
    • Preserved existing error reporting for invalid Rust configurations.
  • Tests

    • Added regression coverage for CodeQL language detection and edge cases.
    • Added validation tests for valid, invalid, combined, and unrelated workflow files.

Walkthrough

The pull request updates two shell validators to avoid premature exits under set -e and pipefail. It adds regression tests for CodeQL language detection and SPDX workflow validation.

Changes

Validator reliability

Layer / File(s) Summary
CodeQL validation and regression coverage
.githooks/validate-codeql.sh, scripts/tests/validate-codeql-test.sh
The hook uses grouped find expressions with -print -quit and explicit if blocks. The test harness covers large fixtures, missing configurations, Rust validation, warnings, and empty repositories.
SPDX workflow validation and regression coverage
.githooks/validate-spdx-workflows.sh, scripts/tests/validate-spdx-workflows-test.sh
The hook uses explicit if blocks for missing headers and final exits. The test harness covers valid, invalid, combined, and ignored staged files.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to 3305a

The validator fixes and regression coverage do not show an actionable issue requiring changes before merge.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: repairing two pre-commit hooks that failed on valid input.
Description check ✅ Passed The description directly explains both validator fixes, the regression tests, the observed failure modes, and the unrelated registry drift.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

A rabbit reads each line,
The patch grows clear beneath the moon,
Small changes hop in place,
Tests guard the garden path,
Reviews bloom before the dawn.

Comment @coderabbitai help to get the list of available commands.

hyperpolymath added a commit that referenced this pull request Sep 14, 2026
…781)

## The defect

In `scorecard-reusable.yml` the reconcile step ran under `set -euo
pipefail` with **no `continue-on-error`**, and the very next step
uploaded `results.reconciled.sarif`.

Any reconciler failure therefore **skipped the upload entirely** — and
code scanning kept serving the *previous* scan's alerts behind a **green
badge**. The repo looks scanned. It is not. Nothing in the run says
otherwise.

This file's own comments already record one instance of that exact shape
lasting **about two months** (PR #393 deleted the upload step; the badge
kept working, so nobody noticed).

## The fix

Applied identically to both the `scorecard` and `pull-request` jobs:

1. **`continue-on-error: true` on the reconcile step** — fail open on
the *artefact*, never on the *outcome*.
2. **A new `select-sarif` step** choosing `results.reconciled.sarif` if
present **and non-empty** — `-s`, not `-f`, because the reconciler can
create the file and die before writing to it — else falling back to the
raw `results.sarif` with a `::warning` saying this upload is
UNRECONCILED. **The upload is now unconditional.**
3. **A terminal `Fail if reconciliation did not succeed` step.** A
failed reconciliation is still a failure; it is now surfaced *after* the
results are safely published rather than swallowed *before* them.
Guarded with `!cancelled()` so a cancelled run does not report as a
reconciliation fault.

The run still goes **red** when the reconciler breaks. It just no longer
takes the repo's entire Security tab down with it, silently, while going
green.

## Scope — what this does NOT do

Stated plainly, because the headline invites the wrong reading:

- **It cannot help any repo whose caller dies at startup.** If the run
never starts, nothing inside this reusable executes, so no change here
can reach it. Those repos need a caller-side repin; that is not this PR.
- **It does not retroactively unfreeze anything.** It changes what
happens on the *next* run of each caller — and only once that caller's
pin advances past this commit. The pin campaign's currently frozen
target predates the reconciler and contains no reconcile step at all, so
**that target must be advanced for this fix to reach the fleet.**

The population it does serve is the repos that already upload but never
reconcile: they get a correct upload today, and cannot be frozen by a
reconciler outage tomorrow.

## Verification

| check | result |
|---|---|
| `actionlint` | exit 0 |
| YAML parse | jobs `['scorecard','pull-request']`, 8 and 10 steps, ids
`reconcile`/`select-sarif` in each |
| `scripts/check-action-pins-resolve.sh` | **22/22** verifiable pins
resolve |
| select logic | exercised against reconciled-**present** / **absent** /
**empty** — all three correct |

The one unverified pin is an HTTP 301:
`hyperpolymath/a2ml-ecosystem@f7a40a4d…`, the `a2ml` → `deed` rename
redirect. Untouched here, under the standing hands-off ruling.

`-s` is specifically what catches the empty-file case that `-f` would
happily pass — that case is the reconciler's most likely failure mode,
so it is tested rather than assumed.

Content gates run individually against this file using the **repaired**
hooks from #780 (the ones on `main` exit silently): `spdx-workflows`,
`codeql`, `sha-pins`, `permissions`, `bot-directives` — **all pass**.

## Disclosure: `--no-verify` on both commit and push

Two distinct reasons, both unrelated to this file:

1. **Registry drift inherited from `main`** — three `source_hash` lines
in `.machine_readable/REGISTRY.a2ml`. Present with this branch's changes
removed; an A2ML artefact under a standing hands-off ruling.
2. **The pre-push gate itself is broken** — and this branch is a clean
natural experiment for #780. The push died at `Running Workflow SPDX...`
with **no further output**: the silent-death signature. The branch in
#780, which carries the repair, pushed through the identical hook
minutes earlier without complaint. Same repo, same hook path, fix
present vs absent, opposite outcomes.

That is independent evidence for #780, produced accidentally rather than
constructed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_0168Bgpez8mFBcAqYAj8VgEx
@sonarqubecloud

Copy link
Copy Markdown

@hyperpolymath
hyperpolymath merged commit 28849f5 into main Sep 14, 2026
17 of 20 checks passed
@hyperpolymath
hyperpolymath deleted the secqual/fix-spdx-workflow-validator branch September 14, 2026 17:40
@JoshuaJewell

Copy link
Copy Markdown

@coderabbitai Please direct review to the owner @hyperpolymath, not me @JoshuaJewell.

@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@hyperpolymath, I will direct review feedback to you.


✏️ Learnings added
Learnt from: JoshuaJewell
URL: https://github.com/hyperpolymath/standards/pull/780

Timestamp: 2026-09-14T17:45:59.823Z
Learning: For pull requests in the hyperpolymath/standards repository, direct CodeRabbit review feedback to the owner hyperpolymath rather than to JoshuaJewell.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

@hyperpolymath

Copy link
Copy Markdown
Owner Author

Yeah, stop spamming contributors, @CodeRabbit

@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@hyperpolymath Understood. I will direct review feedback for this pull request to you and will not tag contributors.

🐇


🧠 Learnings used
Learnt from: JoshuaJewell
URL: https://github.com/hyperpolymath/standards/pull/780

Timestamp: 2026-09-14T17:45:59.895Z
Learning: For pull requests in the hyperpolymath/standards repository, direct CodeRabbit review feedback to the owner hyperpolymath rather than to JoshuaJewell.

You are interacting with an AI system.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants