test(fixtures): replace sector-specific example material with generic, fictitious examples — green

Every fixture, test document, tool example and document now uses an invented
kitchen-and-baking handbook series, written in this repository. The package's
behaviour is unchanged; src/ changes are comments and help text only.

- Generated fixtures are regenerated from their generators. Their structural
  counts are identical before and after: elements, images, rows, cells,
  headings, bookmarks and the witness inventory's per-document totals. The
  image-inbox and accounting documents are renamed kapittel-84-*.
- tools/okf_accounting_gate.py: the two options that named one real corpus
  each are replaced by a generic, repeatable --corpus PATH with no default.
  Row 5 compares the PDF pair alone. Gate verdict unchanged: RED rows 2, 3, 6.
- tools/okf_witness.py: the STS JSON reader for one publisher's delivery is
  removed, along with its three twins and five tests. The mutation harness
  loses W09.
- docs/: 13 dated reports that documented runs on a retired reference corpus
  are removed, and 40 are neutralized. Dead links are removed, and no new
  dangling path is introduced.
- The synthetic MCP-gate corpus and the residual probe words are neutral.

Valgt: keep the `okf quality --fasit` bar value (the measured fraction, one corpus) and
rewrite only its provenance, because the verdict stays unchanged and the
number names nothing.

Term check with the local list: 0 of 411 tracked files, 0 file names, 0 of
27 binary fixtures. Suite after git add: 2457 passed, 1 skipped. The base
tree had 2460 passed and 2 skipped; five tests went with the JSON reader and
four were added by the term check. ruff, ruff format and mypy --strict src/
are clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-23 13:54:57 +02:00
commit 9d1f4b14ed
174 changed files with 1889 additions and 6512 deletions

View file

