docs: measurement prose names no document by its numbers — green

Section totals, a page count and build sizes identified one reference
document and one generated HTML corpus to anyone who knows them. They are
written as N, M, an offset of N or a share in docstrings, comments, help
text, reports, README, CLAUDE.md and the changelog; no measured relation
changes. Sums that let N be worked back are written in words. The
`okf quality --fasit` bar is 999/1000 (99.9 %), just under the known-good
arm's measured share, and every build measured keeps its verdict.

Red with the local list widened to those numbers: 52 tracked files. Green: 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-23 14:51:17 +02:00
commit 81d7194e20
33 changed files with 412 additions and 398 deletions

View file

@ -5,10 +5,10 @@ neither a function of corpus size. Both are closed; neither is closed with a
ceiling.
- **A.** `.html` was in `_CORE_EXTRACTORS` and no HTML document could ever be
segmented: **828 of 828** real sections gave 0 plans and exit 2.
segmented: **M of M** real sections (M files in one corpus) gave 0 plans and exit 2.
- **B.** One source document took **8 of 8** delivered places on a question and
**7 of 8** on the known-positive, on a 3206-concept bundle where it is
**0.87 %** of the concepts.
**7 of 8** on the known-positive, on a bundle of a published handbook (a few
thousand concepts) where it is **0.87 %** of the concepts.
Both were reproduced here before either was touched, and three of the numbers
the order carried are corrected below.
@ -19,11 +19,11 @@ the order carried are corrected below.
| claim | source | reproduced here |
|---|---|---|
| HTML: 0 plans, N with no boundary, exit 2 | SK1, frozen `6ff18fd` | **yes**83 of 83 at the 10 % point, exit 2 |
| markdown control: 83 plans, 343 concepts | SK1 | **yes**, exactly |
| HTML: 0 plans, N with no boundary, exit 2 | SK1, frozen `6ff18fd` | **yes**every file at the 10 % point, exit 2 |
| markdown control at the 10 % point: one plan per file, same concept count | SK1 | **yes**, exactly |
| smoke folder 52 md / 26 concepts, 0 of 5 rejected | order | **yes** |
| suite 1535, ruff + format + mypy clean | order | **yes** — 1535 passed, ruff 0.16.6 clean, `ruff format --check` 114 files, mypy 21 files |
| corpus counts 83 / 414 / 828 / 145 / 110 | order | **yes**, `find -type f` and suffix census |
| corpus counts at a tenth / half / all M / 145 / 110 | order | **yes**, `find -type f` and suffix census |
| dominant document takes 23 of 42 places | SK1 | **close, not equal** — 22 of 43 at `k = 50`; see below |
| known-positive at rank 12 | SK1 | **no** — rank 8 at `k = 50` here |
| the document takes 8 of 8 places on the known-positive at `k = 8` | order | **no****7 of 8** here |
@ -93,15 +93,15 @@ own: a boundary needs a line that MATCHES a grammar.
| point | files | before | after | markdown path |
|---|---|---|---|---|
| 10 % | 83 `.html` | 0 plans, 83 of 83 no boundary, exit 2 | **83 plans, exit 0, 343 concepts** | 83 plans, **343** concepts |
| 50 % | 414 `.html` | 0 plans, exit 2 | **414 plans, exit 0, 1651 concepts** | **1651** |
| 100 % | 828 `.html` | 0 plans, exit 2 | **828 plans, exit 0, 3206 concepts / 6015 md** | **3206** / 6015 |
| 10 % | a tenth of M `.html` | 0 plans, every file no boundary, exit 2 | **one plan per file, exit 0**, concept count = markdown path | one plan per file, same count |
| 50 % | half of M `.html` | 0 plans, exit 2 | **one plan per file, exit 0**, concept count = markdown path | same count |
| 100 % | M `.html` | 0 plans, exit 2 | **M plans, exit 0**, concepts and md files = markdown path | same counts |
| coarser cut | 145 `.html` | 145 of 145 no boundary, exit 2 | **145 plans, exit 0, 953 concepts** | not built |
The acceptance bar was ±2 % of the markdown path. The deviation is **0.0 %** at
all three points — the HTML path and the markdown path produce the **same
concept count**, not a similar one. `merged + coded rejections = 828; N = 828`
closes at every point, 0 unreadable, 0 coded rejections.
concept count**, not a similar one. `merged + coded rejections = N`, with N the
file count, closes at every point, 0 unreadable, 0 coded rejections.
### Text preservation as an exact invariant
@ -111,11 +111,11 @@ what the old extractor returned for the same bytes.
| measure | value |
|---|---|
| files exact | **828 of 828** |
| files exact | **M of M** |
| characters, old vs new (markers stripped) | 1 634 454 vs 1 634 454 — ratio **1.000000** |
| SK1's own bar | ≥ 99.8 % |
| ATX markers added | 7 600 over 828 files |
| lines produced | **31 141**, where the old extractor produced 828 — one per file |
| ATX markers added | 7 600 over M files |
| lines produced | **31 141**, where the old extractor produced M — one per file |
The invariant runs **in the suite over the whole corpus**, not over one
document, behind `OKF_HTML_CORPUS`: the path names a consumer's own export and
@ -158,7 +158,7 @@ source file whose extraction is three lines.
`_EVIDENCE` had five rows, all converter formats, and no `.html` row even though
`.html` is core-supported. It now has one, at **`measured`**, chosen against the
class definitions rather than assumed: `measured` means "someone wrote the
document for their own purposes and we counted what we got", and the 828 files
document for their own purposes and we counted what we got", and the M files
are a consumer's own export of a real published handbook, produced for their
ingestion and not to exercise this row, with a fasit written before any lookup.
What the class does **not** claim travels with it in the source: one product,
@ -184,7 +184,7 @@ green is not. These are the first kind and the third and fourth are neither.
### The finding, re-measured here
On the 3206-concept bundle, at the default `k = 8`:
On the handbook bundle (a few thousand concepts), at the default `k = 8`:
| question | fasit delivered at | dominant document's share of the 8 places |
|---|---|---|
@ -198,8 +198,8 @@ On the 3206-concept bundle, at the default `k = 8`:
| known-negative | — (correctly) | 3 of 8 |
hit@8 over S1S6: **2 of 6**. The document is the code's own process overview:
28 of 3206 concepts (**0.87 %**) and 8.0 % of the source characters. SK1 measured
the same 8-of-8 share at **343**, **1651** and **3206** concepts, so this is the
**0.87 %** of its concepts and 8.0 % of the source characters. SK1 measured
the same 8-of-8 share at a tenth, at half and at the whole corpus, so this is the
corpus's **composition** — it holds its own table of contents — and not its size.
A split would move the defect, not remove it; every corpus with a contents list,
a project overview or a summary document has the same property.
@ -291,12 +291,12 @@ that disagreement for two rounds, and the reading side had no equivalent guard.
## 3. Honesty limits
- **The HTML corpus is machine-generated from ONE source by another session.**
It is not 828 documents anyone wrote: the cut follows the source's own `<sec>`
It is not M documents anyone wrote: the cut follows the source's own `<sec>`
nesting, each file's `<h1>` is a generator's, and a different cut would give
different numbers. The **text** is a real published handbook's; the
**documents** are a rendering of it.
- **One product, one format, one publisher is not generality.** `okf build` over
828 markdown files says nothing about 828 PDFs: SK1 measured 0.0076 s per
M markdown files says nothing about M PDFs: SK1 measured 0.0076 s per
document against K2's 19.2, which is an **extraction-cost** difference and not
a scaling one.
- **SK1's hit@k rests on 6 questions, one rater, one fasit**, written by the
@ -329,12 +329,12 @@ that disagreement for two rounds, and the reading side had no equivalent guard.
| | before | after |
|---|---|---|
| documents with a segmentation plan | **0 of 828** | **828 of 828** |
| documents with a segmentation plan | **0 of M** | **M of M** |
| exit code | 2 (`FAILED — no segmentation plans`) | **0** |
| concepts | 0 | **3206** the markdown path's count exactly |
| md files | 0 | **6015** |
| conservation identity | never reached | `merged + coded rejections = 828; N = 828` |
| text preserved (exact, non-whitespace) | n/a | **828 of 828**, ratio **1.000000** |
| concepts | 0 | the markdown path's count exactly |
| md files | 0 | the markdown path's count exactly |
| conservation identity | never reached | `merged + coded rejections = N`, N = M |
| text preserved (exact, non-whitespace) | n/a | **M of M**, ratio **1.000000** |
| coarser 145-document cut | 0 of 145 | **145 of 145** |
| evidence class | absent from `_EVIDENCE` | **`measured`**, with its limit stated |

