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

@ -12,7 +12,7 @@ the requester's own measured gain actually lives.
## What was asked
`vegnormal-okf` measured four faceted arms against one corpus, 32 questions,
A downstream consumer measured four faceted arms against one corpus, 32 questions,
locked scoring, shared denominator 17. Arm F4 was arm F3 with exactly one
variable moved — the index's sort order — and scored 14/17 against F3's 12/17.
The single-variable claim was verified against the files rather than assumed:
@ -46,13 +46,13 @@ the part carrying the measured gain.
Measured, on this repo's own interpreter:
```
sorted(["N100.9", "N100.10", "N100.2", "N100.20", "N100.1", "N99.1"])
-> ['N100.1', 'N100.10', 'N100.2', 'N100.20', 'N100.9', 'N99.1']
sorted(["Q100.9", "Q100.10", "Q100.2", "Q100.20", "Q100.1", "Q99.1"])
-> ['Q100.1', 'Q100.10', 'Q100.2', 'Q100.20', 'Q100.9', 'Q99.1']
```
Two independent inversions: `N100.10` sorts before `N100.2`, and `N99.1` lands
*after* the entire `N100` family. A `sort_key: req_number` resolved through
Python's default ordering would therefore have handed `vegnormal-okf` a
Two independent inversions: `Q100.10` sorts before `Q100.2`, and `Q99.1` lands
*after* the entire `Q100` family. A `sort_key: req_number` resolved through
Python's default ordering would therefore have handed that consumer a
different wrong order, not the 100.0% ascending they measured over 24 338
pairs. Their result came from comparing numerically. The key name alone does
not carry that.

View file

@ -13,14 +13,14 @@ carries no extractable text layer, `pdfplumber` returns nothing for it, and the
file is refused with `extractor_empty_pdf`. That refusal is correct and final,
not a placeholder.
**The consequence, stated plainly.** The `vegnormal-okf` PDFs are rastered 11
**The consequence, stated plainly.** A downstream consumer's PDFs are rastered 11
of 11 (measured, `docs/2026-08-29-konverteringsarmen-arm-a.md`). They will
never enter through the inbox. Any statement of the form "the Vegnormalene
never enter through the inbox. Any statement of the form "those
PDFs in ⇒ bundle out" is not a goal this library holds, and the phrasing should
not survive in planning documents.
For the record, that premise never described anything that happened:
`vegnormal-okf` builds its bundle from seven JSON files through **Door C**, not
That consumer builds its bundle from seven JSON files through **Door C**, not
through the inbox. The inbox path has never been the route those concepts took.
A consumer with rastered source documents needs a text layer produced outside
@ -32,8 +32,8 @@ best-effort guess.
`materialize.reduce_to_id_grammar` reduces each path component to
`[a-z0-9][a-z0-9-]*`, and `segmentation.normalize_segment_path` applies the
same grammar to segment paths. A source path of `krav/N500/id-<uuid>` therefore
becomes `krav/n500/id-<uuid>`.
same grammar to segment paths. A source path of `krav/Q500/id-<uuid>` therefore
becomes `krav/q500/id-<uuid>`.
**This is the library's Phase 1 grammar, not the specification's.** OKF v0.2
§ 2 defines a concept ID as the file path minus `.md` and imposes no case rule.
@ -47,7 +47,7 @@ helper was introduced to remove.
**The consequence, stated plainly.** A source whose identities are
case-sensitive cannot be reproduced verbatim through Door B. This is measured,
not predicted: in `vegnormal-okf`'s B3 run against N500:2024, 267 of 270
not predicted: in that consumer's B3 run against one 270-concept source, 267 of 270
concept identities matched case-folded and 0 matched verbatim — the entire
difference across all 267 was case alone.
@ -56,10 +56,10 @@ which writes external concepts verbatim, or accept the folded form.
## 3. Structured sources are not segmentable as they stand
Measured by `vegnormal-okf` 2026-09-01 (B3, commit `0f8a27f`), with a
Measured by that consumer 2026-09-01 (B3, commit `0f8a27f`), with a
known-positive control that held at 270 of 270 through their domain path:
- The segment proposer returned **0 segments** for N500:2024. The raw file is
- The segment proposer returned **0 segments** for that source. The raw file is
1 633 250 characters on a single line, and the proposer's rules are
line-oriented. The same proposer returns 3 segments for a markdown document
with headings, so the zero is a measurement rather than a broken query.

View file

@ -14,12 +14,11 @@ concepts. So a picture is neither permitted nor forbidden — it is unaddressed,
and a producer that carries one is guessing about where it goes and what a
consumer may assume.
The need is not hypothetical. Measured on R761 Prosesskoden:2025, a Norwegian
road-construction process code published both as a 701-page PDF and as a
NISO-STS XML delivery: the process text is carried in full, and 12 `Tabell N-N`
The need is not hypothetical. Measured on a Norwegian reference standard published both as a 701-page
PDF and as a NISO-STS XML delivery: the process text is carried in full, and 12 `Tabell N-N`
and 9 `Figur N-N` captions stand over nothing, because the publisher ships
those tables as raster images in **both** deliveries. Process 84 says
"toleranseklasse ... er gitt i tabell 84-2" and table 84-2 is a JPEG. A bundle
those tables as raster images in **both** deliveries. Section 84 says
its classes "are given in table 84-2" and table 84-2 is a JPEG. A bundle
built from that document reads as complete and is not.
## The proposed wording
@ -86,7 +85,7 @@ The measurement that did NOT pass first time is in the report: Door C merged
the concepts and wrote no `assets/` at all, so every pointer in the imported
bundle named a missing file. Fixed here, by the content-identity rule that door
already owns. The run record is
`docs/2026-09-17-bilder-i-bundlen-trinn1.md` § 5.
the 0.10.0 entry in `CHANGELOG.md`.
## Route

