test(mutants,assets): a mutant is judged by the suite that owns it, 45 of 45

The runner could only run one test file, which is why PM's three
survivors from `43331fc` could not be added: two are held by the gate's
row 3 and one by the soft-hyphen door's suite. A mutant now names its
suite; the catalogue goes 39 to 45.

X3 and X4 rewritten against the code as it now stands -- a mutant table
is a copy of the code it mutates, and this round moved the lines both of
them quoted. X6 is the defeated state exactly, X7 cuts the ledger off at
its source, X8 removes the cursor rule, P6/P11/P12 are PM's three.

Two survivors on the first run, both findings, both closed:
- X4 survived because every forgery arm now fails on the ledger check
  before the binding is reached. An arm was added where the run DID book
  the pair and the block stating it points at another picture.
- X5 survived the WHOLE suite -- 2134 passed with the disarming removed
  -- because a document-supplied field can no longer reach the gate. The
  property is about the BUNDLE and not about one judge, so it is kept and
  measured in `tests/test_assets.py`, with a known-positive counting the
  run's own two fields on the same expression.

killed 45 of 45, exit 0. Report, CHANGELOG and CLAUDE.md written.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-19 19:33:23 +02:00
commit 44ad845e29
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
6 changed files with 431 additions and 17 deletions

View file

@ -63,6 +63,12 @@ class Mutant:
#: a chance to see the disagreement.
occurrences: int = 1
first_only: bool = False
#: The test file this mutant is judged by. Defaults to the gate's own
#: suite, which every mutant used until 2026-09-19 -- which is why the
#: three PM found in `43331fc` could not be added here: they are held by
#: the SHY door's suite and by the gate's row 3, and a runner that can
#: only run one file cannot ask about them.
suite: str = SUITE
MUTANTS: tuple[Mutant, ...] = (
@ -304,31 +310,82 @@ MUTANTS: tuple[Mutant, ...] = (
Mutant(
"X3 the conversion claim is read from anywhere in the bundle text",
GATE,
""" found: dict[str, str] = {}
for pointer in _POINTER.finditer(bundle_text):
clause = _CONVERSION.search(pointer.group("detail"))
if clause is None:
continue
after = clause.group("after")
# The claim has to be about the picture the block points at. A clause
# standing in one asset's block while naming another's digest is a
# sentence nothing in this build writes.
if pointer.group("asset").startswith(after[:12]):
found[clause.group("before")] = after
return found""",
""" return {m.group("before"): m.group("after") for m in _CONVERSION.finditer(bundle_text)}""",
""" declared = _declared_conversions(build)
if not declared:
return {}
found: dict[str, str] = {}""",
""" declared = _declared_conversions(build)
del declared
return {
m.group("before"): m.group("after") for m in _CONVERSION.finditer(build.bundle_text)
}
found: dict[str, str] = {}""",
),
Mutant(
"X4 the claim need not be about the asset its block points at",
GATE,
'if pointer.group("asset").startswith(after[:12]):',
"if True:",
'if (before, after) in declared and pointer.group("asset").startswith(after[:12]):',
"if (before, after) in declared:",
),
# X5 moved suites 2026-09-19. It was felled by the gate's own suite while
# the gate read its claim out of the bundle; once the claim came from the
# run's ledger, a document-supplied field could not reach the gate at all
# and the mutant survived the WHOLE suite -- measured, 2134 passed. The
# property did not stop mattering: the line is in every concept body, and
# a bundle must not state a conversion the run never performed. It is
# measured where it lives now.
Mutant(
"X5 a document-supplied label may emit a checksum field",
"src/llm_ingestion_okf/assets.py",
'return _CHECKSUM_FIELD.sub("sha256 ", collapsed.replace("[", "(").replace("]", ")"))',
'return collapsed.replace("[", "(").replace("]", ")")',
suite="tests/test_assets.py",
),
# PM's sjekkpunkt 2026-09-19 on `ae441ab`: the round above bound the claim
# to a pointer BLOCK, and one HTML file with two `<p>` elements writes one.
# X6 is that state exactly -- the shape believed without the run's ledger.
Mutant(
"X6 a pointer block is believed without the run having booked it",
GATE,
'if (before, after) in declared and pointer.group("asset").startswith(after[:12]):',
'if pointer.group("asset").startswith(after[:12]):',
),
Mutant(
"X7 the run's ledger is read from the bundle instead of the accounting",
GATE,
" accounting = build.accounting\n if not isinstance(accounting, dict):",
" accounting = None\n if not isinstance(accounting, dict):",
),
# The RLE8 cursor rule, judged by the file that measures it against an
# independent decoder. Without a per-mutant suite this could not be asked
# from here at all.
Mutant(
"X8 a stream may end before the cursor reaches the frame",
"src/llm_ingestion_okf/assets.py",
" if y < height - 1 or (y == height - 1 and x < width):",
" if False:",
suite="tests/test_asset_viewable.py",
),
# PM's three survivors from `43331fc`, held until now by ordinary tests
# and not by what runs AS the gate.
Mutant(
"P6 the normalisation door removes U+00A0 as well",
"src/llm_ingestion_okf/extract.py",
' return text.replace(SOFT_HYPHEN, ""), removed',
' return text.replace(SOFT_HYPHEN, "").replace("\\u00a0", ""), removed',
suite="tests/test_soft_hyphen_door.py",
),
Mutant(
"P11 row 3's detail line drops the refused count",
GATE,
'f"unverified={u.unverified} invalid={u.invalid} refused={u.refused}"',
'f"unverified={u.unverified} invalid={u.invalid}"',
),
Mutant(
"P12 row 3's reason drops the refused-whole clause",
GATE,
'f"{refused} element(s) lost with {refused_docs} of {documents} document(s) refused whole"',
'f"{refused} element(s) lost"',
),
)
@ -378,7 +435,16 @@ def main(argv: list[str] | None = None) -> int:
environment["PYTHONPATH"] = str(root / "src")
try:
run = subprocess.run(
[sys.executable, "-m", "pytest", SUITE, "-q", "-x", "-p", "no:cacheprovider"],
[
sys.executable,
"-m",
"pytest",
mutant.suite,
"-q",
"-x",
"-p",
"no:cacheprovider",
],
cwd=root,
capture_output=True,
text=True,