View file

@ -23,7 +23,7 @@ cases the number below is the one measured here.
| worktree | clean | clean, `uv.lock` untouched | `git status --short` |
| suite at HEAD | 1575 passed, 1 skipped | **1575 passed, 1 skipped**, 95.84 s | `pytest -q` |
| collected at HEAD | 1576 | **1576** | `pytest --collect-only -q` |
| reference standard build | rc 0, 23.2 s, 2 737 dirs, 2 761 concepts | rc 0, **25 s**, 2 737 dirs, **2 761** concepts | `okf build inn-xml …` |
| reference standard build (N = its declared titled sections) | rc 0, 23.2 s, N 24 dirs, N concepts | rc 0, **25 s**, N 24 dirs, **N** concepts | `okf build inn-xml …` |
| reference standard at default `k` | 3/6 · 5/6 · 5/6, KP 1 | **identical**, and S1 rank 4 · S5 not delivered · S6 rank 3 | the consumer's hit@k script |
| reference standard at `--k 50` | 3/6 · 5/6 · 6/6, KP 1 | **identical** | the same script, `--k 50` |
@ -53,7 +53,7 @@ The third is the document prior.
**The third signal carries no information on this bundle, and that is measured
rather than inferred.** `document_score` is keyed on
`concept_id.split("/", 1)[0]`, which is the source document; the reference standard is one XML
file, so the prior takes **one distinct value over all 2 761 concepts**. With
file, so the prior takes **one distinct value over all N concepts**. With
`tie_shared_rank` on, every concept receives the same constant from it. The
order's hypothesis is CONFIRMED. Its evidence — that `--no-tie-shared-rank`
drops hit@1 to 0/6 — is confirmed too: a signal that separates nothing still
@ -87,7 +87,7 @@ nouns and *Y* a compound.
| 1 | a large neighbouring section | 1 | **5** | **interrogative**, requirements, **verb**, *X*, *Y* | 19 658 |
| 2 | a short section titled *X* plus a qualifier | **2** | 3 | requirements, *X*, *Y* | 442 |
| … | | | | | |
| **12** | **the gold, titled *X* alone** | 1 | 3 | requirements, *X*, *Y* | 2 759 |
| **12** | **the gold, titled *X* alone** | 1 | 3 | requirements, *X*, *Y* | ~2 800 |
Two mechanisms, not one. The rank-1 concept wins on the interrogative and the
verb, both grammatical; its body is 7x the gold's, and a longer body can only
@ -165,7 +165,7 @@ answers it.
RRF consumes RANKS ALONE, so one signal contributes at most `1/(RRF_K + 1)`.
With shared ranks, a signal whose positive group has `m` members gives the group
position 1 and everyone else position `m + 1` — so its whole separating power is
`1/61 - 1/(61 + m)`. **A rule that fires on ONE concept of 2 761 is worth
`1/61 - 1/(61 + m)`. **A rule that fires on ONE concept of N is worth
0.00026**, while the body-signal gap it must close on S1 is
`1/61 - 1/74 = 0.0029`, an order of magnitude larger. **A precise rule is worth
LEAST under this fusion, exactly backwards from what precision is for.**
@ -212,7 +212,7 @@ cells on three other bundles (§ 5). It never fires on the known-negative.
## 5 The row, and every other row measured
**The reference standard, delivered ranks from `okf consume`, 2 761 concepts, one source document:**
**The reference standard, delivered ranks from `okf consume`, N concepts, one source document:**
| | before, default `k` | after, default `k` | before, `--k 50` | after, `--k 50` |
|---|---|---|---|---|
@ -238,7 +238,7 @@ own cause.
| corpus-a | 5 questions, 446 concepts | — | payloads **byte-identical**, rule fires 0 of 5 |
| corpus-b | 5 questions, 1 133 concepts | — | payloads **byte-identical**, rule fires 0 of 5 |
| corpus-c | 5 questions, 270 concepts | — | payloads **byte-identical**, rule fires 0 of 5 |
| 828-file HTML corpus | 828 documents | 828 plans, 0 unreadable, 6 015 md | **828 plans, 0 unreadable, 6 015 md**; `diff -rq` empty apart from `log.md`'s converter PATH (same pandoc 3.9) |
| HTML corpus | M documents | M plans, 0 unreadable | **M plans, 0 unreadable, same md count**; `diff -rq` empty apart from `log.md`'s converter PATH (same pandoc 3.9) |
| `~/okf-test/dokumenter` | 5 documents | 26 concepts / 52 md | **26 / 52**, `diff -r` **EMPTY** |
| `okf project` vs `okf build` | same folder | byte-equal | **byte-equal**, `diff -r` empty |
| suite | — | 1575 passed, 1 skipped | **1582 passed, 1 skipped**, 1583 collected |

View file

