fix(885): the guard states both invariants, and the second one is executed

The two `docs/guard-inventory.md` rows and the module docstring described
`test_workflow_persist_credentials.py` as the `actions/checkout` guard only. The
deferral to #909 rested on `docs/guard-inventory.md` being held by the session
working #881; that issue is closed and its PR is the commit this branch is rebased
onto, so the file is free and the edit belongs here under docs-update-is-part-of-done.

`MUTATIONS` keys at most one declared clause mutation per guard FILE
(`test_the_manifest_covers_exactly_the_MUTATION_rows` asserts `len(MUTATIONS) ==
len(declared)`), and the grading row's proof-ref column is compared against it, so
the route invariant cannot take a second `MUTATIONS` row. It takes a `CLAIMS` entry
instead — the population #881 widened this file to carry — bound to the inventory
sentence that states it: deleting `build`'s `if: github.event_name != 'pull_request'`
from the shipped `docker-build.yml` is applied to a sandbox copy every run and the
named proof is required to redden with the collector's own wording.

That grows the `CLAIMS` population from three entries to four, which invalidates the
cost span `testing.mutation-claims-are-executed` measured over three. Re-taking it
here produced 54.3s/149.5s, 81.6s/78.7s and 114.3s/84.2s across three A/B pairs with
other builds on the host — two inverted, so the load dominates the signal. The record
now says the span is a lower bound and that a re-measurement is owed on a quiet
machine, rather than carrying a scaled or invented number.

Decisions-Edit: yes
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015QqCpYFsKgnAnx6jVwrKiV
This commit is contained in:
2026-09-05 15:15:43 +02:00
co-authored by Claude Fable 5.1
parent e656583247
commit fa06da21f3
5 changed files with 45 additions and 12 deletions
@@ -92,14 +92,16 @@ expressions a human can see are equivalent, leaves the job IN the population. Pa
after repeated defects from that one mechanism; a pin can only ever be too demanding, which is the
safe direction, and widening it is then a deliberate edit rather than an accident.
**One inventory row still understates this, tracked as #909.** `docs/guard-inventory.md` describes
`scripts/tests/test_workflow_persist_credentials.py` as the `actions/checkout` guard only, while it
now also holds this invariant with its own derived population and mutation proofs. Nothing goes red
on that: `test_guard_inventory.py` asserts set equality over FILES, and the file was already listed,
so a row whose PROSE decays is invisible to it. Do not read it as a complete description of what its
subject checks. #909's other half — `docs/remote-state-inventory.md`'s row for
`scripts/ci-toolchain-image-resolves.sh`, which listed "an unusable credential" among the shapes that
fail the job — is corrected in this change.
**The guard now holds TWO invariants, and only one grade can name a declared clause.**
`scripts/tests/test_workflow_persist_credentials.py` holds the `actions/checkout` invariant (#835)
and this one, on the same index-derived population. Both of its `docs/guard-inventory.md` rows say
so; nothing MAKES them say so, because `test_guard_inventory.py` asserts set equality over FILES and
the file was already listed, so a row whose PROSE decays stays green. The manifest keys at most one
`MUTATIONS` entry per guard FILE, so the declared clause there stands for the checkout half and the
route half is executed through a `CLAIMS` entry instead, bound to the inventory sentence that states
it (`testing.mutation-claims-are-executed`). That is the same obligation reached through the
population #881 widened, not a weaker one: the clause is declared by hand, applied to a sandbox copy
and its result read on every run, exactly as an inventory row's is.
**The residual, stated so nobody cites this record as a boundary.** Head-supplied YAML can still name
every secret in the store — the committed workflows' references were never the limit
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+26
View File
@@ -679,4 +679,30 @@ CLAIMS: tuple[Claim, ...] = (
"The mutation drops `record_wing_faults()` from the call and keeps `yaml_faults`, which is "
"the deletion the sentence describes rather than a caricature of it.",
),
Claim(
site="docs/guard-inventory.md",
quote=(
"deleting `build`'s `if: github.event_name != 'pull_request'` from the shipped "
"`docker-build.yml` reddens `test_no_PULL_REQUEST_route_job_names_a_STORED_secret`"
),
target=".gitea/workflows/docker-build.yml",
clause=" if: github.event_name != 'pull_request'",
replacement=" # if: clause removed by the mutation harness",
proof="test_workflow_persist_credentials.py::test_no_PULL_REQUEST_route_job_names_a_STORED_secret",
outcome=RED,
expect="job `build` names stored secret(s) on the pull_request route",
why="`MUTATIONS` is keyed one entry per GUARD FILE, and "
"`scripts/tests/test_workflow_persist_credentials.py` holds TWO invariants since #885 — the "
"`actions/checkout` one its declared entry stands for, and the PR-route stored-secret one. "
"The second would otherwise have no executed mutation at all, so the inventory row states it "
"in prose and the prose is bound here, which is the population #881 widened this file to "
"cover. The clause is the ONE thing keeping `build` — the last job naming `REGISTRY_PASSWORD` "
"— off the head-authored route, so the mutation is the defect the invariant exists for and "
"not a caricature: with it deleted the collector must name that job and that secret. The "
"mutation is driven into the SHIPPED workflow rather than a fixture for the same reason the "
"module's own two mutation tests are: a synthetic document would only prove the collector "
"works on YAML this repo does not run. The declared diagnostic is the fault's own wording "
"rather than the assertion's, because a red anywhere else in that test — a parse error, an "
"empty population — prints a different message and is evidence about nothing.",
),
)
@@ -1,4 +1,9 @@
"""Every `actions/checkout` drops the persisted credential (ersatztv#835).
"""TWO credential invariants over one derived workflow population.
Every `actions/checkout` drops the persisted credential (ersatztv#835) — below — and no job
the `pull_request` trigger reaches, nor the workflow scope outside `jobs:`, names a stored
secret (ersatztv#885). The second is documented at its own banner further down, where the
population it adds to this one is derived; what follows here is the first.
WHAT THIS IS PROTECTING. `actions/checkout` writes an `Authorization` header into `.git/config`
unless `persist-credentials: false` is set, and this instance's Actions default token permission is