Turning on the scripts/tests suite in CI immediately paid for itself: measured on
origin/main, 61 of 178 tests FAIL under jq 1.6, which is what the CI runner ships. They
pass on a dev Mac's jq 1.8.2, which is why this was invisible — and the suite has never
run anywhere else, which is exactly #631's thesis.
Two defects in scripts/check-review-verdict.sh (from #629, the single source of truth
for H10 verdict classification):
1. `contains("<NUL>")` is TRUE FOR EVERY STRING on jq 1.6 — the escape truncates the
literal to the empty string, and every string contains "". So the body guard errored
"NUL in body" on every comment and the H10 grammar was entirely inert on the runner.
Verified against both binaries: 1.6 says true for "hello", 1.7+ says false. Replaced
with `(explode | index(0)) != null`, which involves no regex engine and agrees on
both.
2. A parse error was indistinguishable from "no output". The script used jq's exit code
to separate malformed input from a legitimately empty comment list, treating 4 as
benign — but jq >= 1.7 exits 5 on a parse error while 1.6 exits 4, the same code both
use for "filter produced no output". On 1.6 a garbage API response therefore returned
`absent` instead of an input error. Fixed with an explicit `jq empty` pre-check, which
is non-zero iff the input does not parse regardless of output volume.
Severity: fail-closed, not exploitable. The classifier is only invoked from the
merge-consent hook, which runs on the dev machine (jq 1.8.2), so the live gate is
unaffected. The cost is that #629's hardening was inert on the runner and would have
stayed invisible.
198 tests now pass under BOTH jq 1.8.2 and jq 1.6 (was 138/60 split under 1.6).
This is the third distinct jq-1.6 divergence found in this codebase today (the first was
#643's `jq -e` on empty input). The rule: a shell gate's behaviour is a function of its
interpreter's version — test against the version CI actually runs, or pin it.
Refs #647, #631
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>