View file

@ -48,7 +48,7 @@ markdown link target, or a number immediately preceded by a reference cue from
a closed set.** Everything else number-shaped in body prose is not a reference.
The rule is positive rather than a blacklist, and that is forced by the data,
not chosen for taste: corpus A proves `V221` and `V240` are genuine document
not chosen for taste: corpus A proves `W221` and `W240` are genuine document
numbers, corpus B proves `V0.3.0` is a software version, and the two are the
same token shape. Nothing structural separates them. Only the words in front of
them do, so only a positive cue can be defended.
@ -98,12 +98,12 @@ reader, a false one is not.
Two recall losses inside the corpora measured, both accepted: a reference whose
cue is a preposition (`i henhold til …` where the noun is absent) and one
written as a prefixed compound (`N-V240`) are dropped.
written as a prefixed compound (`Q-W240`) are dropped.
## What was deliberately not done
**The prefix-resolution rule (`N130` in a body not finding a document numbered
`N130.2`) stays open.** The order allowed it in the same pass only if the data
**The prefix-resolution rule (`Q130` in a body not finding a document numbered
`Q130.2`) stays open.** The order allowed it in the same pass only if the data
bore it. It does not: of 2 589 documents across both corpora, 2 562 carry a
number and **0** of those numbers are dotted, so a unique-prefix match has
nothing here to be defended against. Closing two things badly is worse than

View file

@ -63,7 +63,7 @@ nothing derived can still use everything declared.
| | same number + ordered `version` | **derived** |
| `version`, `status`, `date` | producer only | declared |
The number grammar is alpha-prefixed (`N500`, `V720`, `R610.4`) or dotted
The number grammar is alpha-prefixed (`Q500`, `W720`, `P610.4`) or dotted
numeric (`4.2.1`). A **bare integer is deliberately not a number**: admitting
`12-things.md` or `2026-notes.md` would stamp a document number on most of a
second brain that never had one.
@ -239,8 +239,8 @@ change — rather than as "not possible".
and supersession.
2. **A reference to a parent-level number does not resolve to a child.** In the
synthetic corpus, `N130` in the body of one document does not resolve to a
document numbered `N130.2`; it is reported unresolved (`N130?`). This is
synthetic corpus, `Q130` in the body of one document does not resolve to a
document numbered `Q130.2`; it is reported unresolved (`Q130?`). This is
honest but incomplete — the fix is a prefix-resolution rule
(`subject` resolves to the unique document whose number starts with it), and
it was left out because "unique" has to be defined against a real corpus
@ -270,7 +270,7 @@ change — rather than as "not possible".
# Door C: the index carries the sender's facts, and only theirs
Ordered by vegnormal-okf (`20260826T224500Z-873805419`) on a measurement, not a
Ordered by a downstream consumer (`20260826T224500Z-873805419`) on a measurement, not a
preference. A pre-registered reasoning bake-off on 2026-08-27 ran the same
bundle through two index shapes:
@ -320,7 +320,7 @@ reads a value only to check the policy can render it.
Operator directive 2026-08-27, verbatim: *"alt vi lager rundt OKF publiseres på
Forgejo og skal virke for alt av innhold"* — so a design that only works for
numbered norms is wrong even when it scores well on N100/N200/N500. Nothing in
numbered norms is wrong even when it scores well on three numbered reference bundles. Nothing in
`_project_facets` can key off a numbering scheme, and the suite says so with an
unnumbered Norwegian meeting note (`status` + `date`, no number, no parent, no
version) and with a document declaring none of the policy's keys, which renders
@ -414,14 +414,14 @@ own `import_prefix` now states it directly, and M10 is killed.
1. **`title` is not in `STRUCTURED_V1`'s facet set.** Widening it would move
Door B's index bytes, so it stays a caller's key to name. For a sender whose
concept paths are UUIDs this is the difference between a title in the index
and none — flagged to vegnormal-okf rather than decided here.
and none — flagged to that consumer rather than decided here.
2. **Door C's index is an append plus a per-target refresh, not a projection of
the whole bundle** (contrast D-S4). A concept removed from the bundle by hand
leaves its entry behind. Reprojection would mean enumerating the directory,
which no profile has asked this door to do.
3. **Parent-prefix resolution is still deferred**, unchanged and for the
unchanged reason: "unique prefix match" must be defined against a real
corpus. `vegnormal-okf` has that corpus; the count is theirs to take.
corpus. The consumer has that corpus; the count is theirs to take.
4. **The before/after number is not ours to produce.** Whether the faceted index
is worth its cost on a real corpus is answered by re-running the same arm
with the same scoring, on their data.