@ -14,8 +14,8 @@ changed, no extractor or arm was added, no flag default moved, `uv.lock` and the
The functionality the tag carries was measured in the two rounds before it: the
`.xml` core file type read as NISO-STS and the PDF arm's collision counter
(round 13), and the declared-structure route taking `.xml` from 15 of 2 761 to
**2 761 of 2 761** boundaries and from 23 to **2 761** concepts at the shipped
(round 13), and the declared-structure route taking `.xml` from 15 of N (the reference
standard's declared titled sections) to **N of N** boundaries and from 23 to **N** concepts at the shipped
defaults, hit@1/8/50 from 0/6 · 0/6 · 0/6 to **3/6 · 5/6 · 6/6** (round 14).
Nothing in this round re-measures any of that.
@ -117,7 +117,7 @@ Three rows, each with its denominator, all run from the installed binary:
| row | measured | expected |
|---|---|---|
| `okf project` on the five-document folder | 5 documents read, **26 concepts**, **52 md files**, **0 of 5** left out | 26 / 52 / 0 of 5 |
| `okf build` on the 828-file HTML corpus | `proposed 828 plan(s); 0 document(s) with no boundary; 0 unreadable`; 6 015 md | 828 of 828 |
| `okf build` on the HTML corpus (M files) | `proposed M plan(s); 0 document(s) with no boundary; 0 unreadable`; md count as expected | M of M |
| `okf skill` + `okf consume` + `okf check` | `conformant: 15 rules over 8 excerpts and 18 withheld entries, 0 findings` | 15 rules, 0 findings |
The HTML build was run twice into separate scratch directories and the two are
@ -141,7 +141,7 @@ restore it.
exercised at all, because the tag is unpushed.
- **The `okf project` figure of 26 is ONE corpus of 5 documents.** It is a
regression check against a known number, not a property of the tool.
- **828 of 828 is ONE publisher's HTML from ONE product.** The generality of the
- **M of M is ONE publisher's HTML from ONE product.** The generality of the
`.html` row rests on that corpus and no other.
- **The grep criterion was changed deliberately** (D1, D2). The published
acceptance in the commissioning STATE is not the one that ran, and the reason
@ -150,7 +150,7 @@ restore it.
- **"The tag installs" is not "the tag is right."** No functionality is verified
in this round beyond what rounds 13 and 14 already measured. The three rows
above are smoke measurements chosen because their expected values were known;
they do not re-derive 2 761 of 2 761, and no `.xml` document was in any of
they do not re-derive N of N, and no `.xml` document was in any of
them.
- **The CHANGELOG carries no compare link for `[0.8.0]`**, and none was added.
This file has published none since `[0.6.0]`; inventing one would be a claim

View file

@ -97,8 +97,8 @@ cd <export> && uv sync --frozen --extra extract
| version from the tag's bytes | **0.8.1** (`importlib.metadata`) |
| suite in the export | 1573 passed, 1 failed, 9 skipped -- the export artefact |
| `okf project` on the five-document folder | **26 concepts of 5 documents, 52 md, 0 of 5 rejected** |
| HTML corpus | **proposed 828 plan(s); 0 with no boundary; 0 unreadable**, 828 of 828, 6 015 md |
| reference standard, XML | **2 761 concepts** of 2 761 declared sections at shipped defaults, no arm flag; 109 of 110 files are images and are coded `extractor_unknown` |
| HTML corpus (M files) | **proposed M plan(s); 0 with no boundary; 0 unreadable**, M of M |
| reference standard, XML | **N concepts** of its N declared titled sections at shipped defaults, no arm flag; 109 of 110 files are images and are coded `extractor_unknown` |
| `okf check --skill ... --payload ...` | **conformant, 15 rules, 0 findings** |
Bundles were built to scratch. `~/okf-test/dokumenter` and
@ -110,7 +110,7 @@ a downstream consumer repository were read and never written.
Mac with the interpreter `uv` resolved here. It is not a portability claim.
- **`okf project`'s 26 is ONE corpus of five documents.** It is the same five
every previous round used, so it is a regression control, not a sample.
- **828 of 828 and 2 761 of 2 761 are ONE publisher's files from ONE product.**
- **M of M and N of N are ONE publisher's files from ONE product.**
Both numbers say the reader did not lose anything against its own denominator;
neither says anything about a document nobody in that pipeline wrote.
- **The grep criterion is deliberately NOT "0 hits on `v0.8.0`".** Two bare

View file

@ -1,6 +1,7 @@
# K3 round 17 — a covered title stops beneath a title that answers more of the question
Round 16's `--title-covered` took the 2 761-concept bundle of one standard from
Round 16's `--title-covered` took the one-document bundle of one standard (a few
thousand concepts) from
hit@1 **3 of 6** to **6 of 6**. Re-measured on another bundle, it failed a
constructed known-negative: a short, generic one-token title that a long
question happens to contain took rank 1 from the section the question names.
@ -30,7 +31,7 @@ number below is the one measured here.
| known-negative | before rank 1 (rival rank 2), after rank 2, ranks 3-8 identical | **identical**, row for row | `okf consume`, with and without `--no-title-covered` |
| question tokens | 9, `er` and `og` dropped | **9**, the same nine | `normalise` |
| `title_covered_hits` | exactly one concept, one title token | **one**, one token | — |
| reference standard build | ~23 s, 2 737 dirs, 2 761 concepts | **16 s**, 2 737, 2 761 | `okf build` |
| reference standard build (N = its declared titled sections) | ~23 s, N 24 dirs, N concepts | **16 s**, N 24, N | `okf build` |
| reference standard before / after | 3/6·5/6·5/6 → 6/6·6/6·6/6 at default `k`; 3/6·5/6·6/6 → 6/6·6/6·6/6 at `--k 50` | **identical**, KP rank 1 at both | the consumer's hit@k script |
| reference standard group sizes | 1, 1, 1, 1, 9, 2, 0, 0 | **identical** | § 1 |
@ -224,7 +225,7 @@ no new parameter, no new constant:
## 4 The row, and every other row measured
**The standard (a 701-page reference standard, XML, 2 761 concepts, one source document), delivered
**The standard (a long reference standard, XML, N concepts, one source document), delivered
ranks from `okf consume` through the consumer's hit@k script, run from the `e15de71` export:**
| | off (pre-0.8.1) | round 16 (0.8.1) | round 17 |
@ -256,7 +257,7 @@ ranks from `okf consume` through the consumer's hit@k script, run from the `e15d
| corpus-a | 5 questions (round 16's), 446 concepts | **identical 5 of 5**; group 0 on 5 |
| corpus-b | 5 questions, 1 133 concepts | **identical 5 of 5**; group 0 on 5 |
| corpus-c | 5 questions, 270 concepts | **identical 5 of 5**; group 0 on 5 |
| 828-file HTML corpus | 828 documents | **828 plans, 0 of 828 rejected, 6 015 md**; `diff -r` **empty**, old and new source on one interpreter |
| HTML corpus | M documents | **M plans, 0 of M rejected, md count unchanged**; `diff -r` **empty**, old and new source on one interpreter |
| `~/okf-test/dokumenter` | 5 documents | **26 concepts / 52 md**; `diff -r` **empty**, old and new source on one interpreter (built in two venvs, one line differs: `log.md`'s converter path) |
| `okf project` vs `okf build` | same folder, `--id d` | **byte-equal**, `diff -r` empty, 52 md |
| suite, working tree | — | 1593 passed, 1 skipped → **1600 passed, 1 skipped** (7 new) |

View file

@ -79,8 +79,8 @@ cd <export> && uv sync --frozen --extra extract
| `len(contract_check.RULES)` | **16**, rule 4 `rule_bundle_identity` |
| `okf check` on `skills/okf-consume/` and its payload | **conformant: 16 rules over 3 excerpts and 0 withheld entries, 0 findings**, rc 0 |
| `okf project` on the five-document folder | **26 concepts of 5 documents, 52 md, 0 of 5 left out** |
| HTML corpus | **proposed 828 plan(s); 0 document(s) with no boundary; 0 unreadable**, 3 206 concepts / 6 015 md |
| reference standard, XML | **2 761 concepts** at shipped defaults, no arm flag; the folder holds 1 file at top level and 110 in total, and 109 are images under `graphics/` (43 png, 41 jpg, 25 bmp), coded unreadable |
| HTML corpus (M files) | **proposed M plan(s); 0 document(s) with no boundary; 0 unreadable**, concept and md counts unchanged |
| reference standard, XML | **N concepts** (N = its declared titled sections) at shipped defaults, no arm flag; the folder holds 1 file at top level and 110 in total, and 109 are images under `graphics/` (43 png, 41 jpg, 25 bmp), coded unreadable |
| hit@1 / 8 / 50 on the reference standard's bundle | **6/6 / 6/6 / 6/6**, N = 6 scored of 8 asked, known-positive at rank 1 |
| round 17's known-negative on the five-document bundle | the section the question names at **rank 1** (6 793 B), the neighbour at rank 2 (10 590 B); both concepts exist and both are delivered of 8 |
@ -95,7 +95,7 @@ known export artefact as the one failure.
which does not exist until the push.
- **`okf project`'s 26 is ONE corpus of five documents** -- the same five every
previous round used, so it is a regression control, not a sample.
- **828 of 828 and 2 761 of 2 761 are ONE publisher's files from ONE product.**
- **M of M and N of N are ONE publisher's files from ONE product.**
They say the reader lost nothing against its own denominator; they say
nothing about documents nobody in that pipeline wrote.
- **hit@1 is 6 scored questions on ONE bundle, against a fasit someone else

View file

@ -104,12 +104,12 @@ cd <export> && uv sync --frozen --extra extract
| `len(contract_check.RULES)` | **16** |
| `okf check` on `skills/okf-consume/` and its payload | **conformant: 16 rules over 3 excerpts and 0 withheld entries, 0 findings**, rc 0 |
| `okf project` on the five-document folder | **26 concepts of 5 documents, 52 md, 0 of 5 left out** |
| HTML corpus | **proposed 828 plan(s); 0 document(s) with no boundary; 0 unreadable**, 3 206 concepts / 6 015 md |
| reference standard, XML | **2 761 concepts** at shipped defaults, no arm flag; the folder holds 1 file at top level and 110 in total, 109 of them images under `graphics/` (43 png, 41 jpg, 25 bmp), coded unreadable |
| HTML corpus (M files) | **proposed M plan(s); 0 document(s) with no boundary; 0 unreadable**, concept and md counts unchanged |
| reference standard, XML | **N concepts** (N = its declared titled sections) at shipped defaults, no arm flag; the folder holds 1 file at top level and 110 in total, 109 of them images under `graphics/` (43 png, 41 jpg, 25 bmp), coded unreadable |
| reference standard's document directory (new this round) | **its declared `<doc-number>`**, not the delivery file's UUID stem; denominator 1 document |
| hit@1 / 8 / 50 on the reference standard's bundle | **6/6 / 6/6 / 6/6** at the default `k` and at `k` = 50, N = 6 scored of 8 asked |
| the known-positive (new this round) | **rank 1 at the default `k` and rank 1 at `k` = 50**, denominator 1 |
| `--shell-parent` (new this round) | **675 of 710** heading-only concepts carry `parent:`; **0 of 2 761** without the flag, and 0 non-heading-only concepts with it; `--shell-parent` and `--no-shell-parent` both in `okf build --help` |
| `--shell-parent` (new this round) | **675 of 710** heading-only concepts carry `parent:`; **0 of N** without the flag, and 0 non-heading-only concepts with it; `--shell-parent` and `--no-shell-parent` both in `okf build --help` |
| round 17's known-negative on the five-document bundle | the section the question names at **rank 1** (6 793 B), the neighbour at rank 2 (10 590 B); both concepts exist and both are delivered of 8 |
"Heading-only" was counted as a concept whose body, after the frontmatter, is
@ -130,7 +130,7 @@ The suite was not run inside the export this round.
which does not exist until the push.
- **`okf project`'s 26 is ONE corpus of five documents** -- the same five every
previous round used, so it is a regression control, not a sample.
- **828 of 828 and 2 761 of 2 761 are ONE publisher's files from ONE product.**
- **M of M and N of N are ONE publisher's files from ONE product.**
They say the reader lost nothing against its own denominator; they say
nothing about documents nobody in that pipeline wrote.
- **The directory name is ONE publisher's `<doc-number>` on ONE document.** It

View file

@ -134,8 +134,8 @@ cd <export> && uv sync --frozen --extra extract
| `len(contract_check.RULES)` | **17**, the same as the working tree |
| `okf check` on `skills/okf-consume/` and its payload | **conformant: 17 rules over 3 excerpts and 0 withheld entries, 0 findings**, rc 0 |
| `okf project` on the five-document folder | **26 concepts of 5 documents, 52 md, 0 of 5 left out**; the folder holds 5 files before and after |
| HTML corpus | **proposed 828 plan(s); 0 document(s) with no boundary; 0 unreadable**, 6 015 md |
| reference standard, XML | **2 761 concepts** at shipped defaults, no arm flag; the folder holds 1 file at top level and 110 in total, 109 coded `extractor_unknown` |
| HTML corpus (M files) | **proposed M plan(s); 0 document(s) with no boundary; 0 unreadable**, md count unchanged |
| reference standard, XML | **N concepts** (N = its declared titled sections) at shipped defaults, no arm flag; the folder holds 1 file at top level and 110 in total, 109 coded `extractor_unknown` |
| reference standard's document directory | **its declared `<doc-number>`**; denominator 1 document |
| hit@1 / 8 / 50 on the reference standard's bundle | **6/6 / 6/6 / 6/6** at the default `k` and at `k` = 50, N = 6 scored of 8 asked |
| the known-positive | **rank 1 at the default `k` and rank 1 at `k` = 50**, denominator 1 |
@ -149,7 +149,7 @@ the working tree before the bump differ from the tag's build in exactly one
line, `log.md`'s converter path (a different venv); every concept file is
byte-identical. The pin's one earlier claim without a source -- 26 of 28 --
reproduces on this bundle. The three other corpus figures that came with it
(2 761 of 2 763, 3 206 of 3 208, 412 of 455) were **not** measured here.
(N of N + 2, all but 2 of the HTML bundle's concepts, 412 of 455) were **not** measured here.
The hit@k instrument lives in the consumer's repository and was only read. It
writes its payloads to fixed paths another session also writes, so it ran as a
@ -164,7 +164,7 @@ scratch copy that differs from the original in exactly those two output paths
which does not exist until the push.
- **`okf project`'s 26 is ONE corpus of five documents** -- the same five every
previous round used, so it is a regression control, not a sample.
- **828 of 828 and 2 761 of 2 761 are ONE publisher's files from ONE product.**
- **M of M and N of N are ONE publisher's files from ONE product.**
- **hit@1 and the known-positive rank are 6 scored questions and ONE
known-positive on ONE bundle, against a fasit someone else set.** They are
re-measured from the tag's bytes, which is all they add.

View file

@ -65,9 +65,9 @@ three of the five premises moved.
|---|---|---|
| 13 extensions in the registry | `_CORE_EXTRACTORS` 7 + `_OPTIONAL_EXTRACTORS` 6 = **13** | holds |
| four evidence corpora under a downstream consumer repository's finished-build directory | present: `corpus-a`, `corpus-b`, `corpus-c`, `std-consumer` — plus `std-xml-generic` and `std-xml-v080`, two more builds of the reference standard the order does not name | **holds, and is incomplete** |
| "the first corpus shall PASS on the measured types" | `grep -rl '^source_file:' corpus-a --include='*.md' | wc -l`**0 of 446 concepts**. Same for `corpus-b` (0 of 1 133), `corpus-c` (0 of 270) and `std-consumer` (0 of 2 756) | **cannot hold**: three of the four name no file type at all, so no type of theirs can be PASS or FAIL |
| "41,6 % grenser" | not in this repository when the order arrived — measured on the base commit, `git grep -c "41[.,]6" d310dbb -- docs src`**0 files**. It is the consumer's (one of its own reports), 1 148 of 2 761 declared STS sections, against the XML arm's 2 759 of 2 761 | **holds, and the owner is another repo** |
| "0 av 6 hit@k" on the HTML arm | two published numbers for the same 828-document bundle: **0 of 6** (the consumer, their six fasit questions, 10.09) and **2 of 6 → 4 of 6 with `--source-quota 2`** (this repo, `docs/2026-09-10-k3-runde11-html-og-dominans.md:200,217`, questions S1S6) | **holds for one instrument, not for the other** |
| "the first corpus shall PASS on the measured types" | `grep -rl '^source_file:' corpus-a --include='*.md' | wc -l`**0 of 446 concepts**. Same for `corpus-b` (0 of 1 133), `corpus-c` (0 of 270) and `std-consumer` (0 of all its concepts) | **cannot hold**: three of the four name no file type at all, so no type of theirs can be PASS or FAIL |
| "41,6 % grenser" | not in this repository when the order arrived — measured on the base commit, `git grep -c "41[.,]6" d310dbb -- docs src`**0 files**. It is the consumer's (one of its own reports), 1 148 of the reference standard's N declared STS sections, against the XML arm's N 2 of N | **holds, and the owner is another repo** |
| "0 av 6 hit@k" on the HTML arm | two published numbers for the same HTML-arm bundle: **0 of 6** (the consumer, their six fasit questions, 10.09) and **2 of 6 → 4 of 6 with `--source-quota 2`** (this repo, `docs/2026-09-10-k3-runde11-html-og-dominans.md:200,217`, questions S1S6) | **holds for one instrument, not for the other** |
The last row is the reason the gate reports a denominator on every line: two
true numbers about one bundle, four apart, because the question set differs.
@ -76,19 +76,21 @@ true numbers about one bundle, four apart, because the question set differs.
Command: `okf quality <bundle>` (part 2 of this order), cross-checked against
`grep -rh '^source_file:' <bundle> --include='*.md' | sed 's/.*\.//' | sort |
uniq -c`. The index walk and the directory listing agree on 453, 2 761, 3 206
and 446 concepts — four bundles, both methods.
uniq -c`. The index walk and the directory listing agree on the concept count
of the K2 reference, the XML arm, the HTML arm and `corpus-a` — four bundles,
both methods. Below, N is the reference standard's number of declared titled
sections and M the HTML arm's number of documents.
| bundle | producer | concepts | file types present |
|---|---|---|---|
| `K2-bundle-default-20260912` | Door B (pinned reference) | 453 | `.pdf` 32 docs, `.docx` 5, `.xlsx` 2 |
| `std-xml-generic` | Door B | 2 761 | `.xml` 1 doc |
| `std-xml-generic` | Door B | N | `.xml` 1 doc |
| `std-pdf-default` | Door B | 2 182 | `.pdf` 1 doc |
| `html-arm-828` | Door B | 3 206 | `.md` 828 docs |
| `html-arm` | Door B | a few thousand | `.md` M docs |
| `corpus-a` | the consumer's own pipeline | 446 | none — no `source_file` |
| `corpus-b` | the consumer's own pipeline | 1 133 | none |
| `corpus-c` | the consumer's own pipeline | 270 | none |
| `std-consumer` | the consumer's own pipeline | 2 756 | none |
| `std-consumer` | the consumer's own pipeline | N 5 | none |
| `b3` | — | — | no `index.md`; the gate refuses to walk a directory (SS 9.2) and exits 2 |
**The four corpora the order names as the evidence base carry no file type.**
@ -111,26 +113,26 @@ limit is "a `1/1` is not a rate" (`docs/2026-09-08-k3-runde2-per-filtype.md`).
| file type | metric | threshold | N | source |
|---|---|---|---|---|
| every type | `empty_concepts` | **0** | 8 602 concepts over 4 Door B bundles, all 0 | definitional: `corpus.CorpusReport` calls a zero-character merge degenerate |
| every type | `empty_concepts` | **0** | every concept of 4 Door B bundles, all 0 | definitional: `corpus.CorpusReport` calls a zero-character merge degenerate |
| `.pdf` | `structure_null_share` | **8/32** | 32 documents | `K2-bundle-default-20260912` |
| `.docx` | `structure_null_share` | **2/5** | 5 documents | `K2-bundle-default-20260912` |
| `.xlsx` | — | **UNMEASURED** | 2 documents | below the floor of 5 |
| `.xml` | — | **UNMEASURED** | 1 document | below the floor of 5 |
| `.md` | — | **UNMEASURED** | 828 documents, `structure_null_share` 362/828 measured | the corpus is one generator's cut of one product, and it is the bundle under suspicion; a bar read off it would pass it by construction |
| `.html` | — | **UNMEASURED** | 0 bundles in this repository | the 828-document HTML measurement was made outside it |
| `.md` | — | **UNMEASURED** | M documents, `structure_null_share` 44 % measured | the corpus is one generator's cut of one product, and it is the bundle under suspicion; a bar read off it would pass it by construction |
| `.html` | — | **UNMEASURED** | 0 bundles in this repository | the HTML measurement was made outside it |
| `.htm`, `.txt`, `.csv`, `.json`, `.pptx`, `.odt`, `.rtf` | — | **UNMEASURED** | 0 corpus documents | no corpus class in `extract._EVIDENCE`; `.pptx`/`.odt`/`.rtf` are `constructed`, N = 12 |
### Candidates measured and not shipped
Three fasit-free metrics were measured over the same four Door B bundles, with
the known-bad arm (`html-arm-828`, 1 148 of 2 761 boundaries) and the
known-good arm (`std-xml-generic`, 2 759 of 2 761) named in advance:
the known-bad arm (`html-arm`, 1 148 of N boundaries) and the
known-good arm (`std-xml-generic`, N 2 of N) named in advance:
| candidate | known-bad arm | known-good arm | pdf arm | K2 reference | verdict |
|---|---|---|---|---|---|
| duplicate titles within a document | 0/3 206 (0.0 %) | 349/2 761 (12.6 %) | 355/2 182 | 11/453 | **wrong direction** |
| concepts under 40 characters | 181/3 206 (5.6 %) | 402/2 761 (14.6 %) | 1/2 182 | 25/453 | **wrong direction** |
| duplicate titles across the whole bundle | 1 211/3 206 (**37.8 %**) | 349/2 761 (12.6 %) | 355/2 182 (16.3 %) | 26/453 (5.7 %) | orders correctly, **no bar** |
| duplicate titles within a document | 0.0 % | 349/N (12.6 %) | 355/2 182 | 11/453 | **wrong direction** |
| concepts under 40 characters | 5.6 % | 402/N (14.6 %) | 1/2 182 | 25/453 | **wrong direction** |
| duplicate titles across the whole bundle | **37.8 %** | 349/N (12.6 %) | 355/2 182 (16.3 %) | 26/453 (5.7 %) | orders correctly, **no bar** |
The third orders all four bundles correctly and is still not shipped: any bar
that separates them sits between 16.3 % and 37.8 %, which is a number read off
@ -156,8 +158,8 @@ STATE rather than taken here.
The 41.6 % arm is `UNMEASURED` under this gate, and that is the honest answer
rather than a hidden one: the metric that fells it is `boundary_share`, which
needs the publisher's own declared structure. The reference standard has one (2 761 STS `<sec>`
titles, `<consumer repository>/fasit-2761.json`), and 4 of the 9
needs the publisher's own declared structure. The reference standard has one (N STS `<sec>`
titles, `<consumer repository>/fasit.json`), and 4 of the 9
bundles above have no fasit at all. A `--fasit` input, a threshold on
`boundary_share`, and the operator's bar are one further round, named in STATE
and not begun here. **That round is § 7 below** (G37b, order
@ -183,29 +185,29 @@ records traps.
### § 7.1 — P1: the normalisation, measured on a known-positive first
The fasit is `<consumer repository>/fasit-2761.json` — 665 134 bytes,
a JSON list of exactly **2 761** objects, each carrying `title` and `norm`
The fasit is `<consumer repository>/fasit.json`,
a JSON list of exactly **N** objects, each carrying `title` and `norm`
(`sec_type`, `depth`, `label`, `outline_page` and `id` as well). Every `norm` is
unique, and so is every `title`.
**The normalisation is derived, not guessed.** Strip all whitespace, then
lowercase, reproduces the fasit's own `norm` from its own `title` on **2 761 of
2 761** rows. Two candidates were run first and fell on the same file:
lower-casing and keeping only alphanumerics scores **58 of 2 761** (it eats the
lowercase, reproduces the fasit's own `norm` from its own `title` on **N of
N** rows. Two candidates were run first and fell on the same file:
lower-casing and keeping only alphanumerics scores **58 of N** (it eats the
`.` in `2.1Grunnoppskrifter` and the `-` in `3Mel- og gjærtyper`). Unicode NFC
makes no difference here — 2 761 of 2 761 titles and norms are already NFC — and
makes no difference here — N of N titles and norms are already NFC — and
the rule is the one the consumer's own measuring script applies, so a number
produced here and a number produced there are the same number.
**P1's own bar is 99 % on the known-good arm, and the literal reading of it
fails — for a reason that is measured and is not the normalisation.** On
`std-xml-generic` (the declared-structure arm, 2 761 concepts):
`std-xml-generic` (the declared-structure arm, N concepts):
| match form | recovered of 2 761 |
| match form | recovered of N |
|---|---|
| literal: normalised concept title equals the fasit's `norm` | **22** (0.8 %) |
| pair: `(concept's own directory, normalised residual title)` | **2 737** (99.1 %) |
| either | **2 759** (99.9 %) |
| pair: `(concept's own directory, normalised residual title)` | **N 24** (99.1 %) |
| either | **N 2** (99.9 %) |
The cause is this repository's own id form: STS glues the numbering token into
`<title>` (`11.1Surdeigsstarter`), and okf's default route moves that token into the
@ -214,21 +216,21 @@ the literal form alone would report a 99.9 % arm as 0.8 % and call it a
segmentation defect. Both forms therefore ship, neither as a fallback for the
other: the literal form wants the title *with* its token, the pair form *without*,
and no single bundle can offer both. The `std-d1` build is the control in
the opposite direction — literal **2 727**, paired **0**.
the opposite direction — literal **N 34**, paired **0**.
That decomposition is not this round's invention either. It is the M8 correction
the consumer took verbatim from this repository's own round-14 report; the
instrument here reproduces their two forms so the two repositories cannot
silently measure different things.
**Known-positive delivered: 2 759 of 2 761 (99.9 %)**, the published number to
the unit. **Known-negative delivered: `html-arm-828` at 1 148 of 2 761
**Known-positive delivered: N 2 of N (99.9 %)**, the published number to
the unit. **Known-negative delivered: `html-arm` at 1 148 of N
(41.6 %)**, also to the unit — the arm the bundle-only gate returned
`UNMEASURED`/exit 3 for.
### § 7.2 — P2: the threshold rests on one product, and says so
The fasit describes **one** document: a 701-page reference standard. Every number
The fasit describes **one** document: a long reference standard. Every number
below is that product's. The bar is declared with `corpora = 1` in the code
(`quality.BOUNDARY_THRESHOLD`), the row prints `N = 1 corpus` on every run, and
the printed line also states what `--fasit` is: **an assertion by the caller**
@ -236,7 +238,7 @@ that this bundle is a build of the document the fasit describes — the same
posture `okf consume --ref` has.
That is not a formality. Measured: the pinned 43-document K2 reference bundle
scores **0 of 2 761** and `corpus-a` scores **0 of 2 761**. Both read `FAIL`,
scores **0 of N** and `corpus-a` scores **0 of N**. Both read `FAIL`,
and in both cases the right reading is *the assertion was wrong*, not *the
bundle is bad*. A gate that could tell those two apart would need a bar on
"does this fasit describe this bundle", and that bar would have to be read off
@ -244,15 +246,15 @@ the bundles it judges.
### § 7.3 — every bundle measured, with the fasit
`okf quality <bundle> --fasit <consumer repository>/fasit-2761.json`
`okf quality <bundle> --fasit <consumer repository>/fasit.json`
| bundle | concepts | literal | paired | recovered of 2 761 | verdict | rc |
| bundle | concepts | literal | paired | recovered of N | verdict | rc |
|---|---|---|---|---|---|---|
| `std-xml-generic` (reference) | 2 761 | 22 | 2 737 | **2 759** (99.9 %) | PASS | 0 |
| `std-xml-v080` | 2 761 | 22 | 2 737 | **2 759** (99.9 %) | PASS | 0 |
| `std-consumer` | 2 756 | 20 | 2 732 | **2 752** (99.7 %) | FAIL | 1 |
| `std-d1` (the consumer's own pipeline) | 2 728 | 2 727 | 0 | **2 727** (98.8 %) | FAIL | 1 |
| `html-arm-828` (known-bad arm) | 3 206 | 1 130 | 28 | **1 148** (41.6 %) | FAIL | 1 |
| `std-xml-generic` (reference) | N | 22 | N 24 | **N 2** (99.9 %) | PASS | 0 |
| `std-xml-v080` | N | 22 | N 24 | **N 2** (99.9 %) | PASS | 0 |
| `std-consumer` | N 5 | 20 | N 29 | **N 9** (99.7 %) | FAIL | 1 |
| `std-d1` (the consumer's own pipeline) | N 33 | N 34 | 0 | **N 34** (98.8 %) | FAIL | 1 |
| `html-arm` (known-bad arm) | a few thousand | 1 130 | 28 | **1 148** (41.6 %) | FAIL | 1 |
| `corpus-a` (another product) | 446 | 0 | 0 | **0** | FAIL | 1 |
| `K2-bundle-default-20260912` (another product) | 453 | 0 | 0 | **0** | FAIL | 1 |
@ -260,19 +262,22 @@ the bundles it judges.
| metric | threshold | N | source |
|---|---|---|---|
| `boundary_share` | **2 759/2 761** | 2 761 declared boundaries, **1 corpus** | `std-xml-generic` against `fasit-2761.json` |
| `boundary_share` | **999/1000** | N declared boundaries, **1 corpus** | `std-xml-generic` against `fasit.json` |
Set at the value measured on the pinned artifact rather than at a rounder
number nearby — the same rule the two bars in § 4 follow, and the same
semantics: **a regression bar, not a quality claim**. `PASS` means *no worse
than that build of that product*.
The bar is 99.9 %, just under the known-good arm's measured share (N 2 of N,
which sits 0.03 percentage points above the bar). Unlike the two bars in § 4 it is not the
measured fraction itself, and nothing turns on that: every build measured in
§ 7.3 reads the same verdict under 999/1000 as under the measured fraction. The
semantics are the same as in § 4: **a regression bar, not a quality claim**.
`PASS` means *no worse than that build of that product, to within 0.03
percentage points*.
**It is a tight bar and the cost is stated rather than hidden:** two of the four
builds of the reference standard measured fall under it, at 2 752 and 2 727 of 2 761 — 0.25 % and
builds of the reference standard measured fall under it, at N 9 and N 34 of N — 0.25 % and
1.2 % below the reference. The class separation is nevertheless wide: **any bar
between 1 148/2 761 (41.6 %) and 2 727/2 761 (98.8 %) separates the known-bad
arm from every build of the reference standard measured.** The shipped bar is the strictest point of
that interval because it is the only one in it that is read off a pinned
between 1 148/N (41.6 %) and (N 34)/N (98.8 %) separates the known-bad
arm from every build of the reference standard measured.** The shipped bar sits
above that interval, just under the one share that is read off a pinned
artifact instead of off the bundles being judged. Moving it inside that interval
is the operator's call, and the interval is published here so the call can be
made on numbers.
@ -284,7 +289,7 @@ of four rows gives `UNMEASURED` rather than a share.
### § 7.5 — what did not change
- **Without `--fasit`, nothing moves.** `okf quality <bundle>` on
`html-arm-828` still prints one `.md` row, no boundary row, and exits **3**.
`html-arm` still prints one `.md` row, no boundary row, and exits **3**.
A test holds it, and the word "boundary" does not appear in the per-type half
of the output.
- **No version bump, no tag, no push.** v0.9.0 was already bumped by G37 and is
@ -298,9 +303,9 @@ of four rows gives `UNMEASURED` rather than a share.
`boundary_share` does on a second declared structure, because no second one
exists in reach.
- **The bar fails builds nobody calls bad.** Named above rather than tuned
away. The alternative — a bar at 2 727 — is a number read off a bundle the
away. The alternative — a bar at N 34 — is a number read off a bundle the
gate judges.
- **A zero is ambiguous by construction.** 0 of 2 761 means *this fasit does not
- **A zero is ambiguous by construction.** 0 of N means *this fasit does not
describe this bundle* on both bundles where it occurred, and the gate cannot
distinguish that from a total segmentation failure. It prints both match-form
counts so a reader can see which it is (a real build of the product scores in
@ -321,7 +326,7 @@ of four rows gives `UNMEASURED` rather than a share.
- **`structure_null_share` has never fired on a real bundle.** Of the five Door
B bundles measured, none is worse than its reference; the rule's `FAIL` side
is exercised on constructed bundles in `tests/test_quality.py` only.
- **`empty_concepts` is 0 on 8 602 of 8 602 concepts.** The bar is definitional
- **`empty_concepts` is 0 on every concept of the four bundles.** The bar is definitional
and has never fired on real data either. A metric that never fires is not
evidence that nothing is wrong.
- **One rule was found by running the gate, not by reading it.** A one-document

View file

@ -4,7 +4,7 @@
`8e82da4` (the instrument) · **Measured from:** a frozen `git archive` export of
`8e82da4` installed into a scratch virtualenv (`__file__` under
`/private/tmp/...`, never `/Users/ktg/repos`, never the `okf` on PATH), Python
3.14, guard 1.4.0. One document: a 701-page reference standard, built twice in scratch
3.14, guard 1.4.0. One document: a long reference standard, built twice in scratch
from the publisher's own NISO-STS source, once with `--shell-parent` and once
without. The consumer repository was read only: `git status --porcelain` empty
before and after, `build/ferdig/` listing identical including mtimes.
@ -23,7 +23,8 @@ decomposes both.
carried both figures and asked which one a fresh measurement reproduces: it
reproduces the dispatch note's, not round 21's. Measured here as the byte
difference between each concept's body and the same body with the door's
line removed, over the 2 761 concepts of the flagged build: **71 616 B of
line removed, over the concepts of the flagged build (one per declared
titled section): **71 616 B of
1 607 855 B body bytes**, the line itself 70 941 B, median line **101 B**,
max **245 B**, min 60 B, and **68.3 %** of the 103 835 B those 675 bodies
hold. Round 21's median and max are each exactly 2 B above these, which is
@ -65,7 +66,7 @@ it.
| reading | hit@1 | hit@8 | hit@50 | KP rank, `k` 8 | KP rank, `k` 50 | denominator |
|---|---|---|---|---|---|---|
| X | 6/6 | 6/6 | 6/6 | 1 | 1 | 6 questions, 2 761 concepts |
| X | 6/6 | 6/6 | 6/6 | 1 | 1 | 6 questions, one concept per declared titled section |
| Y | 6/6 | 6/6 | 6/6 | 1 | 1 | 6 |
| Z | 6/6 | 6/6 | 6/6 | 1 | 1 | 6 |
@ -147,7 +148,7 @@ second statement points at a fix.
| row | result | denominator |
|---|---|---|
| rows where X and W deliver a different SET | **1** | 16 |
| the concept displaced | `12-11/tilrigging`, KN at `k` 50: 43 delivered with the link bytes, 44 without | 1 |
| the concept displaced | `12-11/<section>`, KN at `k` 50: 43 delivered with the link bytes, 44 without | 1 |
| rows where the budget binds at the default `k` | 0 (max `spent` 54 025 of 120 000) | 8 |
**Rank movement and budget displacement are different sizes.** At the default
@ -217,7 +218,7 @@ Measured here rather than quoted:
|---|---|---|
| questions whose fasit section is a heading-only concept | **0** | 7 with a fasit (8 questions, KN has none) |
| fasit sections present in the bundle at all | 7 | 7 |
| heading-only concepts in the document | **710** | 2 761 |
| heading-only concepts in the document | **710** | all concepts (one per declared titled section) |
| of those, with an ancestor holding text (a parent to follow) | **675** | 710 |
| of those, with no such ancestor (nothing to inherit) | **35** | 710 |
@ -252,7 +253,7 @@ payloads are conformant too, and they answer nothing.
## Honesty limits
- **N = 1 document.** Everything here is one 2 761-concept standard from one
- **N = 1 document.** Everything here is one standard of a few thousand concepts from one
publisher. The mechanism -- a bundle-absolute path repeating the document
directory in every linked body -- is a property of the FORM and would appear
in any bundle, but its size depends on whether a question happens to name the

View file

@ -179,13 +179,13 @@ left alone. Everything below ran from `<export>/.venv/bin/`.
| `contract_check.RULES` | 1 | **17**, equal to the work tree's |
| `okf check` on the shipped skill | 1 | rc 0, `conformant: 17 rules over 3 excerpts and 0 withheld entries, 0 findings` |
| `okf project` on the five-document folder | 5 documents | **26** concepts, **52** md files, **0 of 5** left out -- R6's numbers to the unit |
| HTML corpus | 828 documents | `proposed 828 plan(s); 0 document(s) with no boundary; 0 unreadable`, **6 015** md |
| reference standard, XML | 110 source files, 1 at top level | **2 761** concepts over 2 737 directories, of which `11` and `12` hold two each; document directory is its declared `<doc-number>`, not the UUID |
| HTML corpus | M documents | `proposed M plan(s); 0 document(s) with no boundary; 0 unreadable`, md count unchanged |
| reference standard, XML | 110 source files, 1 at top level | **N** concepts (N = its declared titled sections) over N 24 directories, of which `11` and `12` hold two each; document directory is its declared `<doc-number>`, not the UUID |
| hit@1 / @8 / @50 on the reference standard | **N = 6** scored of 8 asked | **6/6 · 6/6 · 6/6** |
| known-positive rank | 1 | rank **1** at the default `k` and rank **1** at `k` 50 |
| known-negative (K3-17) | 1 question | rank 1 = the RITB section (**6 793 B**), rank 2 = «Prøvedrift» (**10 590 B**); both concepts present and delivered |
| block `sources:` read to 0 empty | 4 605 files | see below |
| the flow string through PyYAML | 4 605 files | see below |
| block `sources:` read to 0 empty | every block file of four bundles | see below |
| the flow string through PyYAML | every block file of four bundles | see below |
**The hit@1 row matters more this round than last**, because K3-25 makes
`link_in_signal=False` the default and that is a ranking change on a published
@ -198,22 +198,21 @@ is rank 1, the known-positive is rank 1 at both `k`, and S1's `spent` is
because it is not named in K3-24's report: a downstream consumer
repository's finished-build directory. Chosen after verifying its
denominators against K3-24's table first: files carrying a frontmatter block
are **2 757 / 447 / 1 134 / 271** for the four bundles, exactly K3-24's. Of those, the ones carrying a BLOCK `sources:`
are **2 756 / 446 / 1 133 / 270** = **4 605**. All three flat readers
are exactly K3-24's for the four bundles. Of those, the ones carrying a BLOCK `sources:`
are all but one of those files in each bundle. All three flat readers
(`materialize.parse_frontmatter`, `structure._split_frontmatter`,
`profiles._split_frontmatter`) return a non-empty value on **0 empty of each**,
reading nothing but the bundles.
**The known-positive control for that zero.** The same read with `v0.8.4`'s
flat reader (`git archive v0.8.4 src/llm_ingestion_okf` into scratch,
`sys.path.insert`) returns EMPTY on **2 756 / 446 / 1 133 / 270** -- 4 605 of
4 605, K3-24's before-numbers to the file. The instrument can find, so the zero
`sys.path.insert`) returns EMPTY on every one of them, K3-24's before-numbers to the file. The instrument can find, so the zero
is a measurement.
**The reservation, measured and not asserted.** The string
`parse_frontmatter` now returns for a block `sources:` was passed to
`yaml.safe_load` on each of the same 4 605 files. It is read back on **0 of
4 605**. The reason is visible in one value:
`yaml.safe_load` on each of the same files. It is read back on **0** of
them. The reason is visible in one value:
```
[{ resource: https://example.test/api/nisosts/000000?languageCode=nb, title: Q500:2024 }]
@ -237,7 +236,7 @@ work tree's.
claim and not a claim that the channel works.
- **`okf project`'s 26 is ONE corpus of five documents** -- a regression
control, not a sample.
- **828 of 828 and 2 761 of 2 761 are ONE publisher's files from ONE product.**
- **M of M and N of N are ONE publisher's files from ONE product.**
- **hit@1 and the known-positive rank are 6 scored questions and ONE
known-positive on ONE bundle, against a fasit someone else set.**
- **The known-negative is ONE constructed question on ONE small bundle.**
@ -253,7 +252,7 @@ work tree's.
- **`okf.parse_frontmatter` CHANGES PUBLIC READ BEHAVIOUR.** A consumer who
passed its return value for `sources` to a YAML reader and got something
empty that parsed now gets a parse error: PyYAML reads the returned string
back on **0 of 4 605** block files. That is a regression for them even though
back on **0** of those block files. That is a regression for them even though
it is a fix for everyone who read the empty value as an absent address. It is
stated here, in the CHANGELOG and in the tag-history row, because a release
that sells a read fix without saying who it costs lies by omission.

View file

@ -68,7 +68,7 @@ and exits 1. `tests/test_content_accounting.py` holds that.
|---|---|
| whole | 0 unaccounted over 31 096 elements |
| cut to half | 4 823 paragraphs, 3 621 sections and 55 list items unaccounted — but only 3 titles and 16 section labels |
| first 200 characters only | 12 475 of 12 528 paragraphs and 2 761 of 2 761 titles unaccounted, but 150 of 197 cells |
| first 200 characters only | 12 475 of 12 528 paragraphs and every title unaccounted, but 150 of 197 cells |
- **The limit.** The check proves that a string is present, not where it is.
A short element (a label such as `84.1`, a one-word title, a one-number

View file

@ -46,16 +46,16 @@ Four documents are also pinned to hand counts.
**Two witnesses disagreed by tag, and both disagreements were about placement
rather than content** (the reference standard, measured). A section's label is `sec/label` on
7 714 sections in the XML. The JSON puts it at `sec/label` on 4 954 of them
and at `sec/title/label` on the other 2 760. A table's label is
7 714 sections in the XML. The JSON puts it at `sec/label` on most of them
(64 %) and at `sec/title/label` on the rest. A table's label is
`table-wrap/label` in the XML (10) and `table-wrap/table/caption` in the JSON
(10). The witness therefore counts ROLES, and the mapping is written in
`okf_witness._sts_role` together with these numbers. After the mapping, the
two witnesses agree on all ten roles.
The PM's figures were re-measured and all hold: `p` 12 528, `title` 2 761,
The PM's figures were re-measured and all hold: `p` 12 528, `title` N (the declared titled sections),
`td` 197, `list-item` 111, `graphic` 50, 109 other files in the zip (50
pointed at, 59 not). The PDF witnesses agree at 701 pages and 50 images;
pointed at, 59 not). The PDF witnesses agree on the page count and 50 images;
pdfplumber takes 50 s for that and poppler 1 s.
## The door the capability must open

View file

@ -161,7 +161,7 @@ simply too large — which is what the test asserts against.
## The cost on real documents
Every PDF on this machine — 78 documents, the K2 reference corpus in both
`trinn1` and `trinn2`, the shipped fixtures and a 701-page reference standard — was
`trinn1` and `trinn2`, the shipped fixtures and a long reference standard — was
run through `_pdf_images` page by page on both sides, and each side's census
was run from a **pinned tree** (`git archive 3b587ea` for before, a copy for
after) with the imported module's own `__file__` printed as the control. The

View file

@ -75,7 +75,7 @@ author wrote to themselves.
agree" — could not see a hole in it. They are written apart now. Measured on
the reference standard 2026-09-18: the XML delivery places a section's label at `sec/label`
**7 714** times and inside a title **0** times; the JSON delivery puts
**2 760** of them inside the title. The XML map was narrowed to the XML fact,
**36 %** of them inside the title. The XML map was narrowed to the XML fact,
and a JSON twin fixture carrying the nested placement holds the difference
live — without it, swapping the maps changed nothing measurable.

View file

@ -24,10 +24,10 @@ Nothing is tagged, pushed or released here.
`llm-ingestion-guard` 1.4.0 keeps U+00AD in `_ZERO_WIDTH_CPS` beside U+200B,
U+200C, U+200D and U+FEFF, and `output:zero-width-present` is an **any-tier
carrier**: `fail_secure` at every trust level, with no sanitisation and no
exception. A 701-page reference standard carries **71 U+00AD and 0** of the four real
exception. A long reference standard carries **71 U+00AD and 0** of the four real
zero-width characters (U+2060 also 0). All 71 are Norwegian hyphenation points
inside words -- `ar[SHY]beider` and its like -- so a
701-page reference standard was unreadable for the whole chain over typography. The
inside words -- `ar[SHY]beider` and its like -- so that
reference standard was unreadable for the whole chain over typography. The
verdict was formally right and materially a false positive.
Of the three ways out, the operator chose one door in okf, before the guard,

View file

@ -164,8 +164,8 @@ an unknown bundle answered instead of refused (row 6).
## Limits, stated rather than implied
* **Nothing is cached, and it costs.** On the 2 756-concept bundle the content
identity is a 0.75 s hash of the whole concept tree and one `okf_ask` is
* **Nothing is cached, and it costs.** On a one-document bundle of a few thousand
concepts the content identity is a 0.75 s hash of the whole concept tree and one `okf_ask` is
5.6 s. Row 2's full run over four bundles and 181 pairs took **4 min 13 s**.
A cache would have to be keyed on something cheaper than the hash and still
correct; no such key is shipped, and the cost is the price of the row-3 result

View file

@ -14,11 +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 a Norwegian reference standard published both as a 701-page
The need is not hypothetical. Measured on a reference standard published both as a long
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. Section 84 says
its classes "are given in table 84-2" and table 84-2 is a JPEG. A bundle
those tables as raster images in **both** deliveries. One section says
its classes "are given in table N-N" and that table is a JPEG. A bundle
built from that document reads as complete and is not.
## The proposed wording