Build ErsatzTV Image / Delimiter ban (release path) (pull_request) Successful in 20s
PR Gates / Docs update reminder (pull_request) Successful in 24s
PR Gates / Fix proofs (Proves trailers) (pull_request) Successful in 26s
PR Gates / decisions lifecycle (pull_request) Successful in 31s
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 10s
review-verdict/h10 Awaiting review verdict for fe578c5
Review verdict / Set review-verdict status (pull_request_target) Successful in 14s
PR Gates / Script tests (pytest) (pull_request) Successful in 4m8s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 9m34s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 6m38s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Skipped
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Successful in 6m19s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 7s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Canceled after 4s
#773 §3 Family D is the one class its taxonomy had no bucket for: a check and the action it authorizes are separated in time over state that can change in between, with nothing pinning a version (#536, #622, #632, #706, #707). The repo had already solved this twice without noticing it was one problem — a compare-exchange claim in-process (ffmpeg.work-ahead-slot-atomic) and RFC 7232 If-Match across /api/v1 (concurrency.ifmatch-rfc7232) — and then solved it a third time from scratch for the tooling at #706/#707. Hence a class-level record rather than a fourth per-instance one. What the enumeration actually found, which none of the five records predicted: the merge-consent hook's scheduled-auto-merge path is safe only because `review-verdict/h10` is a REQUIRED status check on main. That is branch-protection CONFIG, it lives outside this repo, nothing compared the two, and the hook asserted it in a comment AND in the grant reason a human reads. Switch the context off and every word of that sentence is false while the hook keeps printing it and keeps auto-granting. The hook now reads the branch protection and treats it as three outcomes: present proceeds, unreadable asks, absent denies. Two defects were caught by the new checks themselves rather than by review: - the population test found .gitea/workflows/dependency-scan.yml absent from the first draft of the inventory (a sixth workflow the recon slice never listed); - self-review found the guard denying with a confident wrong reason when jq errors one level down on a malformed contexts member, so the word is now matched exhaustively rather than compared against "yes". Same swallow that survived the first fix in the #632 base-change guard. Detector D has no plausible linter, so the detector is detector A applied to an enumerated inventory: docs/remote-state-inventory.md classifies every in-scope executable, and scripts/tests/test_remote_state_inventory.py derives the population from the filesystem and asserts set equality both ways. Deferred with reasons stated in the inventory: select-queue.sh (advisory, authorizes no write), ci-detect-already-validated.sh (skip elides re-validation only, the image still builds), review-verdict.yml's status POST (Gitea offers no conditional write; already fenced by #706's retarget counter). Mutation proofs witnessed for both new guards, clause-level, not whole-file. fixes #778 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>