docs(845): the canonical record this PR's own doc rule points at was left behind
Build ErsatzTV Image / CI toolchain image resolves (pull_request) Successful in 14s
PR Gates / Docs update reminder (pull_request) Successful in 15s
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 16s
review-verdict/h10 Review-verdict: MERGEABLE @ 65a36f2 (base: main)
Build ErsatzTV Image / Delimiter ban (release path) (pull_request) Successful in 32s
PR Gates / Fix proofs (Proves trailers) (pull_request) Successful in 13s
PR Gates / decisions lifecycle (pull_request) Successful in 22s
Review verdict / Set review-verdict status (pull_request_target) Successful in 11s
PR Gates / Script lint and tests (ruff + pytest) (pull_request) Successful in 9m0s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 12m24s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 8m32s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Skipped
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Successful in 8m40s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 10s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 8s

Round 9 returned MERGEABLE with four LOW/NIT observations; these are the two that are
actionable in the tree.

`docs/decisions/records/ci/script-tests-job.md` still enumerated the `script-tests` input set as
TWO top-level directories after this branch raised both derived copies (`docs/ci-cd.md` and the
comment in `pr-checks.yml`) to three. Nothing was contradicted — "spans more than `scripts/`" is
a true lower bound — but CLAUDE.md says convention lookups resolve at the record, and the
`.gitea/workflows/**` member was missing from exactly the place a reader is told to look. This
branch created that gap, so it closes it. The enumeration is now explicitly a lower bound rather
than a census, since the corpus tests read `docs/` as well, and the stale present-tense "~10s" is
reframed as the figure at decision time rather than a current one.

`scripts/lib/h10-reviewers.sh`: a colon attributed the `-e` scoping and the bash-version
measurement to `review-verdict-vocabulary.sh`, which documents neither. That library documents
the MECHANISM; the scoping is stated and measured here. Reworded.

The two NITs not acted on, and why: the "CORRECTION to the two previous commit messages" in
`c03717f72` actually spans three commits, and a `rc=127` cited there is invocation-dependent
(127 under `bash -c`, 1 from a script file). Both are commit-message-only and immutable. The
same figure appeared in the ersatztv#886 closing comment, which IS editable — it now reports the
stable outcome (which arm ran, whether execution continued) instead of an exit code, with the
invocation dependence stated.

Full suite 1278 passed / 2 skipped; catalog in sync; decisions-validate OK; doc-narrative clean.

fixes #845

Decisions-Edit: yes
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019zUmJZHhVP7kXg5DV237TW
This commit is contained in:
2026-08-30 04:11:09 +02:00
co-authored by Claude Opus 5
parent dc09eceee9
commit 65a36f2867
2 changed files with 11 additions and 6 deletions
@@ -34,9 +34,14 @@ sibling job carries an ignore-rule. Same question, opposite answers, for stated
**Unconditional, not path-filtered.** The suite's real input set spans more than `scripts/`:
`test_post_review_verdict.py` and `test_merge_consent_exemption.py` execute the actual
`scripts/post-review-verdict.sh` and `.claude/hooks/pretooluse-merge-consent.sh`. A `scripts/**`
filter would silently miss a `.claude/hooks/**` edit — and at ~10s a filter buys nothing but a
drift surface. Default checkout depth is sufficient: every `git` call in the suite runs against a
`scripts/post-review-verdict.sh` and `.claude/hooks/pretooluse-merge-consent.sh`, and since #845 the
former also READS `.gitea/workflows/review-verdict.yml` — the verdict writer derives the H10
allow-list from it, so editing that literal changes the suite's outcome. A `scripts/**` filter would
silently miss a `.claude/hooks/**` or `.gitea/workflows/**` edit. The enumeration is a lower bound
rather than a census, deliberately: the corpus tests read `docs/` too, and an exact list here is the
kind that goes stale — which it did, this way, before #845 extended it. The reason is the INPUT SET,
not the cost: a filter was cheap to skip when the suite ran in seconds and is still the wrong axis
now that it runs in minutes. Default checkout depth is sufficient: every `git` call in the suite runs against a
temp repo it creates itself, never this repository's history.
**Verified by measurement, not a green tick** (the #631 Done-when, and `ci.docs-only-detect-shallow-safe`'s
+3 -3
View File
@@ -108,10 +108,10 @@ unset _etv_h10_root
# on that ground alone.
#
# THE SKIP-PAST-THE-GATE ROUTE IS REAL BUT NOT REACHABLE HERE, which is why the sentinel is kept
# without repeating that rationale. `review-verdict-vocabulary.sh` documents it: under `set -u` and
# without repeating that rationale. `review-verdict-vocabulary.sh` documents the mechanism; the
# scoping below is stated here and measured here, on bash 3.2.57 and 5.3.15. Under `set -u` and
# WITHOUT `set -e`, a `${#arr[@]}` on an unset name or a scalar aborts the FUNCTION only, so an
# `if ! validate` gate takes neither branch and execution continues past it — measured on bash 3.2.57
# and 5.3.15. That is its consumer's situation (`check-review-verdict.sh` runs `set -uo pipefail`).
# `if ! validate` gate takes neither branch and execution continues past it. That is its consumer's situation (`check-review-verdict.sh` runs `set -uo pipefail`).
# It is NOT this file's: `post-review-verdict.sh` runs `set -euo pipefail`, where the shell exits on
# any of those shapes. So the sentinel here earns its place on the data-dependency ground alone.
ETV_H10_REVIEWERS_OK=0