docs(k3): round 14, the declared-structure route at the shipped defaults
The report, with every measurement carrying its denominator and both instrument forms side by side on every row -- including the proof that the two forms cannot both reach 2 761, because 2 739 of 2 761 fasit titles carry a numbering token and the two keys then demand mutually exclusive strings. Also states two things round 13 left unsaid: - XML that is not STS gives 0 plans and exit 2, and the `.txt` known-positive shows it is general `okf build` behaviour for any structureless document, not an `.xml` defect. Separating the two states would change the outcome on 0 of the 4 reference corpora, so the gate stays. - Which two instruments were corrected in scratch and what the correction is. With the shipped matcher the XML bundle scores 0/6 with `rang: null` on every question, including the known-positive whose top excerpt IS the fasit. Round 13's 14 two-file directories are re-classified on measurement: they were false positives of the text route reading the document's own contents listing, not the publisher reusing a title, and they are gone. Two remain, the same two the PDF arm has. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
ee12f644ca
commit
078b7df94f
3 changed files with 448 additions and 2 deletions
45
CHANGELOG.md
45
CHANGELOG.md
|
|
@ -9,6 +9,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
### Added
|
||||
|
||||
- **A section the SOURCE DECLARES now takes the route declared structure takes,
|
||||
at the shipped defaults.** `.xml` gained a reader in the entry above and the
|
||||
reader reached its ceiling -- **2 761 of 2 761** heading lines -- while the
|
||||
build delivered **23 concepts and 15 of 2 761 boundaries**. Everything after
|
||||
the reader ate it, and both steps are measured: the **orphan check** removed
|
||||
**710 of 2 761** (710 of 710 removed headings are followed immediately by
|
||||
another heading and **0 of 2 051** delivered ones are -- they are container
|
||||
sections), and **Arm F** folded **2 066** more, 2 089 -> 23.
|
||||
- `extract.xml_outline` reports the marks the reader wrote itself. There is
|
||||
**no bridge** and therefore no tolerance constant and no `unresolved`
|
||||
bucket: the reader appended the line it names. That is the difference from
|
||||
`pdf_outline`, whose naive nearest-line rule was wrong on 1 840 of 2 762.
|
||||
- `propose.RULE_XML_SECTION` (`rule:xml-section`) is its own name in
|
||||
`RULE_NAMES` and in `_ORPHAN_EXEMPT`, so an artifact still distinguishes an
|
||||
element the reader transcribed from a bridged bookmark
|
||||
(`rule:pdf-outline`) and from a heading somebody guessed (`rule:heading`).
|
||||
- The route is chosen by the ROW (`DECLARED_STRUCTURE_IDS = {"xml"}`), never
|
||||
by the text: the same markdown arriving from a `.md` file is still a guess.
|
||||
**No other file type changes one byte** -- `diff -r` on the five-document
|
||||
reference folder is empty (52 md, 26 concepts, 0 of 5 rejected, 0 `.xml`
|
||||
files in it), `okf project` is still byte-equal to `okf build`, the pinned
|
||||
K2 bundle is unchanged, and the PDF arm still proposes 2 762 segments.
|
||||
- Measured at SHIPPED DEFAULTS, not behind a flag: **2 761 concepts**;
|
||||
**2 761 of 2 761** declared sections became a concept with the source's own
|
||||
directory and title; **0** concepts match no declaration; `a)`-points
|
||||
**0 of 4 954**; table blocks **10 of 10**; hit@1/8/50 **3/6 · 5/6 · 6/6**
|
||||
(from 0/6 · 0/6 · 0/6) with the known-positive at rank 1. Cross-arm,
|
||||
**2 761 shared concept ids** -- 100 % of the XML bundle and 2 761 of 2 762
|
||||
of the PDF arm's, up from round 13's 2 022.
|
||||
- Two directories of 2 738 still hold two concepts (`11`, `12`): the
|
||||
publisher reuses a section number for two distinct sections, and it is the
|
||||
same 2 the PDF arm has. Round 13's 14 such directories were false positives
|
||||
of the text route reading the document's own contents listing, and they are
|
||||
gone.
|
||||
|
||||
- **`.xml` is a core file type, NISO-STS aware, with a generic fallback.** A
|
||||
publisher's own viewer delivers a zip that holds 0 html, 1 xml and 109
|
||||
images; `okf build` on it was **110 of 110 unreadable, 0 plans, exit 2**, and
|
||||
|
|
@ -38,6 +73,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- `.xml` never routes through the converter, and it is measured about 12x
|
||||
faster and about 30x smaller in peak memory than the PDF arm on the same
|
||||
document and the same machine.
|
||||
- **XML that is not STS gives 0 plans and a FAILED build, and that is not an
|
||||
`.xml` defect.** The known-positive that decides it: a folder holding one
|
||||
`.txt` of prose with no headings gives exactly the same three lines and the
|
||||
same exit 2. This is general `okf build` behaviour for any structureless
|
||||
document -- extraction works, 0 unreadable, the text is there, and the
|
||||
proposer has nothing to propose. The gate that refuses a run with no plans
|
||||
stays: a run replaying zero plans would emit a flat bundle and report it as
|
||||
a success. Separating "0 plans, 0 unreadable" from "0 plans because nothing
|
||||
could be read" would change the outcome on **0 of the 4** reference
|
||||
corpora, so it is not separated.
|
||||
- Report: `docs/2026-09-11-k3-runde13-xml-sts.md`.
|
||||
|
||||
### Fixed
|
||||
|
|
|
|||
44
CLAUDE.md
44
CLAUDE.md
|
|
@ -60,7 +60,7 @@ one boundary rule:
|
|||
are 0 occurrences in `uv.lock` -- so it is core beside `.html` rather than
|
||||
behind `[extract]`, which would make a pure-stdlib type binary-dependent.
|
||||
**The output grammar is MARKDOWN**, the same the office rows reach the
|
||||
proposer through, and `propose.py` is untouched: `<sec>` with a `<title>`
|
||||
proposer through: `<sec>` with a `<title>`
|
||||
becomes one ATX line carrying `<label>` + space + `<title>` at its own
|
||||
nesting depth, `<sec>` with only a `<label>` becomes a body line with the
|
||||
label in front (never a heading -- 4 954 of 7 715 are lettered points and one
|
||||
|
|
@ -80,7 +80,47 @@ one boundary rule:
|
|||
>= 2.4.0 and not from Python, while `pyproject.toml` requires only `>=3.10`.
|
||||
XML that is not STS keeps its text in document order and gets NO invented
|
||||
structure, and `.xml` never routes through the converter -- a second parser
|
||||
that would never see that refusal. The registries are COUPLED: a row in
|
||||
that would never see that refusal. **XML that is not STS also gets 0 plans
|
||||
and a FAILED build (exit 2), and that is NOT an `.xml` defect**: a folder
|
||||
holding one `.txt` of prose with no headings gives the same three lines and
|
||||
the same exit, so it is general `okf build` behaviour for any structureless
|
||||
document. The gate stays -- a run replaying zero plans would emit a flat
|
||||
bundle and call it success -- because separating "0 plans, 0 unreadable" from
|
||||
"0 plans because nothing could be read" changes the outcome on **0 of the 4**
|
||||
reference corpora.
|
||||
**THE READER REACHED ITS CEILING IN ROUND 13 AND THE BUILD DID NOT, AND
|
||||
ROUND 14 CLOSED IT AT THE SHIPPED DEFAULTS.** The reader emitted 2 761 of
|
||||
2 761 heading lines while the build delivered **23 concepts and 15 of 2 761
|
||||
boundaries** -- two steps after the reader, each measured: the orphan check
|
||||
took **710 of 2 761** (710 of 710 removed headings are followed immediately
|
||||
by another heading, **0 of 2 051** delivered ones are -- they are container
|
||||
sections) and Arm F took **2 066** more, 2 089 -> 23. `find_candidates`
|
||||
already skipped both for `outline_marks`, which is why the PDF bookmark arm
|
||||
reaches 2 762; an STS `<sec><title>` is the same class of declaration and
|
||||
only arrived as `rule:heading`. The fix is ONE new rule constant reached from
|
||||
ONE row: `extract.xml_outline` reports the marks the reader WROTE ITSELF --
|
||||
**no bridge, no tolerance constant, no `unresolved` bucket**, the difference
|
||||
from `pdf_outline` whose naive nearest-line rule was wrong on 1 840 of 2 762
|
||||
-- `propose.RULE_XML_SECTION` (`rule:xml-section`) is its own name in
|
||||
`RULE_NAMES` and `_ORPHAN_EXEMPT`, and `build_plan` chooses the route by the
|
||||
**ROW** (`DECLARED_STRUCTURE_IDS`), never by the text: the same markdown from
|
||||
a `.md` file is still a guess and still carries `rule:heading`. At shipped
|
||||
defaults, no flag: **2 761 concepts**, **2 761 of 2 761** declared sections
|
||||
became a concept with the source's own directory AND title, **0** concepts
|
||||
matching no declaration, `a)`-points **0 of 4 954**, table blocks **10 of
|
||||
10**, hit@1/8/50 **3/6 / 5/6 / 6/6** from 0/6 / 0/6 / 0/6 with the
|
||||
known-positive at rank 1, and **2 761 shared concept ids** with the PDF arm
|
||||
(100 % of this bundle, 2 761 of 2 762 of that one) against round 13's 2 022.
|
||||
**NO other file type changes one byte and it is MEASURED on the bytes**: the
|
||||
whole 43-document reference corpus rebuilt is `diff -r`-identical to the
|
||||
pinned bundle (865 md), the five-document folder is `diff -r`-identical,
|
||||
`okf project` stays byte-equal to `okf build`, and the PDF arm still proposes
|
||||
2 762. Two directories of 2 738 still hold two concepts (`11`, `12`) -- the
|
||||
publisher reuses a section number, the same 2 the PDF arm has, and 0 is not
|
||||
reachable without inventing an id; round 13's 14 such directories were false
|
||||
positives of the TEXT route reading the document's own contents listing and
|
||||
are gone. Report:
|
||||
`docs/2026-09-10-k3-runde14-deklarert-struktur-tar-ruten.md`. The registries are COUPLED: a row in
|
||||
`_CORE_EXTRACTORS` and not in `segmentation._STDLIB_EXTRACTOR_IDS` refuses
|
||||
every proposal for the type, two layers away from the extractor.
|
||||
`pdf`/`docx`/`xlsx` only via
|
||||
|
|
|
|||
361
docs/2026-09-10-k3-runde14-deklarert-struktur-tar-ruten.md
Normal file
361
docs/2026-09-10-k3-runde14-deklarert-struktur-tar-ruten.md
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
# K3 round 14: the extractor reached the ceiling, the build did not
|
||||
|
||||
Round 13 gave `.xml` a reader that emits **2 761 of 2 761** ATX lines for
|
||||
R761's titled `<sec>` elements, with the text preserved exactly. At **shipped
|
||||
defaults** the build then delivered **23 concepts and 15 of 2 761 boundaries**.
|
||||
The distance between the two was not extraction and not a missing rule: it was
|
||||
two steps that run *after* the reader and judge a publisher's own declaration
|
||||
as if it were a guess.
|
||||
|
||||
This round closes that distance at the defaults, not behind a flag.
|
||||
|
||||
| row | before (HEAD `9d5ebc3`) | after (`ee12f64`) | denominator |
|
||||
|---|---|---|---|
|
||||
| concepts, shipped defaults | 23 | **2 761** | — |
|
||||
| declared sections that became a concept | 15 | **2 761** | 2 761 |
|
||||
| concept title == source `<title>` after NORM | — | **2 761** | 2 761 |
|
||||
| concepts matching no declaration | 8 | **0** | 2 761 |
|
||||
| `a)`-points that became their own concept | 0 | **0** | 4 954 |
|
||||
| markdown table blocks | 10 | **10** | 10 `<table-wrap>` |
|
||||
| directories holding two concept files | 14 (at `--no-unit-fold`) | **2** | 2 738 |
|
||||
| hit@1 · hit@8 · hit@50 (k=50) | 0/6 · 0/6 · 0/6 | **3/6 · 5/6 · 6/6** | 6 |
|
||||
| known-positive rank | 1 of 17 delivered of 23 | **1** | 1 |
|
||||
|
||||
Every figure in this file was measured in this session. Where a figure differs
|
||||
from what the order stated, the measured one is used and the difference is
|
||||
named.
|
||||
|
||||
## 1. What was reproduced first, and what did not reproduce
|
||||
|
||||
`HEAD` was `9d5ebc3`, `git status --short` empty, `git ls-remote origin
|
||||
refs/heads/main` = `9d5ebc3c29b417f3bae90458ee041ea1b10a19ea`. **STATE.md said
|
||||
15 unpushed commits and a remote at `6ff84d7`; the remote is at HEAD, so the
|
||||
push list was already empty when this session opened.** That is the one premise
|
||||
of the order's own package that did not hold, and it holds in the safe
|
||||
direction.
|
||||
|
||||
Suite in the working tree before any change: **1 567 passed, 1 skipped**.
|
||||
`ruff --version` 0.16.6, `ruff check .` and `ruff format --check .` clean over
|
||||
116 files, `mypy src` clean over 21 files. Install channel
|
||||
`uv sync --frozen --extra extract`. `pyproject.toml` version `0.7.0`, tag
|
||||
`v0.7.0` on `1260fac` — no bump, no tag in this round.
|
||||
|
||||
The baseline was rebuilt from a frozen `git archive` export of `9d5ebc3`
|
||||
(never from the editable tree) and reproduced the order's numbers exactly:
|
||||
23 concepts, **15 of 2 761** under the corrected instrument key and **0 of
|
||||
2 761** under the shipped one, 8 false positives of 23, and hit@1/8/50 =
|
||||
**0/6 · 0/6 · 0/6** with the known-positive at rank 1 of 17 delivered.
|
||||
|
||||
### The instrument, and both of its faults are real
|
||||
|
||||
`experiments/sk2_grenser.py` sets `cnorms = ckeys` and uses the `literal` set
|
||||
on the `[ARTEFAKT]` line **alone**, so HOVEDTALL, depth, FALSKE POSITIVE,
|
||||
DELMENGDE, FORSIDE and K3-9 all score the **pair** key `(concept directory,
|
||||
normalised title)`. `experiments/hitk_sk2.py` pairs `fasit_key` (which splits
|
||||
the numbering token off) with `excerpt_key` (which does not). Confirmed by
|
||||
reading both files. Both were corrected in a scratch copy under `/tmp`, run
|
||||
with `PYTHONDONTWRITEBYTECODE=1` from a scratch cwd with the inputs symlinked
|
||||
in; nothing was written into `vegnormal-okf` and no instrument code was copied
|
||||
into this repository. The correction in both is the same: **accept a hit when
|
||||
the pair key matches OR when the concept's own normalised title equals the
|
||||
fasit's `norm`.** Every row below is reported in both forms.
|
||||
|
||||
**The two forms cannot both reach 2 761, and that is a property of the
|
||||
instrument rather than of any bundle.** The pair key requires the concept
|
||||
title to equal the fasit title *without* its numbering token; the literal form
|
||||
requires it to equal the fasit title *with* it. **2 739 of 2 761** fasit rows
|
||||
carry such a token, so for those rows the two forms demand mutually exclusive
|
||||
strings. No bundle can satisfy both. The gate below is therefore stated on the
|
||||
corrected (union) form, with the shipped form reported beside it and its
|
||||
shortfall accounted for row by row.
|
||||
|
||||
`sk2_grenser.py`'s "present in the extracted text" row reads
|
||||
`build/sk2-pagetext.json`, which is the **PDF's** page text and not the
|
||||
extraction under measurement. It was given a haystack of this repository's own
|
||||
extracted XML text instead (`SK2_HAYSTACK`), and the row is labelled with which
|
||||
haystack it used.
|
||||
|
||||
## 2. The mechanism, and why a new rule rather than a flag
|
||||
|
||||
`find_candidates` returns early when `outline_marks` is given: the text
|
||||
heuristics, both gates and Arm F's fold are all skipped, and the orphan check
|
||||
is not applied. That is why the PDF bookmark arm reaches 2 762. An STS
|
||||
`<sec><title>` is the same class of declaration — stronger, in fact, since the
|
||||
publisher states it as an element rather than as a bookmark over a typeset
|
||||
page — but until now it arrived as `rule:heading`, indistinguishable from a
|
||||
heading a converter recovered from a font size, and was judged by:
|
||||
|
||||
- **the orphan check**, which removed **710 of 2 761**. Verified as the only
|
||||
mechanism rather than assumed: 710 of 710 removed headings are followed
|
||||
immediately by another heading, and **0 of 2 051** delivered ones are. They
|
||||
are container sections. `propose.py`'s own comment already named the outcome:
|
||||
the check "judges whether a GUESS was a heading — a question a publisher's
|
||||
own tree has already answered, and one that deletes every container section
|
||||
if asked".
|
||||
- **Arm F** (`--unit-fold`, a build default since 2026-09-08), which took
|
||||
**2 066 more**, 2 089 → 23. On a process code whose sections are almost all
|
||||
`14.121 Langsgående sikring T1 oppgjort etter lengde`, the shallowest
|
||||
repeated heading level is 1, so every deeper level folds into its chapter.
|
||||
|
||||
The change is **one new rule constant reached from one row**:
|
||||
|
||||
- `extract.xml_outline(name, data)` returns the marks the XML reader wrote
|
||||
itself. There is **no bridge**: the reader appended the line it is naming, so
|
||||
the index is exact, there is no tolerance constant and there is no
|
||||
`unresolved` bucket. This is the difference from `pdf_outline`, whose naive
|
||||
nearest-line rule was wrong on 1 840 of 2 762 nodes.
|
||||
- `propose.RULE_XML_SECTION` (`rule:xml-section`) is its own name in
|
||||
`RULE_NAMES` and in `_ORPHAN_EXEMPT`, so an artifact still says whether a
|
||||
boundary came from a bridged bookmark, an element the reader transcribed, or
|
||||
a heading somebody guessed.
|
||||
- `find_candidates` gained one keyword, `outline_rule`, defaulting to
|
||||
`RULE_PDF_OUTLINE` so the PDF arm is reached by exactly the call it was
|
||||
reached by before.
|
||||
- `build_plan` chooses the route by the **row**
|
||||
(`DECLARED_STRUCTURE_IDS = {"xml"}`), never by the text. The same markdown
|
||||
arriving from a `.md` file is still a guess and still carries `rule:heading`
|
||||
— pinned by a test.
|
||||
|
||||
The broader variant — a flag exposing the orphan check to every type — was not
|
||||
taken. It was not needed and it would put twelve unit-worksheet positions at
|
||||
risk for one row's benefit.
|
||||
|
||||
`--pdf-outline` stays a flag and stays off; nothing in this round moved it.
|
||||
That remains an operator question on a reach of **1 of 8** reference PDFs.
|
||||
|
||||
## 3. The gate, in both instrument forms
|
||||
|
||||
Built with the installed console script and no arm flag at all:
|
||||
|
||||
```
|
||||
okf build <the STS folder> --bundle <scratch> --bundle-id r761xml --okf-version 0.2
|
||||
```
|
||||
|
||||
**2 761 segments proposed, 1 plan, 0 documents with no boundary, 109
|
||||
unreadable (the images), `merged + coded rejections = 110; N = 110`.**
|
||||
|
||||
Against the source's own elements — every titled `<sec>`, its `<label>` reduced
|
||||
to a directory and its `<title>` to a title, which is what `_segment_path`
|
||||
does:
|
||||
|
||||
| row | after | denominator |
|
||||
|---|---|---|
|
||||
| declared sections that became a concept (directory **and** title) | **2 761** | 2 761 |
|
||||
| declarations with no concept | **0** | 2 761 |
|
||||
| concepts matching no declaration | **0** | 2 761 |
|
||||
| concept title == source `<title>` after NORM | **2 761** | 2 761 |
|
||||
| `a)`-points (label, no title) that became a concept | **0** | 4 954 |
|
||||
| contiguous pipe-table blocks | **10** | 10 `<table-wrap>` |
|
||||
|
||||
Against the 2 761-row fasit, through the corrected instrument:
|
||||
|
||||
| row | CORRECTED | AS-SHIPPED | denominator |
|
||||
|---|---|---|---|
|
||||
| titles that became a boundary | 2 759 (99.9 %) | 2 737 (99.1 %) | 2 761 |
|
||||
| depth 1 | 28 | 12 | 28 |
|
||||
| depth 2 | 118 | 116 | 118 |
|
||||
| depth 3 | 500 | 499 | 500 |
|
||||
| depth 4 | 1 141 | 1 140 | 1 141 |
|
||||
| depth 5 | 868 | 866 | 868 |
|
||||
| depth 6 | 95 | 95 | 97 |
|
||||
| depth 7 | 9 | 9 | 9 |
|
||||
| false positives | 2 (0.1 %) | 24 (0.9 %) | 2 761 |
|
||||
| pages 68–266 | 566 (100.0 %) | 561 | 566 |
|
||||
|
||||
**The two shortfalls are both instrument artefacts and both are accounted
|
||||
for, not waved past.**
|
||||
|
||||
- The **2** rows the corrected form misses are `36.7331 48V strømforsyning
|
||||
PoE` and `36.7332 24V strømforsyning` — the only two of 2 761 whose
|
||||
`<title>` itself begins with a digit. The fasit glues `<label>` and `<title>`
|
||||
with no separator, so its own key reads `36.733148` as the number and `V
|
||||
strømforsyning PoE` as the title. Both sections **are** in the bundle, with
|
||||
the right number and the right title, which is why the same two appear as the
|
||||
corrected form's only two "false positives". Measured against the source
|
||||
rather than against the glued string, they are hits: **2 761 of 2 761**.
|
||||
- The **24** the shipped form misses are those two plus the **22** rows with no
|
||||
numbering token at all (`Forord`, `Hovedprosess 81 Løsmasser`, …). Those
|
||||
sections get no directory, so the pair key compares the per-document
|
||||
directory name against `""` and can never match. That is the structural
|
||||
proof in §1, in numbers: 22 of 22 unnumbered rows score 0 as-shipped and 22
|
||||
of 22 score under the corrected form.
|
||||
|
||||
## 4. Consumption, cross-arm, and cost
|
||||
|
||||
hit@k over the eight published questions at `k = 50`, corrected instrument
|
||||
(the as-shipped form agrees on every row here, because a concept title no
|
||||
longer carries its numbering token):
|
||||
|
||||
| | before | after |
|
||||
|---|---|---|
|
||||
| hit@1 | 0/6 | **3/6** |
|
||||
| hit@8 | 0/6 | **5/6** |
|
||||
| hit@50 | 0/6 | **6/6** |
|
||||
| known-positive rank | 1, of 17 delivered of 23 concepts | **1**, of 43 delivered of 2 761 |
|
||||
|
||||
Row by row after: S1 rank 3, S2 rank 1, S3 rank 1, S4 rank 1, S5 rank 11,
|
||||
S6 rank 3, KP rank 1, KN no rank (it has no fasit). **S5 is rank 11 on the PDF
|
||||
arm as well** — unchanged, and a ranking question rather than a segmentation
|
||||
one.
|
||||
|
||||
**Cross-arm, and the number moved a long way.** Round 13 measured 2 022 common
|
||||
concept ids — 96.8 % of the XML bundle and 73.2 % of the PDF arm's. Re-measured
|
||||
after this change: **2 761 common ids, 100 % of the XML bundle's 2 761 and
|
||||
2 761 of 2 762 (99.96 %) of the PDF arm's.** The single PDF-only concept is
|
||||
`r761-prosesskoden.md`, the front-matter node the bookmark tree carries and the
|
||||
STS body does not. The 740 that were PDF-only in round 13 were the
|
||||
orphan-check bucket; they are now on both sides.
|
||||
|
||||
**Cost, and cost figures are machine-dependent — direction and order of
|
||||
magnitude only.** `/usr/bin/time -l` reports BSD **bytes**, converted here.
|
||||
XML build 7.6 s → **17.4 s**, peak RSS **145 MiB**. PDF arm with
|
||||
`--pdf-outline`, built once: **209.2 s**, peak RSS **3 251 MiB**. The XML
|
||||
channel remains roughly an order of magnitude cheaper in time and more than an
|
||||
order of magnitude cheaper in memory, on the same machine in the same session.
|
||||
|
||||
## 5. Two-file directories: round 13's 14 were a defect, and they are gone
|
||||
|
||||
Round 13 reported 14 directories holding two concept files on the XML arm and
|
||||
classified them as the publisher reusing a title. **Measured this round, that
|
||||
classification is wrong.** Rebuilt from a frozen `9d5ebc3` export with
|
||||
`--no-unit-fold` (2 089 concepts), the 14 are pairs like `35.54 Nøkkelsafe for
|
||||
brannvesen` / `Nøkkelsafe for brannvesen`: the first is a line from the
|
||||
document's **own contents listing**, a plain `<p>` that happens to read as
|
||||
`<number> <title>` and was proposed as a boundary by the text grammar; the
|
||||
second is the real `<sec><label>35.54</label><title>…</title>`. They were false
|
||||
positives of the text route, not the publisher's doing.
|
||||
|
||||
On the shipped default after this change there are **2**, of 2 738 numbered
|
||||
directories:
|
||||
|
||||
- `11` — `ARBEIDSSTIKNING, TEKNISK KONTROLL` and `Oversikt over prosesser`
|
||||
- `12` — `Prosesser med tekster` and `RIGG, BYGNINGER OG GENERELLE
|
||||
DRIFTS-OMKOSTNINGER`
|
||||
|
||||
These are **identical to the PDF arm's 2 of 2 738**, same two directories and
|
||||
same two title pairs: the publisher reuses a section number for two distinct
|
||||
sections. **0 is not reachable here without inventing an id**, so 2 is the
|
||||
floor and it is named rather than counted.
|
||||
|
||||
The per-document root directory holds 22 concept files. That is not a
|
||||
collision: those 22 sections carry no numeric `<label>`, so no directory is
|
||||
formed, and each filename is distinct.
|
||||
|
||||
## 6. Generic XML: 0 plans and a failed build, and it is not an XML defect
|
||||
|
||||
The order to round 13 required "1 plan, exit 0, never 0" for XML that is not
|
||||
STS. Measured:
|
||||
|
||||
```
|
||||
okf build <folder with one generic-feed.xml> --bundle <scratch> ...
|
||||
okf-propose-segments: nothing to propose for generic-feed.xml — the mechanical rules found no boundary.
|
||||
okf build: proposed 0 plan(s); 1 document(s) with no boundary; 0 unreadable
|
||||
okf build: FAILED - no segmentation plans in ... -- exit 2
|
||||
```
|
||||
|
||||
**The known-positive that decides what this is:** a folder holding one `.txt`
|
||||
with prose and no headings gives **exactly the same three lines and exit 2**.
|
||||
This is general `okf build` behaviour for any structureless document, not an
|
||||
`.xml` defect. Extraction works — 0 unreadable, the text is there — and the
|
||||
proposer has nothing to propose. Round 13's own test asserts
|
||||
`propose.find_candidates(text) == []`, which is the opposite of what its order
|
||||
demanded, and the test is right; the divergence was simply never stated, and it
|
||||
is stated here and in the CHANGELOG.
|
||||
|
||||
**Should such a folder fail the build at all?** The gate exists because a run
|
||||
that replays zero plans would emit a flat bundle and call it a success. That is
|
||||
a good reason and it is not being removed. The question is whether "0 plans,
|
||||
N documents with no boundary, **0 unreadable**" is the same state as "0 plans
|
||||
because nothing could be read". The deciding number:
|
||||
|
||||
| reference corpus | documents | plans | no boundary | unreadable | outcome changes if split? |
|
||||
|---|---|---|---|---|---|
|
||||
| K2 `trinn1` | 43 | > 0 | some | 4 | no |
|
||||
| the five-document folder | 5 | 4 | 1 | 0 | no |
|
||||
| the STS delivery | 110 | 1 | 0 | 109 | no |
|
||||
| the 828-file HTML export | 828 | 828 (recorded, not re-measured here) | — | — | no |
|
||||
|
||||
**0 of 4.** The distinction only fires on a corpus where *every* document
|
||||
yields no boundary, and none of the four is such a corpus. Separating the two
|
||||
states would therefore change no outcome anyone has measured, and it is left
|
||||
unchanged — as a measured result with its denominator, not as an omission. The
|
||||
message a caller sees already distinguishes them by the counts it prints on the
|
||||
same line.
|
||||
|
||||
## 7. Nothing else moved, and it was measured
|
||||
|
||||
- **The five-document folder is byte-identical.** `diff -r` between a tree
|
||||
built from a frozen `9d5ebc3` export and one built from the same export with
|
||||
only `src/` and `tests/` replaced by this round's code: **0 differences**.
|
||||
52 md files, 26 concepts, 0 of 5 rejected. That folder holds **0 `.xml`
|
||||
files**, so it is a real known-negative.
|
||||
- **`okf project` is still byte-equal to `okf build`** on that folder;
|
||||
`diff -r` empty.
|
||||
- **The pinned K2 bundle is unchanged**: `tests/test_default_bundle_pin.py`
|
||||
7 passed, 453 concepts, per-row ranks `(1, 1, 1, 1, 1, 5)`, and the
|
||||
`--no-tie-shared-rank` known-negative still reproduces the fall it names.
|
||||
- **The 43-document reference corpus is byte-identical.** Rebuilt in full with
|
||||
this round's code and compared against the pinned
|
||||
`K2-bundle-default-20260912`, which was built before the change:
|
||||
**`diff -r` = 0 differences**, 865 md files on both sides, same rejection
|
||||
codes (`extractor_empty_pdf` 1 of 43, `extractor_unknown` 3 of 43). The
|
||||
corpus holds **0 `.xml` files of 86** (66 pdf, 10 docx, 4 xlsx, 2 zip, 2 smc,
|
||||
2 doc), so the unit worksheet's 12 positions could not be reached by this
|
||||
change — and now that is measured on the bytes rather than argued from the
|
||||
code path. Build wall time 874 s, 20.3 s per file.
|
||||
- **The PDF arm is unchanged**: built once with `--pdf-outline`, **2 762
|
||||
segments**, exactly as round 12 and round 13 measured it.
|
||||
- Suite after: **1 575 passed, 1 skipped** (1 567 + 8 new). `ruff check src
|
||||
tests tools` and `ruff format --check .` clean over 117 files, `mypy src`
|
||||
clean over 21 files.
|
||||
|
||||
## 8. The instrument correction is reported, not assumed
|
||||
|
||||
Round 13 corrected `hitk_sk2.py` in its scratch copy with the same fix as
|
||||
`sk2_grenser.py`, but its report only said the fasit set was validated
|
||||
"independent of key form". Verified this round: with the **shipped** matcher
|
||||
the XML bundle scores `rang: null` on every question — including the
|
||||
known-positive whose `topp_1` **is** the fasit — and 0/6 · 0/6 · 0/6; with the
|
||||
correction it scores 3/6 · 5/6 · 6/6. **A reader who runs the shipped runner
|
||||
gets zeros and concludes round 13 was wrong.** The two instruments corrected in
|
||||
scratch are `experiments/sk2_grenser.py` and `experiments/hitk_sk2.py`, and the
|
||||
correction in both is: accept a hit when the pair key `(directory, normalised
|
||||
title)` matches **or** when the concept's own normalised title equals the
|
||||
fasit's `norm`. This is sent to `vegnormal-okf` as a proposal for their
|
||||
instrument, not applied there.
|
||||
|
||||
## Honesty limits
|
||||
|
||||
- **The denominator for `.xml` is ONE file, from ONE publisher, in ONE
|
||||
schema.** Closing the build's ceiling does not change that. `.xml` as a file
|
||||
type is far wider than NISO-STS; a document in any other schema keeps its
|
||||
text in document order and gets no structure at all. The `_EVIDENCE` class
|
||||
for the row stays `measured` — the file is a publisher's own delivery,
|
||||
written for their purposes long before any lookup of ours, and the fasit is
|
||||
theirs — and the row now carries that limit in its own comment.
|
||||
- **The ceiling here is structural, not recovered.** 2 761 of 2 761 on this arm
|
||||
is not comparable to a similar percentage on the PDF arm, which had to bridge
|
||||
from (page, `/XYZ` top) to a line index and reports what did not bridge. This
|
||||
arm has nothing to bridge and nothing to lose in bridging; the number is
|
||||
cheaper than it looks.
|
||||
- **The denominator 2 761 is a CHOICE.** The document carries 7 715 `<sec>`, of
|
||||
which 4 954 are lettered points with a `<label>` and no `<title>`. Counting
|
||||
those as sections would give a different fraction of a different question.
|
||||
- **The generic fallback is measured on fixtures, not on a corpus.** The `.txt`
|
||||
known-positive is one hand-written file; the four-corpus table in §6 counts
|
||||
outcomes, not documents that would land differently.
|
||||
- **The unit worksheet is n = 12 positions and contains no `xml` row.** Its
|
||||
`pdf` 7 of 8 and `docx` 3 of 3 stand here by byte identity on the default
|
||||
path, never by a re-score.
|
||||
- **Cost figures are machine-dependent.** Nothing above should be compared
|
||||
against a number from another machine except by direction and order of
|
||||
magnitude.
|
||||
- **"Delivered" is not "answered".** hit@50 = 6/6 says the gold document
|
||||
reached the payload at k = 50; it says nothing about whether a reader would
|
||||
answer the question from it. hit@8 is 5/6 and hit@1 is 3/6.
|
||||
- **hit@8 is not neutral with respect to a rule that changes how many distinct
|
||||
documents a payload holds.** This round changed segmentation inside one
|
||||
document, so that particular caveat does not bite here, but the metric is the
|
||||
same one and the caveat travels with it.
|
||||
- The report for round 13 is dated `2026-09-11` in its filename while its
|
||||
commit is dated 2026-09-10. This file uses the real date.
|
||||
Loading…
Add table
Add a link
Reference in a new issue