docs(780): one residual scope claim, and two wordings the measurement outgrew
Build ErsatzTV Image / Delimiter ban (release path) (pull_request) Successful in 15s
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 12s
PR Gates / Docs update reminder (pull_request) Successful in 18s
PR Gates / Fix proofs (Proves trailers) (pull_request) Successful in 10s
PR Gates / decisions lifecycle (pull_request) Successful in 23s
Review verdict / Set review-verdict status (pull_request_target) Successful in 12s
PR Gates / Script lint and tests (ruff + pytest) (pull_request) Successful in 4m20s
review-verdict/h10 Review-verdict: MERGEABLE @ 7b28fc0 (base: main)
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m22s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 6m12s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Skipped
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Successful in 5m54s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 9s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 7s

Round-4 verification came back MERGEABLE with no unexecuted claim introduced.
Three leftovers from it:

- The record still said an `exclude` 'defeats it in three different config
  scopes' with `it` bound to `ruff check .` — measured, `[format]` does not
  defeat `check`. It defeats DISCOVERY in three scopes, two per command, which
  is what the table three lines below already showed.
- The force-exclude matrix row listed the non-shipped (flag-off) values first
  under a column headed 'what ships'. Swapped.
- 'silences the check step entirely / green over any violation' — a syntax error
  still reds under `select = []` (measured). It silences every selected RULE.

Refs: #780
Decisions-Edit: yes
This commit is contained in:
2026-08-22 01:48:33 +02:00
parent 27603b215e
commit 7b28fc0e66
3 changed files with 11 additions and 10 deletions
+6 -5
View File
@@ -381,8 +381,8 @@ jobs:
# top-level GREEN GREEN red red
# [lint] GREEN red red red
# [format] red GREEN red red
# top + force-exclude GREEN GREEN GREEN GREEN <- without the flag
# red red <- with it
# top + force-exclude GREEN GREEN red red <- with the flag
# GREEN GREEN <- without it
#
# Only the top-level scope empties BOTH discovery commands; `[lint]` empties `check` and
# `[format]` empties `format --check`, so in those two the job would still redden on the other
@@ -390,7 +390,7 @@ jobs:
# `extend-exclude` and a nested `scripts/ruff.toml` behave the same way and are equally inert
# against the explicit form. The last row is the whole reason for `--no-force-exclude`:
# `force-exclude = true` re-applies excludes to explicitly-passed paths, and is the one setting
# that would otherwise defeat this.
# that reaches explicitly-passed paths at all.
#
# `ruff check .` over an empty tree exits **0** with only a stderr warning, so every GREEN above
# is a gate that was switched off without a red.
@@ -399,8 +399,9 @@ jobs:
# (docs/decisions/records/testing/guard-derives-population-from-source.md) and covers
# tracked-but-gitignored files, which `ruff check .` skips. The empty-population arm is the
# anti-vacuity check: a completeness check whose population is empty reports that it proved
# everything. What it does NOT cover: an emptied RULE set. `select = []` silences the `ruff
# check` step entirely — green over any violation, still printing a reassuring file count.
# everything. What it does NOT cover: an emptied RULE set. `select = []` silences every selected
# rule, so the `ruff check` step goes green over any lint violation (a syntax error still reds)
# while printing a reassuring file count.
# `ruff format --check` is unaffected, because formatting is not rule-selected. So half the
# gate is killable by a config edit, and only a human reading that edit catches it.
- name: Lint scripts (ruff check)
@@ -37,8 +37,8 @@ eight known lines — and this is the only Python lint the repo runs, so nothing
token pasted into a fixture next year. Per-site `# noqa: S105` costs the same and keeps the rule live.
**The population comes from `git ls-files`, not from ruff's discovery, and that is the load-bearing
part.** `ruff check .` reports on what it *discovers*, and an `exclude` defeats it in three different
config scopes — including `[format]`, which is where an appended line lands by TOML rules (two of the
part.** `ruff check .` reports on what it *discovers*, and an `exclude` defeats discovery in three
different config scopes — including `[format]`, which is where an appended line lands by TOML rules (two of the
three defeat each command). Measured with ruff 0.12.11 and `exclude = ["scripts/**"]`, against a
tracked file holding an unused import, a hardcoded credential and a formatting error. The pattern
matters: `exclude` is matched per FILE, so a bare `["scripts"]` works at the top level but matches
@@ -62,8 +62,8 @@ green gate, not a red one. Enumerating from git also covers tracked-but-gitignor
discovery skips (`git add -f` under an ignored path is established practice here).
**The rule set is not covered, and that is a stated limit rather than an oversight.** `select = []`
silences the `ruff check` step entirely — green over any violation, still printing a reassuring file
count. `ruff format --check` is unaffected, because formatting is not rule-selected. So the population
silences every selected rule, so the `ruff check` step goes green over any lint violation (a syntax
error still reds) while still printing a reassuring file count. `ruff format --check` is unaffected, because formatting is not rule-selected. So the population
arm makes an emptied *file* set loud, nothing makes an emptied *rule* set loud, and half the gate is
killable by a config edit only a reviewer catches.
@@ -29,7 +29,7 @@ it lives in, so **a job under a standing ignore-rule can host no real gate.**
This does not conflict with `ci.ui-e2e-harness` ("never their own job"). That record folds UI-E2E
into `functional-e2e` because the specs need an app the job has *already booted* — sharing expensive
setup. Here there is no shared setup to reuse (a checkout plus `pip install pytest pyyaml ruff`), and the
setup. Here there is no shared setup to reuse (a checkout plus `pip install pytest pyyaml` and a pinned ruff), and the
sibling job carries an ignore-rule. Same question, opposite answers, for stated reasons.
**Unconditional, not path-filtered.** The suite's real input set spans more than `scripts/`: