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:
parent
1e7345a401
commit
9d1f4b14ed
174 changed files with 1889 additions and 6512 deletions
|
|
@ -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.
|
||||
"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue