diff --git a/docs/decisions/records/ci/verdict-write-retarget-fence.md b/docs/decisions/records/ci/verdict-write-retarget-fence.md index 60bf68c3d..8d485e56d 100644 --- a/docs/decisions/records/ci/verdict-write-retarget-fence.md +++ b/docs/decisions/records/ci/verdict-write-retarget-fence.md @@ -131,7 +131,7 @@ Two reasons it read as deliberate rather than broken, both worth carrying forwar other. The type is now read as a value (`case` over `jq -r 'type'`) rather than through `jq -e`, whose -exit-status semantics already bit this workflow at jq 1.6 (`ci.jq-version-contract`), and both `null` +exit-status semantics already bit this workflow at jq 1.6 (`ci.jq-accepted-version-set`), and both `null` and `[]` are read as the same EMPTY shape (which since #870 ends the walk only at page 1 or at the page cap, not in between). `test_the_fence_TRUSTS_the_count_and_POSTS_when_the_timeline_terminates` is parameterised over both shapes and asserts the POSTED STATUS rather than the log line — on the real diff --git a/docs/defect-shapes-773.md b/docs/defect-shapes-773.md index c398bc38a..10321b612 100644 --- a/docs/defect-shapes-773.md +++ b/docs/defect-shapes-773.md @@ -320,7 +320,7 @@ different fixes, and conflating them is why several of these recurred. | **B** | **Every guard ships with a proof it can go red: delete or disarm *that guard alone* and the suite must fail.** Not "a test exists" — a mutation. | Family B: 9 records (13%) | **rule-present-unenforced** — stated in #685's record and in project memory, enforced nowhere | | **C** | **Dedup by construction.** When two copies of one rule exist, delete one and have both callers invoke it — *unless* deriving the list would feed the judged input back into the judge, which is #711's reasoned exception. | Family C: 9 records (13%) | **rule-missing** | | **D** | **Anything read-then-written against live Gitea/remote state pins a version or uses compare-and-set.** See the honesty note below — this is a fix pattern, and its *detector* is detector A applied to an enumerated inventory of such sites. | Family D: 5 records (7%) | **rule-missing** (partially addressed for the merge path by the per-sha required check from #622) | -| **E** | **Run the check under the interpreter/provider it will actually run under**, and preflight-log the version. | Environment divergence: 7 records | **rule-present** and working — `scripts/jq-preflight.sh` + `ci.jq-version-contract` closed the jq axis after #643/#647/#648. **Unclosed axes**: SQLite-vs-MySQL query semantics (#668), GPU generation (#505), CI-VM speed (#512) | +| **E** | **Run the check under the interpreter/provider it will actually run under**, and preflight-log the version. | Environment divergence: 7 records | **rule-present** and working — `scripts/jq-preflight.sh` + `ci.jq-accepted-version-set` closed the jq axis after #643/#647/#648. **Unclosed axes**: SQLite-vs-MySQL query semantics (#668), GPU generation (#505), CI-VM speed (#512) | | **F** | **Test the DENY path with the production config value.** #756's lesson generalised: a fixture that omits a field tests only the default, so a fail-open in the production value stays invisible. Parametrise the whole matrix, including "config absent". | Fail-open-by-default: ~5 records | **rule-missing** | | **G** | **Full-replace endpoints assert their complete field list in a test**, and prefer reconcile-by-id over delete-and-reinsert where child state exists. | Destructive replace: ~3 records | **rule-missing** | diff --git a/scripts/jq-preflight.sh b/scripts/jq-preflight.sh index ad3999839..50eb6695a 100755 --- a/scripts/jq-preflight.sh +++ b/scripts/jq-preflight.sh @@ -2,11 +2,10 @@ # Make the jq version a job's shell gates run under OBSERVABLE, and any drift LOUD. # # ersatztv#648. Every shell gate in this repo is authored and tested on a developer Mac shipping -# jq 1.8.x. The CI runners shipped jq 1.6 when this was written (2026-07-26); on 2026-10-28 -# both `small` runners reported jq 1.7 (server-management#1080). -# Nothing pinned or checked that, and until ersatztv#631 the one -# thing that could have noticed (scripts/tests/) never ran on the runner. Three independent divergences -# surfaced in a single day: +# jq 1.8.x. The CI runners shipped jq 1.6 when this was written (2026-07-26); on 2026-10-28 both +# `small` runners reported jq 1.7 (server-management#1080). Nothing pinned or checked that, and until +# ersatztv#631 the one thing that could have noticed (scripts/tests/) never ran on the runner. Three +# independent divergences surfaced in a single day: # # ersatztv#643 `jq -e` over EMPTY input -> exit 4 on 1.8, exit 0 on 1.6 (a transport failure # passed the docs-only pagination guard) @@ -28,9 +27,9 @@ # So the contract is the other way round: 1.6 is the FLOOR every gate must work on. Both `small` # runners map to the floating runner-images:ubuntu-latest image with no digest pin, so each host keeps # whatever it last pulled (2026-10-28: both measured hosts reported 1.7; a host may hold an older -# pull). No known runner reports 1.6, so real-jq 1.6 coverage of `scripts/tests/` is currently -# absent; 1.6 stays accepted only because the tag is unpinned. server-management#1080 proposes a fleet digest pin; when it settles one version, narrow -# the `--expect` set in pr-checks.yml to that version. +# pull). No known runner reports 1.6, so real-jq 1.6 coverage of `scripts/tests/` is currently absent; +# 1.6 stays accepted only because the tag is unpinned. server-management#1080 proposes a fleet digest +# pin; when it settles one version, narrow the `--expect` set in pr-checks.yml to that version. # # TWO MODES, deliberately asymmetric: # @@ -51,9 +50,10 @@ set -euo pipefail -# The lowest jq every shell gate in this repo must run correctly on. Do not raise this without -# confirming the CI runner has actually been upgraded first — the runner, not the dev Mac, is the -# binding constraint. +# The lowest jq every shell gate in this repo must run correctly on. Do not raise this until every +# runner that can take a job is confirmed above it — the runner fleet, not the dev Mac, is the binding +# constraint, and while the runner image tag is unpinned (server-management#1080) a host may still hold +# a 1.6 pull. MIN_VERSION="1.6" expect=""