@ -63,8 +63,7 @@ from pathlib import Path
fixtures = Path({fixtures!r})
w.witness_inbox(fixtures / "corpus")
w.witness_inbox(fixtures / "rejected")
w.count_sts_json((fixtures / "witness" / "prosess-84-sts.twin.json").read_bytes())
w.pdf_poppler(fixtures / "corpus" / "prosess-84-tabell.pdf")
w.pdf_poppler(fixtures / "corpus" / "kapittel-84-tabell.pdf")
print(sorted(m for m in sys.modules if m.split(".")[0] == "llm_ingestion_okf"))
"""
@ -127,7 +126,7 @@ HAND_COUNTS = {
},
"krav-rikt-tekstformat.rtf": {"cell": 56, "image": 0, "paragraph": 3, "table_row": 24},
"bilde.rtf": {"cell": 0, "image": 1, "paragraph": 2, "table_row": 0},
"prosess-84-sts.xml": {
"kapittel-84-sts.xml": {
"cell": 0,
"citation": 0,
"figure": 0,
@ -192,8 +191,8 @@ HAND_COUNTS = {
"logg.txt": {"line": 4, "paragraph": 3},
"mengder.csv": {"cell": 6, "header_cell": 3, "row": 2},
"parametre.json": {"key": 6, "value": 6},
"prosess-84-tabell.pdf": {"image": 2, "page": 1},
"prosess-84-web.html": {
"kapittel-84-tabell.pdf": {"image": 2, "page": 1},
"kapittel-84-web.html": {
"cell": 0,
"heading": 1,
"image": 3,
@ -201,7 +200,7 @@ HAND_COUNTS = {
"paragraph": 3,
"table": 0,
},
"prosess-84-notat.docx": {
"kapittel-84-notat.docx": {
"cell": 0,
"comment": 0,
"endnote": 0,
@ -213,7 +212,7 @@ HAND_COUNTS = {
"table": 0,
"text_box": 0,
},
"prosess-84-presentasjon.pptx": {
"kapittel-84-presentasjon.pptx": {
"cell": 0,
"hidden_slide": 0,
"image": 1,
@ -351,103 +350,6 @@ def test_the_sts_witness_counts_citations_formulas_and_figure_captions() -> None
assert inventory.elements[role] > 0, role
def test_the_json_role_map_reads_a_prefixed_tag_and_the_publishers_figcaption() -> None:
"""H3, measured by PM on N200 2026-09-18: the delivery carries 74
`mml:math` and 49 `figcaption`, and the witness counted 0 and 0.
`count_sts_xml` takes every tag through `_local`, which strips both a
Clark-notation namespace and a prefix; `count_sts_json` compared the RAW
string, so `mml:math` never reached `tag == "math"`. And the publisher's
JSON puts a figure's caption in `figcaption` under the `graphic`, not in
the `fig/caption` NISO-STS writes -- measured over the eight deliveries on
this machine: 430 `figcaption`, 0 of them under a `caption`.
The fixture is the JSON half of `sts-rikt.xml`'s family, and both halves
of the defect are in it."""
twin = gate.FIXTURES / "witness" / "sts-mml-and-figcaption.twin.json"
counts = witness.count_sts_json(twin.read_bytes()).counts
assert counts["math"] == 1
assert counts["figure_caption"] == 1
def test_a_prefixed_tag_that_names_no_role_stays_uncounted() -> None:
"""The known-negative for stripping the prefix: N101 ships 568 `tbx:`
tags, and not one of their local names is a role. A rule that generalises
is measured against what it must NOT take."""
for tag in ("tbx:term", "tbx:definition", "tbx:note", "tbx:termEntry"):
assert witness._sts_role_json(witness._local(tag), "sec", "body") is None
#: THE ONE PLACE THIS NUMBER LIVES. The count itself was measured over the
#: delivery by a `skipif`-gated test that read a file only this machine has
#: (removed 2026-09-21 with the retired test track), so on a fresh clone the
#: four published sentences were unguarded again, which is how
#: 574 survived in four docstrings until PM counted it. The guard below needs
#: no delivery: it reads the published sentences and holds them to each other
#: and to this constant.
N101_TBX_TAGS = 568
#: The published sentence, in the five files that carry it. Anchored on `N101`
#: and on the literal `tbx:` that follows the number, so it cannot match some
#: other count standing nearby.
_PUBLISHED_TBX = re.compile(r"N101(?:'s)?[^.]{0,80}?\*{0,2}(\d+)\*{0,2}\s*`tbx:`")
#: Every file that states it. A sentence moving to a sixth file without being
#: added here is the residual, and it is the same residual the constant
#: replaces one level down -- this list is what keeps the number readable in
#: one place rather than five.
_TBX_PUBLISHERS = (
"CHANGELOG.md",
"CLAUDE.md",
"tools/okf_witness.py",
"tests/test_accounting_gate.py",
"docs/2026-09-19-regnskapsgaten-rest-og-normaliseringsdoren.md",
)
def test_the_published_tbx_count_is_one_number_and_needs_no_delivery() -> None:
"""The published strings are held to each other, on any machine.
This is the half the measurement could not cover. The delivery count
cannot run where the delivery is absent, and an assertion that skips
guards nothing. Editing `CLAUDE.md` to
600 tomorrow is red here, on a fresh clone, with no corpus.
It proves nothing about the WORLD -- five files agreeing is agreement, not
a count -- and this test only holds the sentences to the constant it
asserts.
"""
root = Path(__file__).resolve().parents[1]
for name in _TBX_PUBLISHERS:
path = root / name
assert path.is_file(), f"{name}: the file that publishes the count is gone"
# Whitespace-folded first: the sentence wraps differently in each file.
text = " ".join(path.read_text(encoding="utf-8").split())
found = _PUBLISHED_TBX.findall(text)
assert found, f"{name}: the published sentence is gone, or no longer says `tbx:`"
assert [int(value) for value in found] == [N101_TBX_TAGS] * len(found), (
f"{name}: publishes {found}, and the number this repository stands behind "
f"is {N101_TBX_TAGS}"
)
# KNOWN-POSITIVE for the pattern itself: it must find a number that is
# NOT the published one, or the loop above could be passing over nothing.
# Assembled from pieces so the scan above does not read this line as a
# sixth publisher -- written whole, it made the guard red on its own
# fixture, which is also the clearest demonstration that the scan reads
# the file it is pointed at.
wrong = "N101 ships " + "574" + " `tbx:` tags"
assert _PUBLISHED_TBX.findall(wrong) == ["574"]
def test_the_two_sts_role_maps_are_written_twice_and_not_shared() -> None:
"""M-2: both STS witnesses went through ONE `_sts_role`, so row 5 could
never see a hole in it. Two maps, each written for its own delivery."""
assert witness._sts_role_xml is not witness._sts_role_json
assert "_sts_role_json" not in witness._sts_role_xml.__code__.co_names
assert "_sts_role_xml" not in witness._sts_role_json.__code__.co_names
def test_every_witnessed_type_says_what_it_does_not_count() -> None:
assert set(witness.NOT_COUNTED) == set(witness.WITNESSED_SUFFIXES)
assert all(witness.NOT_COUNTED[suffix] for suffix in witness.WITNESSED_SUFFIXES)
@ -543,7 +445,8 @@ def test_the_judge_proves_a_CONVERTED_image_was_carried(tmp_path: Path) -> None:
those bytes. That is right for an image carried verbatim and wrong for one
the build converts: the run did carry the picture, under a new digest the
bundle STATES, and a judge that only knew the first rule reported 19 of
R761's 50 images as claimed-and-not-found the day the conversion landed.
a real delivery's 50 images as claimed-and-not-found the day the conversion
landed.
The second route is not the build's naming rule restated. The gate reads
the two digests the bundle writes, and then HASHES the asset itself: the
@ -656,7 +559,7 @@ def test_a_document_cannot_forge_a_conversion_claim(tmp_path: Path) -> None:
other = "0" * 64
arms = {
"plain body text": f"{pointer}\n\nProsess 84. {clause}. Se figuren over.\n",
"plain body text": f"{pointer}\n\nKapittel 84. {clause}. Se figuren over.\n",
"a table cell": f"{pointer}\n\n| Krav | Kilde |\n| --- | --- |\n| 84-1 | {clause} |\n",
"a figure caption": f"{pointer}\n\nFigur 84-1 -- {clause}\n",
"a fenced code block": f"{pointer}\n\n```\n{clause}\n```\n",
@ -748,10 +651,10 @@ def test_the_build_never_writes_a_claim_the_document_supplied(tmp_path: Path) ->
before = gate._sha256(never_carried)
after = gate._sha256(real)
clause = f"converted from image/bmp sha256:{before} to image/png sha256:{after}"
(corpus / "prosess.html").write_text(
"<!doctype html>\n<html><head><title>Prosess 84</title></head>\n<body>\n"
"<h1>84 Konstruksjoner av betong</h1>\n"
"<p>Toleranseklassene staar i figuren under.</p>\n"
(corpus / "kapittel.html").write_text(
"<!doctype html>\n<html><head><title>Kapittel 84</title></head>\n<body>\n"
"<h1>84 Brød og boller</h1>\n"
"<p>Hevetidsklassene staar i figuren under.</p>\n"
f'<img src="graphics/ekte.png" alt="Figur 84-1 -- {clause}">\n'
"<p>Og den store figuren:</p>\n"
'<img src="graphics/figur.bmp" alt="Figur 84-2">\n'
@ -803,9 +706,9 @@ def _forgery_corpus(root: Path, body: str, note: str | None = None) -> tuple[Pat
(inbox / "graphics" / "figur.bmp").write_bytes(_small_bmp())
before = gate._sha256(never_carried)
after = gate._sha256(real)
(inbox / "prosess.html").write_text(
"<!doctype html>\n<html><head><title>Prosess 84</title></head>\n<body>\n"
"<h1>84 Konstruksjoner av betong</h1>\n"
(inbox / "kapittel.html").write_text(
"<!doctype html>\n<html><head><title>Kapittel 84</title></head>\n<body>\n"
"<h1>84 Brød og boller</h1>\n"
'<img src="graphics/stor.bmp" alt="Stor">'
'<img src="graphics/ekte.png" alt="Ekte">'
'<img src="graphics/figur.bmp" alt="Figur">\n'
@ -859,8 +762,8 @@ def test_a_document_cannot_forge_the_WHOLE_pointer_block(tmp_path: Path) -> None
# both digests are readable from a bundle this build already wrote.
inbox, before, after = _forgery_corpus(root, "<p>placeholder</p>")
shaped = {"before": before, "after": after, "after12": after[:12]}
(inbox / "prosess.html").write_text(
(inbox / "prosess.html")
(inbox / "kapittel.html").write_text(
(inbox / "kapittel.html")
.read_text(encoding="utf-8")
.replace("<p>placeholder</p>", body.format(**shaped)),
encoding="utf-8",
@ -1318,14 +1221,14 @@ def test_an_asset_with_the_right_name_and_the_wrong_bytes_is_not_carried(tmp_pat
def test_an_asset_under_a_reduced_name_still_proves_the_carry(tmp_path: Path) -> None:
"""The build lowercases and folds the source's basename and sniffs the
suffix from the bytes. Measured on R761, a judge checking the FULL name
suffix from the bytes. Measured on a real delivery, a judge checking the FULL name
called 50 of 50 carried images missing -- the judge's defect, not the
build's, so the content address is the check and the readable tail is not.
"""
corpus = _corpus(tmp_path)
source = corpus / "graphics" / "x.png"
digest = gate._sha256(source)
reduced = {f"{digest[:12]}-25-0143-tabeller-r761-r762.jpeg": digest}
reduced = {f"{digest[:12]}-25-0143-tabeller-p761-p762.jpeg": digest}
build = _build(accounting=_declared(fate="carried"), assets=reduced)
assert gate.account(_inventory(), build, corpus)[1].clean
@ -1400,8 +1303,8 @@ def test_row5_is_red_when_a_witness_is_missing() -> None:
def test_row6_without_its_source_is_red_locally_and_skipped_in_ci(tmp_path: Path) -> None:
missing = tmp_path / "absent"
local = gate.row6(missing, None, ci=False)
ci = gate.row6(missing, None, ci=True)
local = gate.row6([missing], ci=False)
ci = gate.row6([missing], ci=True)
assert (local.status, local.fails) == (gate.RED, True)
assert (ci.status, ci.fails) == (gate.SKIPPED, False)
assert "source missing" in ci.reason
@ -1416,7 +1319,7 @@ def test_a_skipped_row_never_leaves_the_verdict_unqualified() -> None:
def test_a_corpus_refused_whole_under_the_default_gate_is_red(tmp_path: Path) -> None:
"""Row 6 was GREEN with R761 100 % rejected: every element booked as a
"""Row 6 was GREEN with a real corpus 100 % rejected: every element booked as a
coded rejection satisfies u = 0 and d = 0. The build order asked for an
honest red there, so the row says this on its own."""
inventory = {
@ -1453,7 +1356,7 @@ def test_a_corpus_refused_whole_under_the_default_gate_is_red(tmp_path: Path) ->
def test_a_corpus_whose_every_document_has_no_declared_fate_says_so() -> None:
"""H6: N200 contributes one blank red. `okf build` proposes 0 plans on it
"""H6: a JSON corpus contributes one blank red. `okf build` proposes 0 plans on it
and FAILS (exit 2) before the accounting door is reached -- reproduced
2026-09-19: no accounting file is written at all -- so all 16 549 elements
land as `u` with `no declared fates` and the corpus measures none of the
@ -1479,11 +1382,19 @@ def test_a_corpus_with_one_declared_document_is_not_called_blank() -> None:
assert gate.measures_no_class([]) is None
def test_the_two_real_corpora_are_named_and_the_second_is_not_r761() -> None:
"""R761 holds 0 `fig`, 0 formulas and 0 references, so the gate's only
real corpus could not see the hole in the STS role map."""
corpora = gate.real_corpora(Path("/r761"), Path("/n200.json"))
assert [c.label.split()[0] for c in corpora] == ["R761", "N200"]
def test_a_real_corpus_is_named_by_its_path_and_read_by_its_kind(tmp_path: Path) -> None:
"""Row 6 knows no corpus by name: each `--corpus` path is one corpus,
labelled by its own file name and read as a zip, a directory or one file."""
folder = tmp_path / "samling"
folder.mkdir()
corpora = gate.real_corpora([tmp_path / "levering.zip", folder, tmp_path / "ett.json"])
assert [(c.label, c.kind) for c in corpora] == [
("levering.zip", "zip"),
("samling", "dir"),
("ett.json", "file"),
]
assert [c.available for c in corpora] == [False, True, False]
assert gate.real_corpora([]) == []
def test_a_unit_clean_in_only_one_of_the_two_builds_is_not_clean() -> None:
@ -1523,7 +1434,7 @@ def test_the_gate_exits_one_when_a_row_is_red(monkeypatch: pytest.MonkeyPatch) -
pytest.importorskip("pdfplumber")
pytest.importorskip("pypandoc")
monkeypatch.delenv("CI", raising=False)
code = gate.main(["--r761", "/no/such/corpus", "--n200", "/no/such/file.json"])
code = gate.main(["--corpus", "/no/such/corpus", "--corpus", "/no/such/file.json"])
assert code == 1
@ -1560,17 +1471,6 @@ def test_a_rejection_code_inside_an_element_fate_must_also_be_known(tmp_path: Pa
assert "because_i_said_so" in "; ".join(unit.notes)
def test_the_json_twin_is_read_with_the_json_role_map(tmp_path: Path) -> None:
"""M-2, measured on R761 2026-09-18: the XML delivery places a section's
label at `sec/label` (7 714, and 0 inside a title); the JSON delivery puts
2 760 of them inside the title. Read with the XML map, the twin loses
every one of those."""
twin = gate.FIXTURES / "witness" / "sts-label-in-title.twin.json"
assert witness.count_sts_json(twin.read_bytes()).counts["section_label"] == 1
assert witness._sts_role_xml("label", "title", "sec") is None
assert witness._sts_role_json("label", "title", "sec") == "section_label"
def test_an_approved_exception_is_read_and_says_what_it_does() -> None:
"""m-3: `APPROVED_EXCEPTIONS` was read by no row, so approving one changed
nothing and the list could have said anything."""
@ -1627,7 +1527,7 @@ def test_bad_usage_exits_two() -> None:
def real_rows() -> list[gate.Row]:
pytest.importorskip("pdfplumber")
pytest.importorskip("pypandoc")
return gate.evaluate(r761=None, n200=None, ci=True, consume=False)
return gate.evaluate(ci=True, consume=False)
def _cheating_report(inventory: dict[str, Any], mode: str) -> dict[str, Any]:
@ -1677,8 +1577,8 @@ def test_the_door_exists() -> None:
def test_the_real_gate_names_what_the_build_does_not_account_for(
real_rows: list[gate.Row],
) -> None:
"""Rows 1-5 against the real `okf build`; row 6 needs R761 and is skipped
here.
"""Rows 1-5 against the real `okf build`; row 6 needs a real corpus and is
skipped here.
Rows 2 and 3 were GREEN at `864570b` and are RED now, and that is the
hardening working rather than a regression: the witness counts thirteen
@ -1818,7 +1718,7 @@ def test_a_bundle_with_no_ledger_says_why_a_converted_image_cannot_be_proved() -
That is the honest reading, and until now it was a silent one: the images
concerned are counted as claimed-and-not-found with no statement that the
run carried no ledger to prove them by. R761 read 19 that way the day
run carried no ledger to prove them by. A real delivery read 19 that way the day
before the ledger existed. The gate itself always passes the flag, so this
is for every OTHER reader of a doorless bundle.
"""