Files
ersatztv/scripts/tests/mutation_manifest.py
T
timothyandtimothy 761e575836
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Skipped
Build ErsatzTV Image / CI toolchain image resolves (push) Successful in 6s
Build ErsatzTV Image / Delimiter ban (release path) (push) Successful in 21s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 8m45s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 6m22s
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (push) Successful in 5m56s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 4m33s
fix(787): derive the dropped-step guard's scope, and reconcile its snapshot against the server (#861)
Co-authored-by: Timothy <timothy@noreply.gitea.tblindustries.be>
2026-08-27 22:37:02 +00:00

414 lines
28 KiB
Python

"""The DECLARED clause mutations, one per `MUTATION`-graded row of `docs/guard-inventory.md`.
Data only. The machinery that applies these is `mutation_harness_lib.py`; the checks that keep this
file honest are `test_mutation_harness.py`.
Every entry is declared by hand and none is inferred, which is the whole design constraint from
ersatztv#790: "a harness that guesses which clause of a 90-line hook is *the* guard would manufacture
exactly the confident-but-empty coverage this is meant to prevent". Where a proof test already names
its own clause in source — the BOM guard's `= "efbbbf" ]; then`, `UNSET_CLAUSE`, `prove-fix.sh`'s
`if [ "$RC" -eq 0 ]; then` — the entry reuses THAT string rather than inventing a second one, so a
retarget in either place is caught by the other.
WHY AN ENTRY'S `target` MAY DIFFER FROM ITS `guard`. Some guards here ARE tests
(`scripts/tests/test_*.py`). Disarming such a guard makes it ABSENT rather than red, so
`testing.guard-ships-with-mutation-proof`'s checker-guard exception applies: the mutation goes into
the guarded ARTIFACT — a deleted row, a planted phantom row — and the check must report it. Mutating
a checker's own POPULATION instead is a trap that looks identical and is not: a shrunken population
makes every real row report as PHANTOM, so the proof reddens on a false positive while saying
nothing about the missing-row detection the row claims. `why` states per entry which shape applies
and why; no count is kept here, because a count of the entries below is a second copy of them.
"""
from __future__ import annotations
from scripts.tests.mutation_harness_lib import Mutation
CLAUSE = Mutation.CLAUSE
DETECTOR = Mutation.DETECTOR
MUTATIONS: tuple[Mutation, ...] = (
Mutation(
guard=".claude/hooks/posttooluse-worktree-marker.sh",
target=".claude/hooks/posttooluse-worktree-marker.sh",
clause='printf \'%s\\n\' "$me" > "$abs/.claude-worktree-owner" 2>/dev/null || true',
replacement="true",
proof="test_worktree_ownership_guard.py::test_MUTATION_a_marker_hook_that_stops_WRITING_makes_the_guard_go_quiet",
granularity=CLAUSE,
expect="the UNMUTATED pair did not deny",
why="The marker write is the hook's entire job; without it the guard has nothing to read and "
"fails open. The clause string is the one the proof test itself passes to its `_mutate` helper.",
),
Mutation(
guard=".claude/hooks/pretooluse-bom-guard.sh",
target=".claude/hooks/pretooluse-bom-guard.sh",
clause='= "efbbbf" ]; then',
replacement='= "deadbeef" ]; then',
proof="test_bom_guard_detection.py::test_DISARMING_the_BOM_comparison_stops_detection",
granularity=CLAUSE,
expect="the BOM comparison has moved",
why="The BOM comparison is the guard's only detection logic. Same clause the proof test names.",
),
Mutation(
guard=".claude/hooks/pretooluse-worktree-guard.sh",
target=".claude/hooks/pretooluse-worktree-guard.sh",
clause='marker="$root/.claude-worktree-owner"',
replacement='marker="$root/.claude-worktree-owner-NOTHING-WRITES-THIS"',
proof="test_worktree_ownership_guard.py::test_MUTATION_disarming_the_guards_MARKER_READ_stops_the_deny",
granularity=CLAUSE,
expect="the UNMUTATED guard did not deny",
why="The marker read is what the ownership decision hangs on. Same clause the proof test names.",
),
Mutation(
guard=".husky/pre-push",
target=".husky/pre-push",
clause="unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE",
replacement=": # clause removed by the mutation harness",
proof="test_prepush_unsets_git_env.py::test_MUTATION_DELETING_the_unset_lets_drift_through_silently",
granularity=CLAUSE,
expect="the UNMUTATED pre-push did not catch the drift",
why="Without the unset, every git call the pre-push chain makes is aimed at the repository git "
"exported the environment for, not the one being pushed. `UNSET_CLAUSE` in the proof test.",
),
Mutation(
guard="scripts/build_decisions_catalog.py",
target="scripts/build_decisions_catalog.py",
clause="want.strip() != have.strip()",
replacement="False",
proof="test_build_catalog_check_path.py::test_MUTATION_disarming_the_stale_comparison_stops_detection",
granularity=CLAUSE,
expect="the stale-detection clause has moved or been reworded",
why="`main()`'s only stale-detection logic, per the proof test's own docstring, which uses this "
"exact clause and this exact replacement.",
),
Mutation(
guard="scripts/ci-step-ran.sh",
target="scripts/ci-step-ran.sh",
clause='if ! grep -qxF "$key" "$marker" 2>/dev/null; then',
replacement="if false; then",
proof="test_ci_dropped_step_guard.py::test_dropping_ANY_single_step_FAILS_the_guard",
granularity=CLAUSE,
expect="never having executed",
why="The per-key membership test is what turns a dropped step into a red job; disarmed, every "
"expected key reads as present and the guard passes a run in which nothing executed.",
),
Mutation(
guard="scripts/tests/test_ci_status_context_uniqueness.py",
target=".gitea/workflows/docker-build.yml",
clause=" name: API docs in sync (OpenAPI + endpoint index)",
replacement=" name: Build & test (.NET)",
proof="test_ci_status_context_uniqueness.py::test_no_two_jobs_synthesize_the_SAME_status_context",
granularity=CLAUSE,
expect="these status contexts can be produced by more than one job",
why="THE CHECKER-GUARD SHAPE: this guard IS a test, so disarming its own assertion makes it "
"absent rather than red. Per `testing.guard-ships-with-mutation-proof` the mutation goes into "
"the guarded ARTIFACT instead — here a workflow job renamed to collide with the REQUIRED "
"`Build & test (.NET)` job, which is exactly the evasion the guard exists to catch: two jobs "
"synthesizing one status context that branch protection cannot tell apart, so the required "
"check could be satisfied by the producer whose steps carry no execution markers.",
),
Mutation(
guard="scripts/check-required-contexts.sh",
target="scripts/check-required-contexts.sh",
clause="($rule.status_check_contexts | sort | unique) == $snap",
replacement="true",
proof="test_check_required_contexts.py::test_MUTATION_disarming_the_set_comparison_stops_every_drift_report",
expect="the set-comparison clause has moved or been reworded",
granularity=CLAUSE,
why="The set comparison IS the finding: it is the only thing that turns a live required-check "
"list differing from the committed snapshot into `drift`. Witnessed: unmutated reports "
"`drift` on an added context, the mutant reports `match` — a permanent no-op that would "
"confirm the snapshot fresh forever. The proof asserts the mutant's EXACT verdict "
"`(0, 'match')` rather than merely 'not drift', and ships a positive control for its own "
"tmp layout: an earlier draft copied the script without the classifier it loads beside "
"itself, so the mutant exited 2 with empty stdout and 'not drift' was satisfied by a copy "
"that never ran. The clause string is the one the proof test asserts on before mutating.",
),
Mutation(
guard="scripts/decisions_validate.py",
target="scripts/decisions_validate.py",
clause="wing_faults=record_wing_faults() + yaml_faults,",
replacement="wing_faults=yaml_faults,",
proof="test_decisions_validate.py::test_main_actually_CALLS_the_wing_scan",
granularity=CLAUSE,
expect="a wing fault must fail the validator",
why="The wiring the proof test exists for: deleting this call left the whole suite green while "
"a real block-scalar record vanished under `decisions-validate: OK` (#609).",
),
Mutation(
guard="scripts/prove-fix.sh",
target="scripts/prove-fix.sh",
clause='if [ "$RC" -eq 0 ]; then',
replacement="if false; then",
proof="test_prove_fix.py::test_MUTATION_disarming_the_UNPROVEN_clause_reddens_the_refusal_test",
granularity=CLAUSE,
expect="the clause under mutation is gone",
why="The UNPROVEN branch: a named test that passes WITHOUT the fix must be refused. Same clause "
"the proof test names.",
),
Mutation(
guard="scripts/tests/test_ci_image_pin_population.py",
target="scripts/tests/test_ci_image_pin_population.py",
clause="for name in sorted(TOOLCHAIN_JOBS - set(pinned)):",
replacement="for name in sorted(set()):",
proof="test_ci_image_pin_population.py::test_a_single_job_losing_its_pin_is_DETECTED",
granularity=CLAUSE,
expect="the population check accepted a workflow in which a container job no longer runs",
why="The against-the-registry direction, and the one the other two clauses cannot cover: a job "
"that loses its `container:` block leaves `declared` and `pinned` equal, so only this "
"comparison notices it has moved to the bare runner. This is the clause #790 asked for "
"instead of neutering `pin_population_faults` wholesale.",
),
Mutation(
guard="scripts/tests/test_guard_populations_derive_from_git.py",
target="scripts/tests/test_guard_inventory.py",
clause="if ref in tracked:",
replacement="if (REPO_ROOT / ref).exists():",
proof="test_guard_populations_derive_from_git.py::test_no_derivation_admits_an_untracked_file",
granularity=CLAUSE,
expect="after git stopped tracking them",
why="THE GUARD IS A TEST, so the mutation goes into the guarded ARTIFACT — one of the "
"derivations it watches — rather than into the checker, per the checker-guard exception. "
"The clause is the exact defect this guard was written after: `derived_guard_files` read "
"its CALLERS from the index and then admitted the paths they name on `Path.exists()`, so a "
"tracked workflow naming a script that exists on one machine only entered the population "
"there, red on that checkout and green in CI (#778's third shape, found by cold review "
"inside #806 itself). Note what this mutation does NOT do: on a clean tree the mutated set "
"is identical, so `test_guard_inventory.py`'s own assertions stay green — only narrowing "
"the index, which is what the proof does, separates them. That is why the proof has to "
"remove EVERY member rather than sample one.",
),
Mutation(
guard="scripts/tests/test_optional_request_members.py",
target="scripts/tests/test_optional_request_members.py",
clause='"ArtworkContentTypeModel": (',
replacement='"ArtworkContentTypeModelRENAMED": (',
proof="test_optional_request_members.py::test_every_droppable_request_schema_has_a_stated_disposition",
granularity=CLAUSE,
expect="no disposition written down",
why="THE GUARD IS A TEST, so the mutation goes into the guarded ARTIFACT — here the "
"DISPOSITIONS registry the checker maintains, the same shape as the deleted "
"guard-inventory row below. Renaming the key rather than deleting the entry keeps the "
"module importable, so the red is a real set-equality failure and not an ImportError "
"reddening for the wrong reason. The rename fires BOTH directions — MISSING for the real "
"schema and PHANTOM for the renamed key — which is the correct behaviour and worth stating, "
"since `expect` names only the MISSING half. "
"`ArtworkContentTypeModel` is the right key to name: it is the exact schema #807's "
"hand-written table omitted, because `...Model` reads as a response model while it is in "
"fact reachable from the full-replace PUT /channels/{id}.",
),
Mutation(
guard="scripts/tests/test_guard_inventory.py",
target="docs/guard-inventory.md",
clause="| `.claude/hooks/decisions-guard.sh` | a commit | GUARD | NONE | — |\n",
replacement="",
proof="test_guard_inventory.py::test_the_inventory_covers_exactly_the_guards_that_exist",
granularity=CLAUSE,
expect="these guard files exist but have no row in guard-inventory.md",
why="THE GUARD IS A TEST, so the mutation goes into the guarded ARTIFACT rather than into the "
"checker — disarming a checker makes it absent, not red, and mutating its population instead "
"would only demonstrate a false POSITIVE (a shrunken population reports every real row as "
"phantom) while proving nothing about the missing-row detection the row claims. A deleted "
"row is the defect this guard exists to catch, and it is one of the mutations #774 witnessed "
"by hand.",
),
Mutation(
guard="scripts/tests/test_hook_fire_log.py",
target="scripts/tests/test_hook_fire_log.py",
clause=" return faults\n\n\ndef strip_instrumentation",
replacement=" return []\n\n\ndef strip_instrumentation",
proof="test_hook_fire_log.py::test_a_hook_that_LOSES_its_instrumentation_is_DETECTED",
granularity=DETECTOR,
expect="left the check GREEN. The check is not load-bearing",
why="NO CLAUSE-LEVEL MUTATION REDDENS THIS ONE, and that is a finding rather than a shortcut. "
"`instrumentation_faults` accumulates from four independent arms and a stripped hook trips "
"three of them at once (no sink source, no ETV_HOOK_FIRE_LIB assignment, no begin call), so "
"disarming any single arm leaves the other two answering and the proof test stays green. The "
"whole detector is therefore the smallest mutation this proof can witness — and the surviving "
"single-arm mutation below is re-run every time so that claim is checked, not recited.",
survived_clause=" if not _SOURCES_SINK.search(text):",
survived_replacement=" if False:",
),
Mutation(
guard="scripts/tests/test_remote_state_inventory.py",
target="docs/remote-state-inventory.md",
clause="| `scripts/post-review-verdict.sh` — commit-status write |",
replacement="| `scripts/DELETED-BY-THE-MUTATION-HARNESS.sh` — not a real path |",
proof="test_remote_state_inventory.py::test_every_in_scope_file_has_a_row_and_every_row_names_a_real_file",
granularity=CLAUSE,
expect="in scope but absent from docs/remote-state-inventory.md",
why="THE GUARD IS A TEST, so the mutation goes into the guarded ARTIFACT: a real executable's "
"row is renamed away, which is the MISSING-row defect the row's Blocks column claims — an "
"in-scope file with no classification. Two shapes were tried and rejected. Emptying the "
"guard's `git ls-files` derivation reddens the proof with an IndexError over an empty "
"population: a crash, not a detection. Planting a PHANTOM row reddens "
"`test_MUTATION_PROOF_a_dropped_row_and_a_phantom_row_are_both_detected` by contaminating "
"the fixture that test builds for itself, and proves the opposite direction from the one the "
"row claims. Renaming the row exercises both directions of the production set comparison at "
"once and is matched on the missing half.",
),
Mutation(
guard="scripts/tests/test_mutation_harness.py",
target="scripts/tests/mutation_harness_lib.py",
clause=" if mutation.expect not in diagnostic:",
replacement=" if False:",
proof="test_mutation_harness.py::test_MUTATION_disarming_the_DIAGNOSTIC_gate_accepts_a_red_for_the_wrong_reason",
granularity=CLAUSE,
expect="the UNMUTATED verdict already accepted it, so the mutant proves nothing",
why="The target is not the guard for a structural reason: the harness keeps its machinery in "
"`mutation_harness_lib.py` so a clause of it can be disarmed in an isolated copy at all. The "
"clause is the DIAGNOSTIC gate — the check that a failing proof failed with the diagnostic "
"its row declares. Disarmed, a red for any unrelated reason is certified as a guard doing "
"its job, which is the shape that made two rows in this very file measure nothing. The other "
"gate, the one requiring pytest exit code 1, carries its own proof in "
"`test_MUTATION_disarming_the_EXIT_STATUS_gate_accepts_a_run_that_NEVER_RAN_A_TEST`; the "
"inventory holds one ref per row, so this entry names the stronger of the two.",
),
Mutation(
guard="scripts/ci-toolchain-image-resolves.sh",
target="scripts/ci-toolchain-image-resolves.sh",
clause=" 404)",
replacement=" 4040)",
proof="test_ci_toolchain_image_resolves.py::test_MUTATION_a_deleted_tag_is_reported_as_a_failure",
granularity=CLAUSE,
expect="a deleted tag was not reported as GONE",
why="404 is the ONE answer that establishes the pinned toolchain image is gone; every other "
"code means the check could not run. Both fail the job, so the EXIT CODE does not separate "
"them and the mutation is caught by the DIAGNOSTIC instead: retargeting the arm sends the "
"real outage down the could-not-verify path, which sends an operator to the registry's "
"health rather than to the rebuild that fixes it.",
),
Mutation(
guard="scripts/tests/test_review_verdict_vocabulary.py",
target="scripts/check-review-verdict.sh",
clause="""POS_RE='^review-verdict:[[:space:]]*('"$POS_ALTERNATION"')([[:space:]@]|$)'""",
replacement="""POS_RE='^review-verdict:[[:space:]]*(mergeable|approved|lgtm)([[:space:]@]|$)'""",
proof="test_review_verdict_vocabulary.py::test_a_word_added_to_the_shared_source_reaches_BOTH_sides",
granularity=CLAUSE,
expect="restating the word list rather than deriving it",
why="THE GUARD IS A TEST, so the mutation goes into the guarded ARTIFACT — the read side "
"whose derivation it watches — rather than into the checker, per the checker-guard "
"exception. The replacement is not an invented mutant: it is the LITERAL pre-#788 line, the "
"second hand-written copy this change removed, so the proof is taken against the real "
"predecessor. Mutating the shared vocabulary instead would be the trap that looks "
"identical: emptying or corrupting the word list reddens the proof through the fail-closed "
"validator, which says nothing about whether the read side still DERIVES from it.",
),
Mutation(
guard="scripts/tests/test_workflow_persist_credentials.py",
target=".gitea/workflows/ci-image.yml",
clause=""" persist-credentials: false
# only docker/ci/Dockerfile is needed; no git describe/log here""",
replacement=""" # only docker/ci/Dockerfile is needed; no git describe/log here""",
proof="test_workflow_persist_credentials.py::test_every_actions_checkout_DROPS_the_persisted_credential",
granularity=CLAUSE,
expect="persists a write-capable Authorization header into .git/config",
why="THE GUARD IS A TEST, so the mutation goes into the guarded ARTIFACT rather than into "
"the checker, per the checker-guard exception. The defect is the one the guard exists for: "
"an `actions/checkout` step that simply omits the key, which is what a newly added job gets "
"by default — not an explicit `true`, which nobody writes. `ci-image.yml` carries exactly "
"one checkout, so the clause is unambiguous there; the trailing comment line is part of it "
"only to make the match unique within the file.",
),
)
# ------------------------------------------------------------------------------------------------
# THE OTHER GUARDS — stated per guard, and compared for SET EQUALITY against the inventory
# ------------------------------------------------------------------------------------------------
#
# #790's third Done-when box asks that guards whose mutation cannot be declared be STATED. A reason
# keyed on the row's GRADE would be cheaper and is tautological: a new guard graded NONE inherits one
# automatically and nobody ever looks at that particular guard. A count of them is no better — it
# moves only on net change, so adding one undeclared guard while promoting another leaves it at 22.
#
# So this is keyed on the guard, and `test_every_GUARD_row_is_either_DECLARED_or_STATED_here` asserts
# set equality against the inventory's GUARD rows in both directions. That makes it the same kind of
# hand-maintained-but-machine-checked table as `docs/guard-inventory.md` itself: a new guard cannot
# arrive without someone writing a line here about why it carries no mutation, and a line cannot
# outlive the row it is about.
#
# WHAT `NONE` ACTUALLY MEANS, because the wording matters here: the row nominates no proof ref. It
# does NOT mean the guard is untested. `scripts/ci-prove-ban-detects.sh` is graded NONE and is driven
# end to end by `test_ci_release_path_scan_job.py`. Nominating a proof is a judgement about which
# test is THE proof, which is #775's scope; this file can only verify one afterwards.
UNDECLARED: dict[str, str] = {
# NO GROUPING. An earlier version sorted these into "driven through their deciding path" and
# "not driven at all", and the sort was wrong twice in successive review rounds — in both
# directions, over entries whose own text said the opposite. A category above a list is a second
# classification of the same facts, and it drifts the moment one entry's situation changes. Each
# entry states its own case instead.
#
# THE TWO THINGS THAT GO MISSING ARE DIFFERENT, and which one it is decides where the work goes.
# A guard may be DRIVEN — `test_hook_fire_log.py` executes most hooks through their real deciding
# branch, its matrix asserting instrumentation TRANSPARENCY (the wrapped and unwrapped runs
# agree), never that the decision is right or that a particular clause produced it — and still
# have no NOMINATED proof and no witnessed clause disarm. Nominating one is a judgement about
# which test is THE proof, which is #775's scope; this file can only verify one afterwards. A
# guard nothing executes at all needs the test first.
#
# `NONE` in the inventory means the row nominates no proof ref. It does NOT mean untested.
".claude/hooks/decisions-guard.sh": "Driven to a block and to a pass by test_hook_fire_log.py's "
"constructed cases, which assert transparency rather than the decision. No nominated proof, and "
"no clause disarmed.",
".claude/hooks/prepush-clean-worktree-check.sh": "Driven with a file both modified in the tree "
"and present in the pushed set, by test_hook_fire_log.py, for transparency. No nominated proof, "
"and no clause disarmed.",
".claude/hooks/prepush-donewhen.sh": "Driven against a stub Gitea by test_hook_fire_log.py, so "
"its real blocking path is reached — for transparency. No nominated proof, and no clause "
"disarmed.",
".claude/hooks/prepush-rebase-check.sh": "BEHAVIOUR-ONLY. A named test drives it and "
"test_hook_fire_log.py reaches its behind-origin block, but which clause carries that decision "
"has not been established by disarming one.",
".claude/hooks/pretooluse-agent-ram.sh": "Driven at 5% and 15% free memory through a stubbed "
"`memory_pressure`, by test_hook_fire_log.py, for transparency. No nominated proof, and no "
"clause disarmed.",
".claude/hooks/pretooluse-agent-model.sh": "Driven with and without a `model` in the payload by "
"test_hook_fire_log.py's matrix, for transparency. No nominated proof, and no clause disarmed.",
".claude/hooks/pretooluse-bash-guard.sh": "Driven with an ETV_UPDATE_GOLDENS command and a "
"harmless one by test_hook_fire_log.py's matrix, for transparency. No nominated proof, and no "
"clause disarmed.",
".claude/hooks/pretooluse-nav-guard.sh": "Driven with an `/iptv/` URL by test_hook_fire_log.py's "
"matrix, for transparency. No nominated proof, and no clause disarmed.",
".claude/hooks/design-sync-reminder.sh": "Driven by test_hook_fire_log.py, which gives it its "
"start/finish arguments and works around its self-throttle — for transparency, and never to the "
"one-shot branch that fires on the first Stop after a UI change and then allows. A proof has to "
"model that state transition rather than a single invocation.",
".claude/hooks/pretooluse-merge-consent.sh": "BEHAVIOUR-ONLY. Several suites execute it, but consent "
"is derived from several independent conditions, so which one a given red belongs to has to be "
"established before a clause can be named.",
".husky/commit-msg": "Outside the hook-fire population (that globs `.claude/hooks/*.sh`) and "
"executed by no test: the repositories the suite builds are fresh `git init`s that never install "
"husky, so the hook is absent rather than bypassed. A proof has to install or invoke it.",
".husky/pre-commit": "Runs lint-staged, the decisions guard, the root-PNG check and the format "
"gate. Only the decisions guard has an inventory row of its own — root-PNG and format are INLINE "
"here, so this one row is the whole classification of both, and neither has a proof. Executed by "
"no test, and nothing observes the dispatch itself.",
"scripts/check-kickoff-guard.sh": "Nothing drives it. A proof needs a tree carrying a revived "
"#237 reference, which is cheap and simply not written.",
"scripts/check-review-verdict.sh": "BEHAVIOUR-ONLY. Its named test feeds the real script an "
"input only one clause rejects, which proves it reacts, not that the clause is load-bearing.",
"scripts/ci-detect-already-validated.sh": "Blocks nothing directly — it feeds the skip gate. The "
"consequence a mutation would have to be observed through is a job that skips, which is visible "
"only in a workflow run.",
"scripts/ci-detect-docs-only.sh": "Same shape: it feeds the skip gate rather than blocking, so "
"its effect is visible only in a workflow run, not in this suite.",
"scripts/ci-prove-ban-detects.sh": "Driven end to end by test_ci_release_path_scan_job.py, and "
"it runs a mutation of its own at CI time. Grading it here needs a decision about what a second "
"mutation would add; the row nominates no ref today.",
"scripts/e2e-functional.sh": "Needs a running instance. Its clauses are HTTP contract "
"assertions, so a proof means booting the app — `scripts/e2e-local.sh`'s job, not this "
"harness's.",
"scripts/jq-preflight.sh": "BEHAVIOUR-ONLY. Its named test drives the real script below the "
"version floor; no clause has been disarmed to show the floor comparison is what refuses.",
"scripts/post-review-verdict.sh": "BEHAVIOUR-ONLY, and the most valuable upgrade on this list: "
"it writes the required `review-verdict/h10` status. Its re-read-and-compare has several arms "
"and naming one as THE clause needs the judgement #790 declines to make blind.",
"scripts/pr-changed-files.sh": "BEHAVIOUR-ONLY. Its named test feeds a short page to the real "
"enumeration; the pagination clause has not been disarmed.",
"scripts/tests/test_ci_release_path_scan_job.py": "A GUARD that is a test, so the mutation would "
"have to go into the guarded artifact — the release-path scan job in the workflow. Which "
"weakening of that job is THE defect it exists to catch has not been settled.",
}