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:
parent
ea8590db82
commit
81d7194e20
33 changed files with 412 additions and 398 deletions
124
CHANGELOG.md
124
CHANGELOG.md
|
|
@ -210,7 +210,7 @@ retrieval gate is red on rows 5, 7 and 8, and speed was not measured.
|
|||
rebuilt while the server runs is seen by the next call without a restart, a
|
||||
configuration edit or a code change — measured, 9 of 9 discovery checks over
|
||||
three bundles written while the process was serving. The cost is paid per
|
||||
call: 0.75 s for the identity of a 2 756-concept bundle, 5.6 s for one ask.
|
||||
call: 0.75 s for the identity of a one-document bundle of a few thousand concepts, 5.6 s for one ask.
|
||||
- **Containment is two independent checks**: the bundle's own index must name
|
||||
the concept, and the resolved path must be inside the bundle. Removing
|
||||
either one alone still refuses — with a different code, which the gate
|
||||
|
|
@ -279,9 +279,9 @@ retrieval gate is red on rows 5, 7 and 8, and speed was not measured.
|
|||
COUNTED** (operator decision 2026-09-18). `llm-ingestion-guard` 1.4.0 keeps
|
||||
the soft hyphen in `_ZERO_WIDTH_CPS`, and `output:zero-width-present` is an
|
||||
any-tier carrier — `fail_secure` at every trust level, no sanitisation, no
|
||||
exception. A 701-page reference standard carries 71 of them and 0 of the four real
|
||||
exception. A long reference standard carries 71 of them and 0 of the four real
|
||||
zero-width characters; all 71 are Norwegian hyphenation points inside words,
|
||||
so a 701-page reference standard was unreadable for the whole chain over
|
||||
so that reference standard was unreadable for the whole chain over
|
||||
typography. `extract.normalise_extracted` removes that one character from
|
||||
every extracted text; `ExtractedDocument.soft_hyphens`,
|
||||
`InboxResult.normalised` and the accounting's `normalised_soft_hyphen` carry
|
||||
|
|
@ -309,7 +309,7 @@ retrieval gate is red on rows 5, 7 and 8, and speed was not measured.
|
|||
- **The judge is `tools/okf_accounting_gate.py`**, written red first
|
||||
against an independent witness (`tools/okf_witness.py`, which imports
|
||||
nothing from this package). At this change it is green on all six rows,
|
||||
including a 701-page reference standard: 110 of 110 units under both the default
|
||||
including a long reference standard: 110 of 110 units under both the default
|
||||
gate and `--gate none`.
|
||||
- **Opt-in, measured.** On the 43-document reference corpus the build took
|
||||
+744 s (+19 %) and +0.53 GB peak RSS.
|
||||
|
|
@ -339,7 +339,7 @@ retrieval gate is red on rows 5, 7 and 8, and speed was not measured.
|
|||
- **`okf build` exits 1 when it extracted at least one document and
|
||||
persisted none.** Until now such a run exited 0, because every refusal was
|
||||
coded and the conservation identity held. The bundle was nonetheless empty.
|
||||
Measured case: guard 1.4.0 refuses a 701-page reference standard whole, because of
|
||||
Measured case: guard 1.4.0 refuses a long reference standard whole, because of
|
||||
its 71 soft hyphens (U+00AD). Door B's library function
|
||||
(`process_inbox`) and `corpus.measure` are unchanged; for a hostile inbox,
|
||||
"all rejected" is a correct outcome.
|
||||
|
|
@ -806,9 +806,9 @@ retrieval gate is red on rows 5, 7 and 8, and speed was not measured.
|
|||
a PDF was opened for its text alone, the converter's markdown writer dropped
|
||||
every picture, and the only writer into a bundle took `content: str`. The two
|
||||
lossiness warnings said so on every run, which made the loss honest and did
|
||||
not make it smaller. Measured on a 701-page reference standard: the section text is
|
||||
not make it smaller. Measured on a long reference standard: the section text is
|
||||
carried in full while 12 `Tabell N-N` and 9 `Figur N-N` captions stand over
|
||||
nothing, so a section's "is given in table 84-2" points
|
||||
nothing, so a section's "is given in table N-N" points
|
||||
at empty space.
|
||||
**Five readers place, one module decides.** `pdf` (embedded image XObjects),
|
||||
`docx`/`pptx`/`odt`/`rtf` (the converter's media, through `--extract-media`),
|
||||
|
|
@ -929,19 +929,19 @@ retrieval gate is red on rows 5, 7 and 8, and speed was not measured.
|
|||
the reason, never a quiet `UNMEASURED`.
|
||||
**The normalisation was measured before the metric was built**: stripping all
|
||||
whitespace and lowercasing reproduces the fasit's own `norm` from its own
|
||||
`title` on **2 761 of 2 761** rows (keeping only alphanumerics scores 58).
|
||||
`title` on **N of N** rows, N being its declared sections (keeping only alphanumerics scores 58).
|
||||
**A boundary is recovered in either of two forms**, and both are load-bearing:
|
||||
a concept whose normalised title equals `norm`, or the pair of the concept's
|
||||
own directory and its residual title -- because the numbering token a
|
||||
publisher glues into a heading lands in the concept id on one route and in the
|
||||
title on another. Measured on the known-good arm, the literal form alone
|
||||
reaches **22 of 2 761** where the two together reach **2 759**; on another
|
||||
build of the same product the split is the exact opposite (2 727 literal, 0
|
||||
paired). One bar, at the value measured on the pinned artifact: **2 759/2 761**,
|
||||
`corpora = 1`. It separates the known-bad arm at **1 148 of 2 761 (41.6 %)**,
|
||||
reaches **22 of N** where the two together reach **N − 2**; on another
|
||||
build of the same product the split is the exact opposite (N − 34 literal, 0
|
||||
paired). One bar, at the value measured on the pinned artifact: **N − 2 of N**,
|
||||
`corpora = 1`. It separates the known-bad arm at **1 148 of N (41.6 %)**,
|
||||
which is now a `FAIL` and exit 1 instead of exit 3. **`--fasit` is an
|
||||
assertion**, like `okf consume --ref`: a bundle of another product scores 0 of
|
||||
2 761 (measured on two of them) and reads `FAIL` -- the assertion being wrong,
|
||||
N (measured on two of them) and reads `FAIL` -- the assertion being wrong,
|
||||
not the bundle. The bar rests on **one product**, and the run says so on every
|
||||
boundary row. `docs/2026-09-12-g37-terskler.md` SS 7 carries the premises
|
||||
re-measured, the seven bundles, the interval any bar could sit in, and the two
|
||||
|
|
@ -969,8 +969,8 @@ retrieval gate is red on rows 5, 7 and 8, and speed was not measured.
|
|||
today, both `structure_null_share` (the share of a type's documents that
|
||||
yielded exactly one concept), read off the pinned 43-document reference
|
||||
bundle: `.pdf` 8/32 and `.docx` 2/5, plus one definitional bar that applies to
|
||||
every type (0 concepts with an empty body, measured 0 of 8 602 over four
|
||||
bundles). A bar needs at least five documents on BOTH sides -- its own and the
|
||||
every type (0 concepts with an empty body, measured over every concept of
|
||||
four bundles). A bar needs at least five documents on BOTH sides -- its own and the
|
||||
judged bundle's -- so `.xlsx` (2), `.xml` (1) and every type with no corpus
|
||||
class in `extract._EVIDENCE` are `UNMEASURED` and print their numbers without
|
||||
a verdict. The gate reads the index tree, never a directory (SS 9.2), and
|
||||
|
|
@ -1015,7 +1015,7 @@ retrieval gate is red on rows 5, 7 and 8, and speed was not measured.
|
|||
it is the instrument that separates a ranking movement from a budget
|
||||
displacement on ONE bundle, and the measurement it was built for is
|
||||
`docs/2026-09-12-k3-runde23-stien-i-kroppssignalet.md`. Measured on the reference standard
|
||||
(2 761 concepts, 710 heading-only sections, 675 carrying the line): of the
|
||||
(N concepts, one per declared section; 710 heading-only, 675 carrying the line): of the
|
||||
**39** newly delivered concepts the line ever added a question token to,
|
||||
**39** gained it from the bundle-absolute PATH and **0** from the link's
|
||||
title, and every token it ever contributed is a segment of the document's
|
||||
|
|
@ -1065,8 +1065,8 @@ retrieval gate is red on rows 5, 7 and 8, and speed was not measured.
|
|||
fix and are struck. Reason 2 STANDS and now carries the rule alone,
|
||||
re-measured by reading `portfolio-optimiser` at `6eb58e5`: `read_provenance`
|
||||
returns `UnreadableProvenance(reason="block-sequence")`. It is not the
|
||||
guard's objection -- guard 1.4.0 reads the block form on 4 609 of 4 609
|
||||
files. The README said this library "cannot read the block form" where one
|
||||
guard's objection -- guard 1.4.0 reads the block form on every one of
|
||||
those files. The README said this library "cannot read the block form" where one
|
||||
reader could and one could not; it now separates the two by KEY.
|
||||
|
||||
- **`okf.parse_frontmatter` returns a flow string for a block `sources:`
|
||||
|
|
@ -1077,8 +1077,8 @@ retrieval gate is red on rows 5, 7 and 8, and speed was not measured.
|
|||
previously got something empty that parsed -- a regression for them, and the
|
||||
reason it is stated here rather than left inside the fix. Measured
|
||||
2026-09-12 over the same four bundles, denominator = concept files carrying
|
||||
a block `sources:` (2 756 + 446 + 1 133 + 270): PyYAML 6.0.3 reads the
|
||||
returned string back on **0 of 4 605** of them, because the `?` opening a
|
||||
a block `sources:` (four bundles): PyYAML 6.0.3 reads the
|
||||
returned string back on **0** of them, because the `?` opening a
|
||||
query string in the viewer URL ends the flow scalar. The string is a READING
|
||||
projection of a value this library does not write in that form; the emitter
|
||||
`materialize._render_sources` still writes flow, so no bundle bytes move.
|
||||
|
|
@ -1091,11 +1091,11 @@ retrieval gate is red on rows 5, 7 and 8, and speed was not measured.
|
|||
only the flow one and returned the key with an EMPTY value for the block
|
||||
form -- not a `KeyError` a consumer can catch, an address that disappears.
|
||||
Measured 2026-09-12 over four bundles a producer ships, denominator = files
|
||||
carrying a frontmatter block: 2 756 of 2 757, 446 of 447, 1 133 of 1 134 and
|
||||
270 of 271 concept files lost it, while PyYAML 6.0.3 and the pinned guard
|
||||
carrying a frontmatter block: all but one of those files in each
|
||||
of the four bundles lost it, while PyYAML 6.0.3 and the pinned guard
|
||||
1.4.0 both read it on 100 % of the same files. After: **0 of each**, and all
|
||||
three flat readers return what BOTH reference readers return on 4 609 of
|
||||
4 609 files, plus 2 762 of 2 762 in a flow-form bundle that is unchanged.
|
||||
three flat readers return what BOTH reference readers return on every one of
|
||||
those files, plus every file in a flow-form bundle that is unchanged.
|
||||
`materialize.parse_frontmatter` is public API, so the external consumer is
|
||||
the one this repairs.
|
||||
|
||||
|
|
@ -1191,7 +1191,7 @@ retrieval gate is red on rows 5, 7 and 8, and speed was not measured.
|
|||
`source_reference_unquotable`, `run_frontmatter_invalid`).
|
||||
- **Behaviour change:** `okf build --frontmatter 'sources=[{ resource: <URL
|
||||
with a query string>, … }]'` now exits 2 and writes nothing. K3-19's own
|
||||
flagged reference-standard build used such a URL and wrote 2 761 of 2 761 frontmatters
|
||||
flagged reference-standard build used such a URL and wrote every one of its frontmatters
|
||||
PyYAML refuses.
|
||||
- **Readers** (`parse_frontmatter`, the index and structure readers, both
|
||||
`read_sources` branches) unquote a `"`-wrapped value; `'`-wrapped values
|
||||
|
|
@ -1209,7 +1209,7 @@ retrieval gate is red on rows 5, 7 and 8, and speed was not measured.
|
|||
ancestor that holds text: the nearest preceding plan entry at a smaller
|
||||
level, passing over an ancestor that is empty too. Nothing is copied and no
|
||||
boundary moves. It reads the plan's level and order, never the row. Measured
|
||||
on one reference standard, 710 of 2 761 concepts are heading-only; the route names
|
||||
on one reference standard, 710 of its N concepts are heading-only; the route names
|
||||
the ancestor the document's own nesting names on 708 of them (two sit at
|
||||
depth 7, which a markdown heading clips to 6), where reading section numbers
|
||||
gets 686.
|
||||
|
|
@ -1229,7 +1229,7 @@ retrieval gate is red on rows 5, 7 and 8, and speed was not measured.
|
|||
directory after its own number, so a question naming the document matched
|
||||
every concept -- except the one whose title already named it, which gained
|
||||
nothing because the overlap counts a question token once. Measured on a
|
||||
2 761-concept bundle, the known-positive fell from rank 1 to not delivered
|
||||
one-document bundle of a few thousand concepts, the known-positive fell from rank 1 to not delivered
|
||||
at the default `k` (13 at `k` = 50). `consume.shared_id_prefix` now keeps
|
||||
the leading directories EVERY id shares out of that signal: the
|
||||
known-positive is rank 1 at both `k` and S1-S6 stay 6/6.
|
||||
|
|
@ -1285,20 +1285,20 @@ retrieval gate is red on rows 5, 7 and 8, and speed was not measured.
|
|||
keyword-only `concept_frontmatter_values` on `measure`, `process_inbox` and
|
||||
`render_inbox_concept`. Without the flag nothing moves. **Note:** a
|
||||
`sources` value carrying a URL or `X:Y` in the flow mapping passes `okf
|
||||
check` and is refused by PyYAML's `safe_load` -- measured on 2 761 of 2 761
|
||||
check` and is refused by PyYAML's `safe_load` -- measured on every one of the
|
||||
concepts with such a value -- and it is written verbatim as stated.
|
||||
- **`description` for an STS section, from its own first spec point.** The
|
||||
first `<p>` of the first direct-child `<sec sec-type="spec">`, whole,
|
||||
carried by the plan entry, screened by the gate, and written only where a
|
||||
YAML reader reads it verbatim (`inbox._yaml_plain`; over 2 024 measured
|
||||
values the rule and PyYAML agree on every one). The spec sets no length, so
|
||||
the one-paragraph limit is ours. On the measured document: 2 026 of 2 761
|
||||
the one-paragraph limit is ours. On the measured document: 2 026 of its N
|
||||
titled sections carry a spec point, 1 807 descriptions are written (2 points
|
||||
have no `<p>`, 217 carry `": "`), none is invented, and none is derived from
|
||||
a title.
|
||||
|
||||
`--ingested-at` alone was confirmed to stamp every concept, on the segmented
|
||||
route too, and to date `log.md`: 2 761 of 2 761.
|
||||
route too, and to date `log.md`: all N concepts of the measured document.
|
||||
|
||||
## [0.8.2] — 2026-09-11
|
||||
|
||||
|
|
@ -1381,7 +1381,7 @@ route too, and to date `log.md`: 2 761 of 2 761.
|
|||
concept now RISES through the fusion's order and stops beneath the first
|
||||
concept whose title answers more question tokens, by equality, than it
|
||||
holds. Same flag, no new parameter, no new constant.
|
||||
- **What the rule was built for does not move.** On the 2 761-concept bundle
|
||||
- **What the rule was built for does not move.** On the one-document bundle
|
||||
of one standard no covered concept had such a title above it, so all 8
|
||||
payloads are byte-identical to 0.8.1's at default `k` AND at `--k 50`;
|
||||
hit@1/8/50 stays 6/6 · 6/6 · 6/6 with the known-positive at rank 1.
|
||||
|
|
@ -1410,7 +1410,7 @@ route too, and to date `log.md`: 2 761 of 2 761.
|
|||
|
||||
- **`--title-covered` (ON by default, opt out with `--no-title-covered`): a
|
||||
question that accounts for a concept's WHOLE title reads that concept first.**
|
||||
On the 2 761-concept bundle of one standard, the answering section was
|
||||
On the one-document bundle of one standard, the answering section was
|
||||
delivered at rank 1 on **3 of 6** scored questions and **none of the reading
|
||||
side's six flags moved that number** -- the whole sweep sits at 3/6 or worse.
|
||||
Measured on that bundle, before and after: hit@1/8/50 **3/6 - 5/6 - 5/6 ->
|
||||
|
|
@ -1429,7 +1429,7 @@ route too, and to date `log.md`: 2 761 of 2 761.
|
|||
- **A PARTITION, never a fourth RRF signal, and the arithmetic is why.** RRF
|
||||
consumes ranks alone, so with shared ranks a rule whose positive group has
|
||||
`m` members is worth `1/61 - 1/(61 + m)` -- a rule firing on ONE concept of
|
||||
2 761 is worth 0.00026 against a body gap of 0.0029. **A precise rule is
|
||||
N is worth 0.00026 against a body gap of 0.0029. **A precise rule is
|
||||
worth LEAST under this fusion.** Measured as a signal it moves hit@1 not at
|
||||
all (3/6, both as a third and as a fourth signal); as a partition it reaches
|
||||
6/6. `lookup_hits` is the same shape for the same measured reason, and it
|
||||
|
|
@ -1447,9 +1447,9 @@ route too, and to date `log.md`: 2 761 of 2 761.
|
|||
- **NOTHING ELSE MOVES AND IT IS MEASURED ON THE BYTES.** The pinned K2 bundle
|
||||
keeps `(1,1,1,1,1,5)` and its 7 pin tests, Arm B keeps `(1,1,1,1,1,5)`, and
|
||||
the payloads on both are **byte-identical on 6 of 6 questions**; the three
|
||||
downstream bundles' payloads are byte-identical on 5 questions each; the 828-file HTML
|
||||
corpus still gives 828 plans, 0 unreadable and 6 015 md with `diff -rq`
|
||||
empty; the five-document folder is `diff -r`-identical at 26 concepts / 52
|
||||
downstream bundles' payloads are byte-identical on 5 questions each; the HTML
|
||||
corpus still gives a plan for every one of its M files, 0 unreadable and
|
||||
the same md count with `diff -rq` empty; the five-document folder is `diff -r`-identical at 26 concepts / 52
|
||||
md; `okf project` stays byte-equal to `okf build`. hit@k on those three bundles
|
||||
is **NOT MEASURED** -- this repository holds no gold set for them, which is
|
||||
0 gold sets and not 0 hits.
|
||||
|
|
@ -1480,16 +1480,16 @@ route too, and to date `log.md`: 2 761 of 2 761.
|
|||
|
||||
- **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
|
||||
reader reached its ceiling -- all N declared heading lines -- while the
|
||||
build delivered **23 concepts and 15 of N 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
|
||||
**710 of N** (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.
|
||||
`pdf_outline`, whose naive nearest-line rule was wrong on 1 840 of N + 1.
|
||||
- `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
|
||||
|
|
@ -1499,15 +1499,15 @@ route too, and to date `log.md`: 2 761 of 2 761.
|
|||
**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
|
||||
K2 bundle is unchanged, and the PDF arm still proposes N + 1 segments.
|
||||
- Measured at SHIPPED DEFAULTS, not behind a flag: **N concepts**;
|
||||
**N of N** 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**
|
||||
**0 of the label-only sections**; 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
|
||||
**N shared concept ids** -- 100 % of the XML bundle and all but one
|
||||
of the PDF arm's, up from round 13's 2 022.
|
||||
- Two directories of 2 738 still hold two concepts (`11`, `12`): the
|
||||
- Two directories of N − 23 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
|
||||
|
|
@ -1517,20 +1517,20 @@ route too, and to date `log.md`: 2 761 of 2 761.
|
|||
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
|
||||
the conservation identity `merged + coded rejections == N` was never written
|
||||
because the run aborted earlier. The one xml file is the whole product: 7 715
|
||||
`<sec>`, **2 761 with a `<title>`**, 4 954 lettered points, 10
|
||||
because the run aborted earlier. The one xml file is the whole product: several
|
||||
thousand `<sec>`, **N (36 %) with a `<title>`**, most (64 %) lettered points, 10
|
||||
`<table-wrap>`, and a `<sec>`-nesting depth distribution row-for-row
|
||||
identical to the publisher's own structure fasit.
|
||||
- The output grammar is MARKDOWN, the same the office and HTML rows reach the
|
||||
proposer through: `propose.py` is untouched. `<label>` + `<title>` become
|
||||
one ATX line at the section's own depth; a `<sec>` with only a `<label>` is
|
||||
a body line and never a heading (**0 of 4 954** became concepts);
|
||||
a body line and never a heading (**none** became concepts);
|
||||
`<table-wrap>` becomes one markdown table (**10 of 10**, against 0 of 10 on
|
||||
the PDF path).
|
||||
- The reader emits **2 761 of 2 761** heading lines and preserves text
|
||||
- The reader emits **N of N** heading lines and preserves text
|
||||
exactly -- 1 283 395 of 1 283 395 non-whitespace characters, ratio
|
||||
**1.000000**. The BUILD reaches 2 065 of 2 761 with `--no-unit-fold` and 15
|
||||
of 2 761 on the shipped defaults; the whole distance is two proposer rules,
|
||||
**1.000000**. The BUILD reaches 2 065 of N with `--no-unit-fold` and 15
|
||||
of N on the shipped defaults; the whole distance is two proposer rules,
|
||||
decomposed with denominators in the report.
|
||||
- hit@k over six questions, k=50: **3/6 · 5/6 · 6/6**, matching the PDF arm
|
||||
row for row, with the known-positive moving from **rank 13 to rank 1**.
|
||||
|
|
@ -1558,25 +1558,25 @@ route too, and to date `log.md`: 2 761 of 2 761.
|
|||
- **A PDF bookmark sharing a line with another left no trace.** `pdf_outline`
|
||||
collected marks in a dict keyed on the destination line index, so a second
|
||||
bookmark on a line was discarded by `setdefault` in silence: measured on a
|
||||
701-page document, **2 763 nodes in, 2 762 marks out, `unresolved` = 0**.
|
||||
long reference document, **N + 2 nodes in, N + 1 marks out, `unresolved` = 0**.
|
||||
`PdfOutline` now carries `collided`, and the identity `nodes in == marks +
|
||||
unresolved + collided` holds. Keeping both nodes was measured and felled --
|
||||
the two candidates then open at one offset and the first closes with an empty
|
||||
span the orphan check deletes.
|
||||
|
||||
- **`--pdf-outline` (OFF): cut a PDF at the boundaries its own `/Outlines`
|
||||
bookmark tree declares.** Measured outside this repository on one 701-page
|
||||
bookmark tree declares.** Measured outside this repository on one long
|
||||
reference standard whose publisher also ships a NISO-STS structure for it: the
|
||||
shipped default recovers **1 967 of 2 761** titled sections, **0 of its 28**
|
||||
shipped default recovers **1 967 of its N** titled sections, **0 of its 28**
|
||||
chapters, and **794 of 794** misses have their heading text present in the
|
||||
extracted text -- the line was read, the boundary was never opened. The same
|
||||
file carries a 2 763-node bookmark tree that matches **2 761 of 2 761** STS
|
||||
file carries a bookmark tree of N + 2 nodes that matches **N of N** STS
|
||||
titles exactly after `re.sub(r"\s+","",s).lower()`. With the arm on:
|
||||
**2 759 of 2 761 boundaries (99.9 %)**, depth 1 **28 of 28**, concept titles
|
||||
identical to the publisher's own after that normalisation **2 761 of 2 761**,
|
||||
false positives **3 of 2 762** (was 163 of 2 182), directories carrying two
|
||||
**N − 2 of N boundaries (99.9 %)**, depth 1 **28 of 28**, concept titles
|
||||
identical to the publisher's own after that normalisation **N of N**,
|
||||
false positives **3 of N + 1** (was 163 of 2 182), directories carrying two
|
||||
concept files **2** (was 132, of which 65 were a contents copy and a body
|
||||
section under one id), front-matter concepts **2 of 2 762** (was 72). Seven
|
||||
section under one id), front-matter concepts **2 of N + 1** (was 72). Seven
|
||||
of seven consumption fasit now exist in the bundle (was four); hit@1/8/50 is
|
||||
**3/6 · 5/6 · 6/6** against **1/6 · 2/6 · 4/6**.
|
||||
- It is a SEGMENTATION arm, not a reader option: the extracted text is byte
|
||||
|
|
@ -1589,10 +1589,10 @@ route too, and to date `log.md`: 2 761 of 2 761.
|
|||
- No new dependency and no second parse of the file's pages: the tree is read
|
||||
through `pdfminer.six`'s `PDFDocument.get_outlines()`, which
|
||||
`pdfplumber` already ships under the existing `[extract]` extra. Cost on
|
||||
the 701-page document: 119.22 s -> 183.31 s wall, peak RSS 3 252 -> 3 251
|
||||
the whole document: 119.22 s -> 183.31 s wall, peak RSS 3 252 -> 3 251
|
||||
MiB.
|
||||
- An unresolvable `/Dest` is dropped and COUNTED, never fabricated into a
|
||||
boundary and never a refusal of the file. That document has 0 of 2 763;
|
||||
boundary and never a refusal of the file. That document has 0 of N + 2;
|
||||
one of the eight reference PDFs in this repository's own sample has 2 of 2.
|
||||
- **The default does not move in this release.** Reach measured: 1 of the 8
|
||||
reference PDFs carries a usable tree at all.
|
||||
|
|
|
|||
125
CLAUDE.md
125
CLAUDE.md
|
|
@ -27,7 +27,7 @@ one boundary rule:
|
|||
`" ".join("".join(parts).split())`, and `str.split()` with no argument splits
|
||||
on newlines too, so extraction of ANY HTML file returned unconditionally ONE
|
||||
line while every boundary grammar in `propose` is line-anchored -- measured
|
||||
outside this repo, **828 of 828** real sections gave 0 plans and exit 2 at
|
||||
outside this repo, **M of M** real sections gave 0 plans and exit 2 at
|
||||
every sample point, and a coarser 145-document cut gave 145 of 145. Block tags
|
||||
now open their own lines and `h1`-`h6` carry the ATX marker for their OWN level
|
||||
(a flat `#` would hand `_ATX` three top-level boundaries where the document
|
||||
|
|
@ -35,10 +35,10 @@ one boundary rule:
|
|||
same the office rows reach the proposer through, so **no HTML-only heading
|
||||
grammar exists**; the fix is in the extractor and **never** the converter,
|
||||
because `.html` stays out of `_PANDOC_FORMATS` on CVE-2025-51591. After:
|
||||
**828 of 828 plans, exit 0, 3206 concepts / 6015 md -- the markdown path's
|
||||
count EXACTLY**, and the same at 414 (1651) and 83 (343). Text preservation is
|
||||
**M of M plans, exit 0, the markdown path's concept and file
|
||||
count EXACTLY**, and the same at half and at a tenth of the corpus. Text preservation is
|
||||
an EXACT invariant and not a percentage: strip the added ATX markers and the
|
||||
non-whitespace sequence is identical to the old extractor's, **828 of 828
|
||||
non-whitespace sequence is identical to the old extractor's, **M of M
|
||||
files**, character ratio **1.000000**. `_SKIP_TAGS` stays `{script, style}`.
|
||||
Exposure elsewhere measured rather than argued: **0 of 86** K2 corpus files and
|
||||
**0 of 5** smoke-folder files are HTML, and the smoke bundle is byte-identical
|
||||
|
|
@ -46,16 +46,16 @@ one boundary rule:
|
|||
`measured` since 2026-09-10, the row with the most measurement behind it and
|
||||
no entry in the table at all -- with the limit
|
||||
that travels with it -- one product, one format, one publisher, and a
|
||||
generator's cut, not 828 documents anyone wrote.
|
||||
generator's cut, not M documents anyone wrote.
|
||||
**`.xml` became a CORE type 2026-09-11** and it is the first row whose
|
||||
ceiling is structural rather than recovered. A NISO-STS zip from a
|
||||
publisher's own viewer was **110 of 110 unreadable, 0 plans, exit 2** --
|
||||
`.xml` was in neither registry -- and the one xml file in it IS the whole
|
||||
product: a large regulatory reference document, the one round 12 met as a
|
||||
701-page PDF, carrying 7 715 `<sec>`, **2 761 with a `<title>`**, 4 954 with a
|
||||
`<label>` and no title, 10 `<table-wrap>`, root `<standard>`, 0 `<!DOCTYPE`.
|
||||
Its `<sec>`-nesting depths over the titled sections are
|
||||
28/118/500/1141/868/97/9, row for row the fasit's own. The reader is stdlib
|
||||
long PDF, carrying several thousand `<sec>`, **N (36 %) with a `<title>`**, most
|
||||
(64 %) with a `<label>` and no title, 10 `<table-wrap>`, root `<standard>`,
|
||||
0 `<!DOCTYPE`. Its `<sec>`-nesting depths over the titled sections are,
|
||||
row for row, the fasit's own. The reader is stdlib
|
||||
(`xml.etree.ElementTree`) and adds NO dependency -- `defusedxml` and `lxml`
|
||||
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.
|
||||
|
|
@ -63,17 +63,17 @@ one boundary rule:
|
|||
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
|
||||
heading each buries the document's own 2 761), and `<table-wrap>` becomes its
|
||||
label in front (never a heading -- 64 % are lettered points and one
|
||||
heading each buries the document's own N), and `<table-wrap>` becomes its
|
||||
label plus one markdown table through `render_table`. **`<label>` carries the
|
||||
number and `<title>` carries the text** -- 2 of 2 761 titles begin with a
|
||||
digit -- so emitting `<title>` alone scores 0 of 2 761 with nothing in the
|
||||
number and `<title>` carries the text** -- 2 of N titles begin with a
|
||||
digit -- so emitting `<title>` alone scores 0 of N with nothing in the
|
||||
code looking wrong. Inline is an ALLOWLIST and block is the default, the
|
||||
inverse of the HTML reader, because block-versus-inline is a property of HTML
|
||||
and XML has no such universal; the allowlist is load-bearing at 1 701
|
||||
`<italic>` and 1 396 `<bold>` inside that document's prose. The ATX ceiling is
|
||||
6 and STS nesting reaches 7, so the depth is CLIPPED and not dropped: 9 of
|
||||
2 761 sit at depth 7 and `#######` matches nothing. Since K3-21 the clip is
|
||||
N sit at depth 7 and `#######` matches nothing. Since K3-21 the clip is
|
||||
the HEADING's alone: the `OutlineMark` beside it carries the declared depth,
|
||||
so the plan reads those 9 at 7. A `<!DOCTYPE` is REFUSED
|
||||
unparsed with its own code, a guarantee about the code rather than about the
|
||||
|
|
@ -91,33 +91,33 @@ one boundary rule:
|
|||
"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
|
||||
ROUND 14 CLOSED IT AT THE SHIPPED DEFAULTS.** The reader emitted N of
|
||||
N heading lines while the build delivered **23 concepts and 15 of N
|
||||
boundaries** -- two steps after the reader, each measured: the orphan check
|
||||
took **710 of 2 761** (710 of 710 removed headings are followed immediately
|
||||
took **710 of N** (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
|
||||
reaches N + 1; 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
|
||||
from `pdf_outline` whose naive nearest-line rule was wrong on 1 840 of N + 1
|
||||
-- `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
|
||||
defaults, no flag: **N concepts**, **N of N** 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
|
||||
matching no declaration, `a)`-points **0 of the label-only points**, 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.
|
||||
known-positive at rank 1, and **N shared concept ids** with the PDF arm
|
||||
(100 % of this bundle, N of N + 1 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
|
||||
N + 1. Two directories of N − 23 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
|
||||
|
|
@ -134,7 +134,7 @@ one boundary rule:
|
|||
and never cleaned up. A titled section's `description` is its own FIRST
|
||||
spec point (first `<p>` of the first DIRECT-child `sec-type="spec"`, whole),
|
||||
carried by the plan entry, screened by the gate, and written only where a
|
||||
YAML reader reads it verbatim (`inbox._yaml_plain`): 2 026 of 2 761 titled
|
||||
YAML reader reads it verbatim (`inbox._yaml_plain`): 2 026 of N titled
|
||||
sections on the reference standard carry a point, **1 807** are written (2 have no `<p>`, 217
|
||||
carry `: ` and PyYAML refused exactly those frontmatters), none invented.
|
||||
SS 4.1 sets no length, so the one-paragraph limit is ours. **The directory
|
||||
|
|
@ -197,10 +197,10 @@ one boundary rule:
|
|||
read, an STS `<graphic>` was walked past, a PDF was opened for text alone,
|
||||
the converter's markdown writer dropped every picture, and the only writer
|
||||
into a bundle was `materialize.write_bytes(..., content: str)`. Measured on
|
||||
a 701-page reference standard: the section text is carried in full while **12
|
||||
a long reference standard: the section text is carried in full while **12
|
||||
`Tabell N-N` and 9 `Figur N-N`** captions stand over nothing, because that
|
||||
publisher ships those tables as raster pictures in BOTH the PDF and the
|
||||
NISO-STS delivery -- a section's "is given in table 84-2"
|
||||
NISO-STS delivery -- a section's "is given in table N-N"
|
||||
points at empty space. **Five readers PLACE and one module DECIDES:**
|
||||
`assets.py` owns what an image is (sniffed from the bytes, never from the
|
||||
claimed extension), what it is called (`<sha256[:12]>-<the source's own
|
||||
|
|
@ -802,7 +802,7 @@ and fixtures, never code.
|
|||
double-quoted where a YAML reader would not read it plain, and a flow value
|
||||
goes out as given but is REFUSED (exit 2) when a leaf has no flow form both
|
||||
PyYAML and the guard read -- so a `sources` URL with a query string, the form
|
||||
K3-19's own flagged build wrote on 2 761 of 2 761 concepts, fails the build. It adds any key and REPLACES only
|
||||
K3-19's own flagged build wrote on every concept, fails the build. It adds any key and REPLACES only
|
||||
`sources` and `description`, the two with a derived layer below them:
|
||||
precedence flag > what the document declares > file name. Every other key
|
||||
the door writes (`inbox._door_keys`, including Door A's `ingest_manifest`,
|
||||
|
|
@ -861,15 +861,15 @@ and fixtures, never code.
|
|||
at the boundaries its own `/Outlines` bookmark tree declares. It is NOT Arm D
|
||||
-- `--outline-run`/`--outline-gate` are a TEXT heuristic over numbered lines
|
||||
in the extracted text, and this opens a structure index the file already
|
||||
carries. Measured on ONE 701-page reference standard whose publisher also ships a
|
||||
NISO-STS structure for it, so the fasit is the publisher's own 2 761 titled
|
||||
sections: the shipped default finds **1 967 of 2 761**, **0 of its 28**
|
||||
carries. Measured on ONE long reference standard whose publisher also ships a
|
||||
NISO-STS structure for it, so the fasit is the publisher's own N titled
|
||||
sections: the shipped default finds **1 967 of N**, **0 of its 28**
|
||||
chapters, and **794 of 794** misses have their heading text PRESENT in the
|
||||
extracted text -- the line is read, the boundary is never opened. With the
|
||||
arm: **2 759 of 2 761 (99.9 %)**, chapter level **28 of 28**, concept titles
|
||||
identical to the source after normalisation **2 761 of 2 761** (the bookmark
|
||||
arm: **N − 2 of N (99.9 %)**, chapter level **28 of 28**, concept titles
|
||||
identical to the source after normalisation **N of N** (the bookmark
|
||||
title is complete because it does not come from the page), false positives
|
||||
**163 of 2 182 -> 3 of 2 762**, directories carrying two concept files
|
||||
**163 of 2 182 -> 3 of N + 1**, directories carrying two concept files
|
||||
**132 -> 2** with the 65 contents-copy pairs at **0**, front-matter concepts
|
||||
**72 -> 2**. Consumption: fasit present in the bundle **4 of 7 -> 7 of 7**,
|
||||
hit@1/8/50 **1/6 - 2/6 - 4/6 -> 3/6 - 5/6 - 6/6**; the known-positive is a
|
||||
|
|
@ -877,15 +877,15 @@ and fixtures, never code.
|
|||
the segmentation half of that row is closed and the ranking half is not.
|
||||
**The bridge from (page, `/XYZ` top) to a line index is the whole risk and
|
||||
BOTH routes are measured:** `extract_text_lines` splits lines identically to
|
||||
`extract_text` on **701 of 701** pages and that check SHIPS per page, the y
|
||||
route and the title route disagree on **0 of 2 762**, flat from 0 to 8 pt and
|
||||
`extract_text` on **every** page and that check SHIPS per page, the y
|
||||
route and the title route disagree on **0 of N + 1**, flat from 0 to 8 pt and
|
||||
collapsing at 12, so the rule carries **no tolerance constant**; the naive
|
||||
nearest-line rule was wrong on **1 840 of 2 762**, one line early every time.
|
||||
nearest-line rule was wrong on **1 840 of N + 1**, one line early every time.
|
||||
The orphan check is NOT applied to a bookmark mark -- it asks whether anything
|
||||
stands under a candidate's first line, the right question for a guess and the
|
||||
wrong one for a publisher's declaration; **683 of 2 762** marks are container
|
||||
wrong one for a publisher's declaration; **683 of N + 1** marks are container
|
||||
sections and applying it scores 2 079. An unresolvable `/Dest` is dropped and
|
||||
COUNTED (the reference standard has 0 of 2 763; one of the eight reference PDFs has 2 of 2). NO
|
||||
COUNTED (the reference standard has 0 of N + 2; one of the eight reference PDFs has 2 of 2). NO
|
||||
new dependency: `pdfminer.six` already ships under `pdfplumber` in
|
||||
`[extract]`, so `uv.lock` is untouched and `pypdf` stays out. Cost 119.22 s ->
|
||||
183.31 s wall, peak RSS 3 252 -> 3 251 MiB, pages parsed 1 -> 1. **The default
|
||||
|
|
@ -1214,7 +1214,7 @@ and fixtures, never code.
|
|||
column, never also `extractor_unknown`. The files come from
|
||||
`ExtractedDocument.files` (what the resolver actually resolved), never
|
||||
from byte equality, which would credit the reference standard's 7 unpointed duplicates.
|
||||
- **A 701-page reference standard WAS refused whole by guard 1.4.0 because of its 71 U+00AD soft
|
||||
- **A long reference standard WAS refused whole by guard 1.4.0 because of its 71 U+00AD soft
|
||||
hyphens, and since 2026-09-19 okf removes them itself.** Operator decision
|
||||
2026-09-18, the chosen alternative verbatim: **one documented
|
||||
normalisation door BEFORE the guard**, `extract.normalise_extracted`,
|
||||
|
|
@ -1435,15 +1435,16 @@ the first retired set **8** (S1-S6 + KP + KN), the second **32** questions /
|
|||
be a silent pass (0 clean, 1 a `FAIL`, 2 did not run). Two bars today, both
|
||||
`structure_null_share` off the pinned 43-document reference -- `.pdf` 8/32,
|
||||
`.docx` 2/5 -- plus one definitional bar for every type (0 empty bodies,
|
||||
measured **0 of 8 602** concepts over four bundles). A bar needs **five
|
||||
measured **0** over every concept of four bundles). A bar needs **five
|
||||
documents on BOTH sides**, its own and the judged bundle's, which was found
|
||||
by RUNNING the gate: a one-PDF bundle scored 0 of 1 against the 32-document
|
||||
reference and read as `PASS`. **The bars are regression bars against a
|
||||
pinned artifact, never a quality claim**, and the defect that started G37 --
|
||||
the HTML arm's 1 148 of 2 761 boundaries -- is `UNMEASURED` here, because no
|
||||
the HTML arm's 1 148 of the reference standard's N declared boundaries --
|
||||
is `UNMEASURED` here, because no
|
||||
bundle-only metric reaches it: three candidates were measured over the same
|
||||
four bundles and **two order the known-bad and known-good arms the wrong way
|
||||
round** (duplicate titles within a document 0/3 206 against 349/2 761; short
|
||||
round** (duplicate titles within a document 0.0 % against 12.6 %; short
|
||||
concepts 5.6 % against 14.6 %), while the third (duplicate titles across the
|
||||
whole bundle, 37.8 / 16.3 / 12.6 / 5.7 %) orders them correctly and ships
|
||||
anyway WITHOUT a bar, since any bar separating them sits between the two
|
||||
|
|
@ -1461,34 +1462,37 @@ the first retired set **8** (S1-S6 + KP + KN), the second **32** questions /
|
|||
input this gate takes: one whole-bundle row, `boundary_share` = declared
|
||||
boundaries that became a concept over declared boundaries. It is whole-bundle
|
||||
and never per file type, because a fasit names ONE document's sections and a
|
||||
bundle can spread them over 828 source files -- which the known-bad arm does.
|
||||
bundle can spread them over hundreds of source files -- which the known-bad
|
||||
arm does.
|
||||
**The normalisation was derived before the metric was built**, not guessed:
|
||||
strip all whitespace, lowercase, reproduces the fasit's own `norm` from its
|
||||
own `title` on **2 761 of 2 761** rows (alphanumerics-only scores 58 -- it
|
||||
own `title` on **all N** rows (alphanumerics-only scores 58 -- it
|
||||
eats the `.` in `2.1Hovedkapitler`). **A boundary is recovered in EITHER of
|
||||
two forms and neither is a fallback**: the concept's normalised title equal to
|
||||
`norm`, or the pair `(concept's own directory, residual title)` -- the literal
|
||||
form wants the declared title WITH its numbering token, the pair form WITHOUT,
|
||||
and no bundle can offer both, because okf's default route moves that token
|
||||
into the concept id. Measured on the known-good arm: literal **22 of 2 761**,
|
||||
paired **2 737**, either **2 759 (99.9 %)**; on an earlier build of the same document the split is
|
||||
exactly inverted (2 727 literal, 0 paired), so a gate scoring one form alone
|
||||
into the concept id. Measured on the known-good arm: literal **22 of N**,
|
||||
paired **N − 24**, either **N − 2 (99.9 %)**; on an earlier build of the same document the split is
|
||||
exactly inverted (N − 34 literal, 0 paired), so a gate scoring one form alone
|
||||
reports a 99.9 % arm as 0.8 % and calls it a segmentation defect. The two
|
||||
forms are a downstream consumer repository's M8 correction, which it took verbatim from THIS
|
||||
repository's round-14 report -- the instrument reproduces both so the two
|
||||
repos cannot silently measure different things. One bar, at the pinned
|
||||
artifact's own value: **2 759/2 761 with `corpora = 1`**, and P2 is in the
|
||||
repos cannot silently measure different things. One bar, **999/1000 with
|
||||
`corpora = 1`**: 99.9 %, just under the known-good arm's measured share
|
||||
(N − 2 of N), and every build measured reads the same verdict under it as
|
||||
under that measured fraction. P2 is in the
|
||||
OUTPUT and not only in the document (`N = 1 corpus` on every boundary row).
|
||||
The known-bad arm is **1 148 of 2 761 (41.6 %)**, now `FAIL` + exit 1 where
|
||||
The known-bad arm is **1 148 of N (41.6 %)**, now `FAIL` + exit 1 where
|
||||
the bundle-only gate gave exit 3. **`--fasit` is an ASSERTION** (the posture
|
||||
`okf consume --ref` has) that this bundle is a build of the document the fasit
|
||||
describes: the K2 reference and a downstream bundle of another document both score **0 of 2 761** and read
|
||||
describes: the K2 reference and a downstream bundle of another document both score **0 of N** and read
|
||||
`FAIL`, which is the assertion being wrong and not the bundle -- a gate
|
||||
telling those apart would need a bar read off the bundles it judges. The bar
|
||||
is TIGHT and the cost is published: 2 of 4 builds of that document fall under it (2 752
|
||||
and 2 727 of 2 761), while **any bar between 41.6 % and 98.8 % separates the
|
||||
known-bad arm from every build of that document measured** -- the shipped one is the only
|
||||
point in that interval read off a pinned artifact. An unreadable fasit exits
|
||||
is TIGHT and the cost is published: 2 of 4 builds of that document fall under it (N − 9
|
||||
and N − 34 of N, 99.7 % and 98.8 %), while **any bar between 41.6 % and 98.8 % separates the
|
||||
known-bad arm from every build of that document measured** -- the shipped one sits
|
||||
above that interval, just under the one share read off a pinned artifact. An unreadable fasit exits
|
||||
**2** with its reason, never a quiet `UNMEASURED`, and a fasit under five rows
|
||||
is `UNMEASURED` (`MIN_DECLARED_FOR_A_THRESHOLD`, the document floor in the
|
||||
fasit's unit). **Without `--fasit` the command is byte-for-byte what it was**,
|
||||
|
|
@ -1508,7 +1512,8 @@ the first retired set **8** (S1-S6 + KP + KN), the second **32** questions /
|
|||
removed or rebuilt while the process runs is seen by the next call with no
|
||||
restart, no config edit and no code change (measured, 9 of 9 discovery checks
|
||||
over three bundles written while serving). The cost is paid per call and is
|
||||
published: **0.75 s** for the identity of a 2 756-concept bundle, **5.6 s**
|
||||
published: **0.75 s** for the identity of a one-document bundle of a few
|
||||
thousand concepts, **5.6 s**
|
||||
for one ask. Containment is TWO independent checks -- the bundle's own index
|
||||
must name the concept AND `connectors.safe_resolve` must place it inside the
|
||||
bundle -- and a mutant removing either one alone still refuses, with a
|
||||
|
|
@ -1780,11 +1785,11 @@ the first retired set **8** (S1-S6 + KP + KN), the second **32** questions /
|
|||
NO bundle changing. It caps how many DELIVERED places one `source_file` may
|
||||
take, cutting where `shortlist = candidates[:k]` cuts, so the freed place goes
|
||||
to the next candidate and `k` is still delivered in full. The defect was
|
||||
measured OUTSIDE this repo on a 3206-concept bundle of a published handbook:
|
||||
the code's own process overview is **28 of 3206 concepts (0.87 %)** and **8.0 %
|
||||
measured OUTSIDE this repo on a bundle of a published handbook, a few thousand
|
||||
concepts: the code's own process overview is **0.87 % of its concepts** and **8.0 %
|
||||
of the source characters** yet took **8 of 8** delivered places on one question
|
||||
and **7 of 8** on the known-positive, which was not delivered at all --
|
||||
identical at 343 and 1651 concepts, so it is the corpus's COMPOSITION (it holds
|
||||
identical at a tenth and at half of the corpus, so it is the corpus's COMPOSITION (it holds
|
||||
its own table of contents) and not its size, and a split would move it rather
|
||||
than remove it. Swept over {2, 3, 4, off} on three bundles with the fasit
|
||||
prefixes validated against the bundle FIRST (that control caught a defect in
|
||||
|
|
|
|||
71
README.md
71
README.md
|
|
@ -156,10 +156,10 @@ that test red.
|
|||
| `.txt` | `_extract_passthrough` | core | stdlib, no corpus class | As `.md`. A document with no headings yields no segments, which is a failed build rather than a flat bundle. |
|
||||
| `.csv` | `_extract_csv` | core | stdlib, no corpus class | Parsed with the stdlib reader and rendered as one markdown table; a file with no header row is refused. |
|
||||
| `.json` | `_extract_json` | core | stdlib, no corpus class | Fenced verbatim. No structure is derived from the keys. |
|
||||
| `.html` | `_extract_html` | core | measured | Block tags open their own lines and `h1`–`h6` carry the ATX marker for their own level. It does **not** go through the converter, although the converter reads HTML: that route would add CVE-2025-51591 (SSRF via an iframe in HTML input) and buy nothing. The denominator is 828 files — one product, one format, one publisher. |
|
||||
| `.htm` | `_extract_html` | core | stdlib, no corpus class | The same reader as `.html`. The 828-file class is recorded for `.html` alone, and this row does not borrow it. |
|
||||
| `.xml` | `_extract_xml` | core | measured | A NISO-STS document (`<standard>` root, or any `<sec>`) becomes one heading per titled section at the section's own nesting depth; any other XML keeps its text in document order and gets no invented structure. A `<!DOCTYPE` is refused unparsed. The denominator is one file, one publisher, one schema — 2 761 titled sections. |
|
||||
| `.pdf` | `_extract_pdf` | `[extract]`: pdfplumber | measured | Eight corpus documents with a hand-counted fasit, plus a 701-page reference standard whose publisher also ships its structure. Prose only: drawn content has no text to recover, and every extraction warns. OCR lives here as a reading mode for a PDF page whose own text never arrived (`--ocr`, `OCR_CID_SHARE`), never as an entry for image files. |
|
||||
| `.html` | `_extract_html` | core | measured | Block tags open their own lines and `h1`–`h6` carry the ATX marker for their own level. It does **not** go through the converter, although the converter reads HTML: that route would add CVE-2025-51591 (SSRF via an iframe in HTML input) and buy nothing. The denominator is one HTML corpus of M files — one product, one format, one publisher. |
|
||||
| `.htm` | `_extract_html` | core | stdlib, no corpus class | The same reader as `.html`. The HTML corpus class is recorded for `.html` alone, and this row does not borrow it. |
|
||||
| `.xml` | `_extract_xml` | core | measured | A NISO-STS document (`<standard>` root, or any `<sec>`) becomes one heading per titled section at the section's own nesting depth; any other XML keeps its text in document order and gets no invented structure. A `<!DOCTYPE` is refused unparsed. The denominator is one file, one publisher, one schema — N titled sections. |
|
||||
| `.pdf` | `_extract_pdf` | `[extract]`: pdfplumber | measured | Eight corpus documents with a hand-counted fasit, plus a long reference standard whose publisher also ships its structure. Prose only: drawn content has no text to recover, and every extraction warns. OCR lives here as a reading mode for a PDF page whose own text never arrived (`--ocr`, `OCR_CID_SHARE`), never as an entry for image files. |
|
||||
| `.docx` | `_extract_office` | `[extract]`: pypandoc-binary | measured | Five corpus documents. `source_lines` index the extracted text and not the original's paragraphs — the two counts agree on none of the five. |
|
||||
| `.xlsx` | `_extract_office` | `[extract]`: pypandoc-binary | measured | Written as pipe tables, one source row per line; a sheet name becomes a heading, and a row is located by `source_sheet` and `source_rows`. |
|
||||
| `.pptx` | `_extract_office` | `[extract]`: pypandoc-binary | constructed | N = 2 decks. 2 of 2 slide titles recovered on a deck that declares them, 0 of 2 on a deck that does not, where the converter writes `Slide 1` / `Slide 2` because it has no title to use. |
|
||||
|
|
@ -277,7 +277,7 @@ who meets one in an older document knows what they are looking at.
|
|||
key with an empty value. `okf.parse_frontmatter` is public API, so this
|
||||
changes what an outside caller reads: it returns a flow string where it
|
||||
returned an empty one. That string is a READING projection — PyYAML reads
|
||||
it back on 0 of the 4 605 block files measured, because the `?` opening a
|
||||
it back on 0 of the block files measured, because the `?` opening a
|
||||
query string in the source URL ends the flow scalar — and the emitter still
|
||||
writes flow, so no bundle bytes move. `okf consume` also stops scoring the
|
||||
door's own `Enclosing section:` link line, which is now the default reading;
|
||||
|
|
@ -309,13 +309,13 @@ who meets one in an older document knows what they are looking at.
|
|||
- `v0.8.1` — a question that accounts for a concept's WHOLE
|
||||
title reads that concept first (`--title-covered`, on by default, opt out
|
||||
with `--no-title-covered`). A ranking fix, no new functionality: on one
|
||||
publisher's 2 761-concept bundle the answering section was delivered at
|
||||
publisher's one-document bundle of a few thousand concepts the answering section was delivered at
|
||||
rank 1 on 3 of 6 scored questions before it and 6 of 6 after, and no other
|
||||
measured bundle's payload changed one byte.
|
||||
- `v0.8.0` — `.xml` is a core file type, read as NISO-STS through the stdlib
|
||||
parser, and a section the source DECLARES takes the
|
||||
declared-structure route — one publisher's reference standard segments at 2 761 of
|
||||
2 761 of its own declared sections at the shipped defaults. No other file
|
||||
declared-structure route — one publisher's reference standard segments at all N
|
||||
of its own declared sections at the shipped defaults. No other file
|
||||
type changes one byte, measured on the bytes.
|
||||
- `v0.7.0` — `okf project` builds the bundle `okf build` builds (they were one
|
||||
flag apart before it), and the generated skill states the question,
|
||||
|
|
@ -365,8 +365,8 @@ at the bundle root, named `<sha256[:12]>-<the source's own base name>`, and the
|
|||
concept carries a two-line pointer where the picture stood:
|
||||
|
||||
```markdown
|
||||

|
||||
Image: graphics/tabell-84-2.png (120x90 px) -- Tabell 84-2 Hevetidsklasser
|
||||

|
||||
Image: graphics/tabell-7-2.png (120x90 px) -- Tabell 7-2 Hevetidsklasser
|
||||
```
|
||||
|
||||
The first line is markdown, so a reader that renders the concept sees the
|
||||
|
|
@ -374,11 +374,11 @@ picture. The second states what the first cannot — the name the SOURCE gave th
|
|||
file and the size in pixels — which are the two facts a person checking the
|
||||
bundle against the original needs.
|
||||
|
||||
**Why it exists.** Measured on a 701-page reference standard, published as a
|
||||
**Why it exists.** Measured on a long reference standard, published as a
|
||||
PDF and as a NISO-STS 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.
|
||||
One section says a class "is given in table 84-2" and table 84-2 is a
|
||||
One section says a class "is given in table N-N" and that table is a
|
||||
JPEG. A bundle like that reads as complete and is not.
|
||||
|
||||
**What it costs, measured on the 43-document reference corpus** (`K2/trinn1`,
|
||||
|
|
@ -605,10 +605,10 @@ u and d both stay 0 and nothing else could see the loss.
|
|||
**The soft hyphen is removed before the persist gate, and counted** (operator
|
||||
decision 2026-09-18). U+00AD is in `llm-ingestion-guard`'s zero-width set, and
|
||||
`output:zero-width-present` is an any-tier carrier: a document carrying one is
|
||||
`fail_secure` at every trust level. Measured on a 701-page reference standard — 71
|
||||
`fail_secure` at every trust level. Measured on a long reference standard — 71
|
||||
U+00AD, and 0 of U+200B, U+200C, U+200D, U+FEFF and U+2060 — those 71 are
|
||||
Norwegian hyphenation points inside words (`ar[SHY]beider`, `sur[SHY]deig`),
|
||||
so a 701-page reference standard was unreadable for the whole chain over typography.
|
||||
so that reference standard was unreadable for the whole chain over typography.
|
||||
`extract.normalise_extracted` removes that one character from every extracted
|
||||
text and reports the count as `normalised_soft_hyphen`, per document and for
|
||||
the run, in the accounting JSON and in a `**Normalisation**` bullet in
|
||||
|
|
@ -642,7 +642,7 @@ the nearest ancestor that holds text — the nearest preceding plan entry at a
|
|||
smaller level, passing over an ancestor that is empty too. It copies no text
|
||||
and moves no boundary. It exists for a document that states its points once and
|
||||
lets every nested section inherit them: measured on one reference standard, **710 of
|
||||
2 761** concepts are heading-only, and the plan's level and order name the same
|
||||
its N** concepts are heading-only, and the plan's level and order name the same
|
||||
ancestor as the document's own nesting on **710 of 710** since K3-21 (708
|
||||
before: the two others sit at depth 7, and the reader clipped their level to 6
|
||||
in the plan as well as in the markdown heading, so they pointed one level too
|
||||
|
|
@ -700,11 +700,11 @@ bookmark destination is the contents listing rather than a second copy of the
|
|||
body, so a contents entry and the section it lists stop landing as two concepts
|
||||
under one id.
|
||||
|
||||
The measurement is one 701-page reference standard whose publisher also ships a
|
||||
NISO-STS structure for it, so the fasit is the publisher's own. Under the
|
||||
shipped default that document gives 1967 of 2761 boundaries, none of its 28
|
||||
The measurement is one long reference standard whose publisher also ships a
|
||||
NISO-STS structure for it, so the fasit is the publisher's own: N declared
|
||||
sections. Under the shipped default that document gives 1967 of N boundaries, none of its 28
|
||||
chapters, and 794 of 794 misses have their heading text present in the text the
|
||||
build read. With the arm it gives 2759 of 2761 and 28 of 28. The flag stays off
|
||||
build read. With the arm it gives N − 2 of N (99.9 %) and 28 of 28. The flag stays off
|
||||
because reach is the open question, not quality: **1 of the 8** reference PDFs
|
||||
in this repository's own sample carries a usable tree, and a bookmark tree is
|
||||
the publisher's *claim* about its own structure — a stale or wrongly pointing
|
||||
|
|
@ -753,7 +753,7 @@ rank was measured in the same session and did NOT ship; see
|
|||
| `--outline-gate` (**on** by default since 2026-09-09; opt out with `--no-outline-gate`) | admit `--outline-run`'s RECOVERED headings only where the document declares none of its own, plus any one recovered heading whose span covers `OUTLINE_SHARE` (0.20) of the text. Applied at admission, before spans are closed, so the text a removed mark opened is carried by the mark above it rather than lost | on the 12-document sample: `pdf` **2 of 8 → 5 of 8** alone and **7 of 8** with the rule above, `docx` unchanged at **3 of 3**. Whole corpus: it fires on **25 of 39** readable documents, changes the plan in **15 of 39**, and removes **64 of 485** proposed entries. No plan disappears (32 → 32) |
|
||||
| `--first-span-from-zero` (**on** by default since 2026-09-10; opt out with `--no-first-span-from-zero`) | start the first concept at character 0, so the text above it belongs to a segment instead of to none. Adds no boundary and removes none | Measured over the 39-document corpus, the default before this rule left **207 435 characters — 11.92 %** — in no segment at all: **163 804 above the first entry** (in **32 of the 32** documents that get a plan), 26 041 *between* entries and 17 590 after the last. This rule closes the first part entirely, 79 % of the whole, leaving **43 631 characters (2.51 %) over 8 of 32 documents** with two named mechanisms of their own. It adds no boundary and the K2 concept count is identical with and without it (**425 = 425**); on the 12-position reference it changes **not one cell**, and hit@8 on a K2 bundle built with it holds **5 of 6 at ranks 1,1,1,1,1,–** under both tie-breaks |
|
||||
| `--close-span-gaps` (**on** by default since 2026-09-11; opt out with `--no-close-span-gaps`) | close a concept's span against the next SURVIVING concept, and the last against the end of the text. Three steps remove a candidate AFTER its neighbour's span was already closed against it — the orphan check, and `fold_units` clause 1 both between entries and on the last run — and the removed mark's text then belongs to no segment. Adds no boundary and removes none; only spans' ends move | It closes the whole remainder the rule above left: **43 631 characters, 2.51 % of the corpus over 8 of the 32 documents with a plan, to 0** — both the 26 041 between entries and the 17 590 after the last. Decomposed: orphan check **18 527** over 15 of 39 documents, clause 1 **7 514** between entries, clause 1 on the last run **all 17 590** of the tail (with `unit_fold=False` the corpus tail gap is 0). The entry count is identical (**429 = 429** on the corpus, **436 = 436** concepts on K2, **52 = 52** md on a five-document folder); on the 12-position reference it changes **not one cell** (11 of 12 under `|F|`[3]=12, 10 of 12 under `|F|`[3]=11), and hit@8 holds **5 of 6 at ranks 1,1,1,1,1,–** on the new bundle, the previous default and Arm B alike |
|
||||
| `--pdf-outline` (**off**; opt out is the default, opt in with the flag) | cut a PDF at the boundaries its own `/Outlines` bookmark tree declares, instead of at the ones the text rules recover. A SEGMENTATION arm, not a reader option: the extracted text is byte for byte the same either way, and a PDF that carries no tree builds byte-identically with the flag on. The title comes from the BOOKMARK, so it is not cut short at the page's line break, and a page before the first bookmark destination is the contents listing rather than a second copy of the body | one 701-page reference standard whose publisher also ships a NISO-STS structure for it, so the fasit is the publisher's own: boundaries **1967 of 2761 → 2759 of 2761**, chapter level **0 of 28 → 28 of 28**, concept titles identical to the source title after normalisation **2761 of 2761**, false positives **163 of 2182 → 3 of 2762**, directories carrying two concept files **132 of 2050 → 2 of 2738**, contents-copy pairs **65 → 0**. Consumption on the same eight questions: fasit present in the bundle **4 of 7 → 7 of 7**, hit@1/8/50 **1/6 · 2/6 · 4/6 → 3/6 · 5/6 · 6/6**. Cost 119.22 s → 183.31 s wall, peak RSS 3252 → 3251 MiB, no new dependency and no second parse of the pages. **Off, and the reach is why:** **1 of the 8** reference PDFs carries a usable tree at all, and a bookmark tree is the publisher's CLAIM about its own structure — a stale or wrongly pointing one carries that error straight into the segmentation |
|
||||
| `--pdf-outline` (**off**; opt out is the default, opt in with the flag) | cut a PDF at the boundaries its own `/Outlines` bookmark tree declares, instead of at the ones the text rules recover. A SEGMENTATION arm, not a reader option: the extracted text is byte for byte the same either way, and a PDF that carries no tree builds byte-identically with the flag on. The title comes from the BOOKMARK, so it is not cut short at the page's line break, and a page before the first bookmark destination is the contents listing rather than a second copy of the body | one long reference standard whose publisher also ships a NISO-STS structure for it, so the fasit is the publisher's own, N declared sections: boundaries **1967 of N → N − 2 of N (99.9 %)**, chapter level **0 of 28 → 28 of 28**, concept titles identical to the source title after normalisation **N of N**, false positives **163 of 2182 → 3 of N + 1**, directories carrying two concept files **132 of 2050 → 2 of N − 23**, contents-copy pairs **65 → 0**. Consumption on the same eight questions: fasit present in the bundle **4 of 7 → 7 of 7**, hit@1/8/50 **1/6 · 2/6 · 4/6 → 3/6 · 5/6 · 6/6**. Cost 119.22 s → 183.31 s wall, peak RSS 3252 → 3251 MiB, no new dependency and no second parse of the pages. **Off, and the reach is why:** **1 of the 8** reference PDFs carries a usable tree at all, and a bookmark tree is the publisher's CLAIM about its own structure — a stale or wrongly pointing one carries that error straight into the segmentation |
|
||||
|
||||
They compose, and the order above is the order they apply in. Measured on a
|
||||
five-document tender folder (2 `pdf`, 2 `docx`, 1 `xlsx`), concepts per
|
||||
|
|
@ -906,11 +906,11 @@ pre-exclusion, never before — and the payload then declares
|
|||
with no bundle changing. It caps how many DELIVERED places one source document
|
||||
may take, cutting where the shortlist is cut so the freed place goes to the next
|
||||
candidate and `k` is still delivered in full. The defect it repairs was measured
|
||||
outside this repository on a 3206-concept bundle of a published handbook: the
|
||||
handbook's own overview section contributes **28 of 3206 concepts (0.87 %)** and
|
||||
outside this repository on a bundle of a published handbook, a few thousand concepts: the
|
||||
handbook's own overview section contributes **0.87 % of its concepts** and
|
||||
**8.0 % of the source characters**, and took **8 of 8** delivered places on one
|
||||
question and **7 of 8** on the known-positive, which was not delivered at all.
|
||||
Identical at 343 and 1651 concepts, so the cause is the corpus's COMPOSITION —
|
||||
Identical at a tenth and at half of the corpus, so the cause is the corpus's COMPOSITION —
|
||||
that it holds its own table of contents — and not its size; any corpus with a
|
||||
contents list, a project overview or a summary document has the same property.
|
||||
Swept over {2, 3, 4, off} on three bundles: at 2 and 3 hit@8 goes **5 of 6 to
|
||||
|
|
@ -994,7 +994,7 @@ much of the CANDIDATE the question accounts for, so a section titled with the
|
|||
question's subject alone scores exactly what a narrower section titled with that
|
||||
subject plus a qualifier scores — and then loses on the body count, because a
|
||||
longer title and a longer body can only reach more of the question. Measured on
|
||||
a 2 761-concept bundle of one standard, where **none of the six flags above
|
||||
a one-document bundle of a few thousand concepts, where **none of the six flags above
|
||||
moved the number at all**: hit@1/8/50 **3 of 6 · 5 of 6 · 5 of 6 → 6 of 6 · 6 of
|
||||
6 · 6 of 6** at default `k`, the same 6 of 6 at `--k 50`, the known-positive
|
||||
holding rank 1 at both and the known-negative still not a hit. The three
|
||||
|
|
@ -1003,7 +1003,7 @@ recovered rows go 4 → 1, not-delivered → 1 and 3 → 1.
|
|||
It is a PARTITION and not a fourth RRF signal, and the arithmetic is the
|
||||
reason: RRF consumes ranks alone, so with shared ranks a rule whose positive
|
||||
group has `m` members is worth `1/61 − 1/(61 + m)`, and a rule firing on ONE
|
||||
concept of 2 761 is worth 0.00026 against a body-signal gap of 0.0029 — **a
|
||||
concept of N is worth 0.00026 against a body-signal gap of 0.0029 — **a
|
||||
precise rule is worth least under this fusion, backwards from what precision is
|
||||
for**. Measured as a signal the same predicate moves hit@1 not at all; as a
|
||||
partition it reaches 6 of 6. `lookup_hits` is the same shape for the same
|
||||
|
|
@ -1159,21 +1159,24 @@ never `UNMEASURED`. A boundary counts as recovered in either of two forms — a
|
|||
concept whose normalised title equals `norm`, or the pair of the concept's own
|
||||
directory and its residual title — because the numbering token a publisher glues
|
||||
into a heading lands in the concept *id* on one route and in the *title* on
|
||||
another: measured on one 2 761-section standard, the first form alone reaches
|
||||
**22 of 2 761** where the two together reach **2 759**.
|
||||
another: measured on one standard with N declared sections, the first form alone reaches
|
||||
**22 of N** where the two together reach **N − 2**.
|
||||
|
||||
<!-- quality-boundary-threshold: 2759/2761 -->
|
||||
<!-- quality-boundary-threshold: 999/1000 -->
|
||||
|
||||
| metric | threshold | N |
|
||||
|---|---|---|
|
||||
| `boundary_share` | 2 759/2 761 | 2 761 declared boundaries, **1 corpus** |
|
||||
| `boundary_share` | **999/1000** | N declared boundaries, **1 corpus** |
|
||||
|
||||
The bar is 99.9 %, just under the known-good arm's measured share; every build
|
||||
measured reads the same under it.
|
||||
|
||||
**`--fasit` is an assertion**, the way `okf consume --ref` is: it says this
|
||||
bundle is a build of the document the fasit describes. A bundle of another
|
||||
product scores near zero and reads `FAIL` — that is the assertion being wrong,
|
||||
not the bundle. The bar itself rests on **one product**, which the output says
|
||||
on every run. Both facts, the arm it separates (1 148 of 2 761 against 2 759 of
|
||||
2 761) and the interval any bar could sit in are in
|
||||
on every run. Both facts, the arm it separates (1 148 of N against N − 2 of
|
||||
N) and the interval any bar could sit in are in
|
||||
[`docs/2026-09-12-g37-terskler.md`](docs/2026-09-12-g37-terskler.md) § 7.
|
||||
|
||||
## Judge the retrieval: `python3 tools/okf_retrieval_gate.py`
|
||||
|
|
@ -1391,7 +1394,7 @@ re-reads the directories and recomputes the bundle's content identity, so the
|
|||
identity in an answer is a fact about the bytes at the moment of the call
|
||||
rather than at startup — a server that answered from yesterday's bundle is the
|
||||
one failure you cannot see from the outside. The cost is real and is paid per
|
||||
call: on a 2 756-concept bundle the identity is a 0.75 s hash of the whole
|
||||
call: on a one-document bundle of a few thousand concepts the identity is a 0.75 s hash of the whole
|
||||
concept tree, and one `okf_ask` is 5.6 s.
|
||||
|
||||
**Refusals are loud.** A path climbing out of the bundle, a symlink leaving the
|
||||
|
|
@ -1736,8 +1739,8 @@ blur it:
|
|||
(`materialize.parse_frontmatter`, which is public API, and the two internal
|
||||
ones). A block sequence of mappings is decoded into the flow rendering those
|
||||
readers round-trip; the entries never enter the document's key namespace.
|
||||
Measured against PyYAML 6.0.3 and the pinned guard 1.4.0 on 4 609 of 4 609
|
||||
concept files carrying a block `sources`, all three readers return the same
|
||||
Measured against PyYAML 6.0.3 and the pinned guard 1.4.0 on every concept
|
||||
file of four bundles carrying a block `sources`, all three readers return the same
|
||||
entries both references do.
|
||||
- **Every other key, flow only.** `executor`, `attester` and any other block
|
||||
mapping are still skipped rather than parsed: two block mappings that both
|
||||
|
|
|
|||
|
|
@ -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 S1–S6: **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 |
|
||||
|
||||
|
|
|
|||
|
|
@ -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 |
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) |
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 S1–S6) | **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 S1–S6) | **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 = 1–2 |
|
||||
|
||||
### 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ text, every warning said so, and the only writer into a bundle was
|
|||
:func:`materialize.write_bytes`, whose signature is ``(bundle_dir, name,
|
||||
content: str)`` -- UTF-8 in, text out, no binary route anywhere. A document
|
||||
whose table is a raster picture therefore reached a concept as an absence with
|
||||
no denominator. Measured on a 701-page reference standard, the publisher's
|
||||
no denominator. Measured on a long reference standard, the publisher's
|
||||
own NISO-STS delivery: the section text is carried in full, and 12
|
||||
``Tabell N-N`` and 9 ``Figur N-N`` captions stand over nothing, so a sentence
|
||||
saying "... er gitt i tabell 84-2" points at empty space.
|
||||
saying "... er gitt i tabell N-N" points at empty space.
|
||||
|
||||
THIS MODULE IS THE ONE PLACE THAT DECIDES WHAT AN IMAGE IS. A reader hands it
|
||||
bytes and a name; it returns a carried image or raises a coded rejection. That
|
||||
|
|
@ -353,7 +353,7 @@ def inflated_size(raw: bytes, *, name: str, limit: int | None = None) -> int:
|
|||
and it stops at the first chunk that crosses `limit`. A legitimate image is
|
||||
therefore inflated twice -- once here and once by the reader that carries
|
||||
it -- which is the price of not holding an unbounded buffer to find out how
|
||||
big it is. Measured on the 701-page reference standard (50 image objects): the
|
||||
big it is. Measured on the long reference standard (50 image objects): the
|
||||
second pass costs under a second of a 200-second extraction.
|
||||
|
||||
A stream that is not valid deflate data is not this function's problem: the
|
||||
|
|
@ -987,12 +987,12 @@ def asset_name(image: ExtractedImage) -> str:
|
|||
same grammar every generated filename in this package uses. A link target
|
||||
sits inside `](...)`, which a space or a closing parenthesis terminates --
|
||||
and the reference delivery's own hrefs carry both (a name of the form
|
||||
`25-0143 - Tabeller - Navn (A1-A2).jpg`), so reducing is what makes the pointer
|
||||
`99-0001 - Tabeller - Navn (A1-A2).jpg`), so reducing is what makes the pointer
|
||||
followable rather than merely tidy.
|
||||
"""
|
||||
# The BASENAME, never the path the document reached it through. Measured
|
||||
# on the fixture inbox: one image pointed at as `graphics/figur-84-1.png`
|
||||
# from an HTML document and as `figur-84-1.png` from an STS one was written
|
||||
# on the fixture inbox: one image pointed at as `graphics/figur-7-1.png`
|
||||
# from an HTML document and as `figur-7-1.png` from an STS one was written
|
||||
# twice, under two names, in one run -- with the digest in both announcing
|
||||
# that the bytes were identical. The path is a property of the pointer, not
|
||||
# of the picture, and the full original survives on the pointer's own line.
|
||||
|
|
@ -1203,7 +1203,7 @@ def render_missing(
|
|||
|
||||
`label` is the alt text or the figure caption, and it is written for the
|
||||
same reason the line exists at all: the reader deciding whether the missing
|
||||
picture mattered is much better served by "Figur 84-1 Tverrprofil" than by
|
||||
picture mattered is much better served by "Figur 7-1 Prinsippskisse" than by
|
||||
a file name. 0.10.1 dropped it while closing the live-link defect -- the
|
||||
parameter stayed in the signature and no branch read it -- which an
|
||||
independent review measured as a regression against 0.10.0.
|
||||
|
|
|
|||
|
|
@ -346,7 +346,7 @@ def frontmatter_from_flags(pairs: Sequence[str]) -> dict[str, str]:
|
|||
def _document_prefixes(inbox: Path, walked: Sequence[Path]) -> dict[Path, str]:
|
||||
"""Each document's directory: the name it declares, else its file name.
|
||||
|
||||
MEASURED: a NISO-STS delivery landed every one of its 2 761 concepts under
|
||||
MEASURED: a NISO-STS delivery landed every one of its N concepts (one per titled section) under
|
||||
a directory named for the delivery path's file name, a UUID occurring 0
|
||||
times in the document, while the document's own `<doc-number>` said what it
|
||||
was. Only the file's STEM is replaced; the folders above it are the
|
||||
|
|
@ -927,7 +927,7 @@ def parse_args(argv: list[str] | None) -> argparse.Namespace:
|
|||
help=(
|
||||
"carry the images the sources declare into the bundle's assets/ "
|
||||
"directory and point at them from the concept text where they "
|
||||
"stand. ON since 0.10.0. Measured on a 701-page reference standard: 12 "
|
||||
"stand. ON since 0.10.0. Measured on a long reference standard: 12 "
|
||||
"Tabell N-N and 9 Figur N-N captions stood over nothing, because "
|
||||
"the publisher ships those tables as raster pictures. The image "
|
||||
"BYTES are not screened -- the gate reads text -- and log.md says so"
|
||||
|
|
@ -977,7 +977,7 @@ def parse_args(argv: list[str] | None) -> argparse.Namespace:
|
|||
"Give a concept whose body is its heading alone a `parent:` naming "
|
||||
"the nearest ancestor that holds text, by the plan's level and "
|
||||
"order, passing over an empty ancestor. Nothing is copied and no "
|
||||
"boundary moves. Measured on one standard: 710 of 2 761 concepts "
|
||||
"boundary moves. Measured on one standard: 710 (26 %%) of its concepts "
|
||||
"are heading-only, and the route names the ancestor its own "
|
||||
"nesting names on 708 of them. OFF: `okf consume` does not read "
|
||||
"the key"
|
||||
|
|
@ -1070,10 +1070,10 @@ def parse_args(argv: list[str] | None) -> argparse.Namespace:
|
|||
"SEGMENTATION arm and not a reader option: the extracted text is "
|
||||
"byte for byte the same either way, and a PDF that carries no "
|
||||
"bookmark tree builds byte-identically with the flag on. Measured "
|
||||
"on one 701-page reference standard whose publisher also ships a NISO-STS "
|
||||
"structure for it: the text rules recover 1967 of 2761 titled "
|
||||
"on one long reference standard whose publisher also ships a NISO-STS "
|
||||
"structure for it: the text rules recover 1967 (71 %%) of its titled "
|
||||
"sections and 0 of its 28 chapters, while its bookmark tree matches "
|
||||
"2761 of 2761 exactly. The title comes from the BOOKMARK, so it is "
|
||||
"every one exactly. The title comes from the BOOKMARK, so it is "
|
||||
"not cut short at the page's line break, and a page before the "
|
||||
"first bookmark destination is the table of contents rather than a "
|
||||
"second copy of the body. ONE document, ONE format, ONE publisher, "
|
||||
|
|
|
|||
|
|
@ -1421,7 +1421,7 @@ DEFAULT_STEM_PREFIX = True
|
|||
#: section titled with the question's subject and nothing else scores exactly
|
||||
#: what a narrower section titled with that subject PLUS a qualifier scores,
|
||||
#: and then loses to it on the body count. Measured 2026-09-10 on a
|
||||
#: 2 761-concept bundle of one standard, where the answering section carries
|
||||
#: one-document bundle of a few thousand concepts, where the answering section carries
|
||||
#: the bare term as its title on three of six scored questions, and each time
|
||||
#: lost to a section titled with the same word plus a qualifier (`X` behind
|
||||
#: `X av ...`, `X` behind `Enkel X`).
|
||||
|
|
@ -1429,7 +1429,7 @@ DEFAULT_STEM_PREFIX = True
|
|||
#: WHY A PARTITION AND NOT A FOURTH SIGNAL, measured rather than argued. RRF
|
||||
#: consumes RANKS ONLY, and with shared ranks a signal whose positive group is
|
||||
#: SMALL separates least of all: the group takes position 1 and everyone else
|
||||
#: position `len(group) + 1`, so a rule firing on 1 concept of 2 761 is worth
|
||||
#: position `len(group) + 1`, so a rule firing on 1 concept of thousands is worth
|
||||
#: `1/61 - 1/62` to it -- an order of magnitude under the body-signal gap it
|
||||
#: has to close. Measured as a signal on that bundle it moved hit@1 not at all
|
||||
#: (3 of 6); as a partition it reaches 6 of 6 candidate rank 1 with the
|
||||
|
|
@ -1449,7 +1449,7 @@ DEFAULT_STEM_PREFIX = True
|
|||
#: moved that neighbour from fusion rank 2 to rank 1 over the section the
|
||||
#: question names. A covered concept now RISES only past concepts whose titles
|
||||
#: answer no more question tokens, by equality, than it holds. On the
|
||||
#: 2 761-concept bundle no covered concept had such a title above it, so all
|
||||
#: one-document bundle no covered concept had such a title above it, so all
|
||||
#: eight payloads there are byte-identical to round 16's. Four other repairs
|
||||
#: were measured against it: a minimum title length (hit@1 there back to 3 of
|
||||
#: 6), a share of the question (holds only in a band set by the question's
|
||||
|
|
@ -1471,7 +1471,7 @@ def title_covered_hits(concepts: Sequence["Concept"], question: str) -> tuple[st
|
|||
|
||||
**BY EQUALITY, never by shared prefix, and that is measured rather than
|
||||
assumed.** `tokens_match` accepts four shared leading characters, which
|
||||
would admit `Anchorage` beside `Anchoring` and, on the 2 761-concept
|
||||
would admit `Anchorage` beside `Anchoring` and, on the one-document
|
||||
bundle, took the group from 1 concept to 6 on one question and from 9 to 31
|
||||
on another -- the answering section falling to candidate rank 6 and the
|
||||
known-positive to rank 2. Under equality both hold rank 1. The precedent is
|
||||
|
|
@ -1516,7 +1516,7 @@ def shared_id_prefix(concept_ids: Sequence[str]) -> int:
|
|||
document loses exactly that distinction, and a concept answering nothing
|
||||
but the directory stops being a guess.
|
||||
|
||||
MEASURED 2026-09-11 on a 2 761-concept, one-document bundle whose directory
|
||||
MEASURED 2026-09-11 on a one-document bundle of a few thousand concepts whose directory
|
||||
is the document's own number (K3-19): the known-positive question names the
|
||||
document and fell from rank 1 to not delivered at the default `k` (13 at
|
||||
`k` = 50). With the shared prefix unread it is rank 1 at both, and the six
|
||||
|
|
@ -1820,11 +1820,11 @@ WITHHOLDING_RULES = (
|
|||
#: **2 SINCE 2026-09-10, and it is the third change here that moves a payload
|
||||
#: with NO bundle changing** (after `--tie-shared-rank` and `--stem-prefix`);
|
||||
#: a consumer pinned to the previous excerpt order needs `--no-source-quota`.
|
||||
#: The defect it repairs was measured on a 3206-concept bundle of a published
|
||||
#: handbook: the code's own process overview contributes 28 of 3206 concepts
|
||||
#: (0.87 %) and 8.0 % of the source characters, and took 8 of 8 delivered
|
||||
#: places on one question and 7 of 8 on the known-positive -- identical at 343
|
||||
#: and 1651 concepts, so the cause is the corpus's COMPOSITION (it holds its
|
||||
#: The defect it repairs was measured on a bundle of a published handbook, a
|
||||
#: few thousand concepts: the code's own process overview contributes 0.87 % of
|
||||
#: its concepts and 8.0 % of the source characters, and took 8 of 8 delivered
|
||||
#: places on one question and 7 of 8 on the known-positive -- identical at half
|
||||
#: and at a tenth of the corpus, so the cause is the corpus's COMPOSITION (it holds its
|
||||
#: own table of contents) and not its size.
|
||||
#:
|
||||
#: SWEPT over {2, 3, 4, off} on three bundles. At 2 and 3 hit@8 goes 5 of 6 to
|
||||
|
|
@ -1999,7 +1999,7 @@ def excerpt_weight(excerpt: Mapping[str, object]) -> int:
|
|||
#: the one standard with shells it delivers the same set as the pointer, by
|
||||
#: construction and measured (16 of 16 payloads, both k), and on six questions
|
||||
#: whose answers are never heading-only sections it bought nothing a reader
|
||||
#: scored -- 6 of 6 correct either way -- at +2 488 and +2 746 B on the 2 of 8
|
||||
#: scored -- 6 of 6 correct either way -- at about +2.5 and +2.7 kB on the 2 of 8
|
||||
#: payloads holding a linked shell at the default k and up to +20 817 B at
|
||||
#: k 50. What it does buy shows on a question AT a shell: 3 847 characters of
|
||||
#: the enclosing section in the payload, where the pointer alone needs one
|
||||
|
|
@ -3218,7 +3218,7 @@ def _parser() -> argparse.ArgumentParser:
|
|||
"the concepts the fusion ranked above it, stopping beneath any whose "
|
||||
"title answers MORE question tokens than it holds (round 17). ON "
|
||||
"since 2026-09-10. "
|
||||
"Measured on a 2 761-concept bundle of one standard: hit@1 over six "
|
||||
"Measured on a one-document bundle of a few thousand concepts: hit@1 over six "
|
||||
"questions 3 of 6 -> 6 of 6 with the known-positive holding rank 1, "
|
||||
"where none of the six existing reading-side flags moved that "
|
||||
"number at all. The title is read by EQUALITY, never by shared "
|
||||
|
|
@ -3240,7 +3240,7 @@ def _parser() -> argparse.ArgumentParser:
|
|||
"cap how many DELIVERED places one source document may take, "
|
||||
"filling the freed places from the next candidate so k is still "
|
||||
"delivered in full. Default 2 since 2026-09-10. Measured on a "
|
||||
"3206-concept bundle whose corpus holds its own table of contents: "
|
||||
"handbook bundle whose corpus holds its own table of contents: "
|
||||
"that one document took 8 of 8 places and the answer was not "
|
||||
"delivered at all; at 2 it takes 2 of 8 and the answer comes in at "
|
||||
"rank 4. A bundle with no alternatives is unaffected -- the "
|
||||
|
|
|
|||
|
|
@ -103,11 +103,11 @@ REFUSED_NAME_CAP = 10
|
|||
#:
|
||||
#: ON, and the reason is the premise the capability was ordered on: what ends
|
||||
#: up in a bundle after converting N sources has to BE the sources. Measured on
|
||||
#: a 701-page reference standard -- 2 726 of 2 726 sections carry their text and 12
|
||||
#: a long reference standard -- every section carries its text and 12
|
||||
#: `Tabell N-N` and 9 `Figur N-N` captions stand over nothing, because the
|
||||
#: publisher ships those tables as raster pictures in both the PDF and the
|
||||
#: NISO-STS delivery. A bundle that reads as complete and answers "... er gitt
|
||||
#: i tabell 84-2" with an empty space is the defect; it is
|
||||
#: i tabell N-N" with an empty space is the defect; it is
|
||||
#: not a preference about bundle size.
|
||||
#:
|
||||
#: WHAT THIS COSTS IS PUBLISHED RATHER THAN IMPLIED. Measured over the K2
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ class ExtractionError(IngestError):
|
|||
bytes are not an image at all: this one says they are, and carrying them
|
||||
would put a file in the bundle that the `images: N` count reports as
|
||||
arrived and nothing downstream can read. Measured 2026-09-19 on the
|
||||
frozen delivery of a 701-page reference standard: 19 of its 50 assets were carried in exactly that
|
||||
frozen delivery of a long reference standard: 19 of its 50 assets were carried in exactly that
|
||||
condition, as RLE8 BMP
|
||||
- `asset_bmp_unsupported` — a BMP variant this reader does not express
|
||||
(RLE4, BITFIELDS, 16- or 32-bit samples, a 12-byte BITMAPCOREHEADER, a
|
||||
|
|
|
|||
|
|
@ -120,19 +120,19 @@ _PANDOC_FORMATS: dict[str, str] = {
|
|||
# and it is the one open finding of the three.
|
||||
#
|
||||
# `.html` JOINED THE TABLE 2026-09-09, as `measured`, and the class was chosen
|
||||
# against the definitions above rather than assumed: the 828 files are a
|
||||
# against the definitions above rather than assumed: 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 -- which is `measured`'s test, "someone wrote the document for their
|
||||
# own purposes and we counted what we got". What that class does NOT claim, and
|
||||
# the honesty limit that travels with it: the 828 files are ONE product in ONE
|
||||
# the honesty limit that travels with it: the M files are ONE product in ONE
|
||||
# format from ONE publisher, and the file boundaries and `<h1>`s are a
|
||||
# generator's cut of that document, not 828 documents anyone wrote.
|
||||
# generator's cut of that document, not M documents anyone wrote.
|
||||
#
|
||||
# `.xml` JOINED THE TABLE 2026-09-11, as `measured`, and the class was read off
|
||||
# the definitions above rather than inherited: the one file is a publisher's own
|
||||
# NISO-STS delivery of a 701-page reference standard, written for their purposes years before any lookup
|
||||
# of ours, and its 2 761 titled `<sec>` are a fasit nobody here authored. The
|
||||
# NISO-STS delivery of a long reference standard, written for their purposes years before any lookup
|
||||
# of ours, and its N titled `<sec>` are a fasit nobody here authored. The
|
||||
# honesty limit that travels with it and does NOT move when the build reaches
|
||||
# the reader's ceiling: the denominator is ONE file, ONE publisher, ONE schema.
|
||||
# `.xml` as a file type is far wider than NISO-STS, and a document in any other
|
||||
|
|
@ -141,12 +141,12 @@ _PANDOC_FORMATS: dict[str, str] = {
|
|||
#
|
||||
# `.pdf` JOINED THE TABLE 2026-09-10, as `measured`, and it enters on the
|
||||
# strongest evidence of any row here: eight real corpus PDFs with a fasit the
|
||||
# operator hand-counted document by document, plus a 701-page reference standard
|
||||
# whose PUBLISHER also ships a NISO-STS structure for it -- 2 761 titled
|
||||
# operator hand-counted document by document, plus a long reference standard
|
||||
# whose PUBLISHER also ships a NISO-STS structure for it -- N titled
|
||||
# sections, written for their own purposes and long before any lookup of ours.
|
||||
# The honesty limit that travels with it: those 2 761 rows are ONE product in
|
||||
# The honesty limit that travels with it: those N rows are ONE product in
|
||||
# ONE format from ONE publisher, its structure is a strict numbered hierarchy
|
||||
# on 2 739 of 2 761 titles, and a running prose document would measure
|
||||
# on N - 22 of its N titles, and a running prose document would measure
|
||||
# something else entirely.
|
||||
_EVIDENCE: dict[str, str] = {
|
||||
".pdf": "measured",
|
||||
|
|
@ -291,15 +291,15 @@ _BLOCK_TAGS = frozenset(
|
|||
# --- NISO-STS, and the two facts the whole reader turns on ------------------
|
||||
#
|
||||
# ONE: `<label>` carries the number and `<title>` carries the text. Measured on
|
||||
# the 701-page reference standard, 2 of its 2 761 `<title>` strings begin with a
|
||||
# the reference standard, 2 of its N declared `<title>` strings begin with a
|
||||
# digit -- the number is a sibling element, never glued on. A fasit that shows
|
||||
# `"2.1Hovedkapitler"` is its BUILDER joining the two. Emitting `<title>`
|
||||
# alone therefore scores 0 of 2 761 while every line of this file looks right,
|
||||
# alone therefore scores 0 of N while every line of this file looks right,
|
||||
# because the number is what okf reduces to a directory name.
|
||||
#
|
||||
# TWO: a `<sec>` with a `<label>` and no `<title>` is not a section heading. It
|
||||
# is a lettered point (`a)`, `c)`, `sec-type="spec"`) inside a process
|
||||
# description, and there are 4 954 of them against the document's own 2 761.
|
||||
# description, and they are most (64 %) of its `<sec>`, against the document's own N.
|
||||
# One heading each and the document's structure is the minority of its own
|
||||
# outline.
|
||||
_STS_ROOT = "standard"
|
||||
|
|
@ -340,7 +340,7 @@ _XML_INLINE_TAGS = frozenset(
|
|||
)
|
||||
|
||||
# The maximum ATX level `propose._ATX` can read (`#{1,6}`), which `_HEADING_TAGS`
|
||||
# stops at for the same reason. STS nesting goes DEEPER: 9 of the 2 761 titled
|
||||
# stops at for the same reason. STS nesting goes DEEPER: 9 of the N titled
|
||||
# sections in that document sit at depth 7, and `#######` matches nothing at
|
||||
# all. The depth is CLIPPED rather than dropped -- a clipped heading still sets
|
||||
# its boundary, where a dropped one loses the section entirely. The clip is the
|
||||
|
|
@ -386,7 +386,7 @@ _DATA_URI = re.compile(r"^data:(?P<media>[^;,]*)(?P<base64>;base64)?,(?P<payload
|
|||
#: `_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. Measured by PM
|
||||
#: 2026-09-18, a 701-page reference standard carries 71 U+00AD and 0 of the
|
||||
#: 2026-09-18, a long reference standard carries 71 U+00AD and 0 of the
|
||||
#: other four, so the whole document is unreadable for the whole chain over
|
||||
#: Norwegian hyphenation points inside words -- `ar[SHY]beider`,
|
||||
#: `sur[SHY]deig`, `bake[SHY]plate`. The verdict is formally right and
|
||||
|
|
@ -633,7 +633,7 @@ class _HTMLTextExtractor(HTMLParser):
|
|||
office rows reach the proposer through. `_ATX` and every other boundary
|
||||
grammar is line-anchored, so this class decides -- alone -- whether an HTML
|
||||
document can be segmented at all. It emitted one line for any input until
|
||||
2026-09-09, which is why 828 of 828 real sections produced zero boundaries.
|
||||
2026-09-09, which is why every one of M real sections produced zero boundaries.
|
||||
|
||||
TEXT IS PRESERVED EXACTLY. The only characters this adds are the ATX
|
||||
markers; strip those and the non-whitespace sequence is identical to the
|
||||
|
|
@ -761,8 +761,8 @@ class _XmlTextExtractor:
|
|||
self._prefix = ""
|
||||
# The declared structure, recorded WHERE it is written rather than
|
||||
# recovered from the finished string. The PDF arm has to bridge from
|
||||
# (page, `/XYZ` top) onto a line index and was wrong on 1 840 of 2 762
|
||||
# nodes under the naive rule; here the reader appended the line, so the
|
||||
# (page, `/XYZ` top) onto a line index and was wrong on 1 840 of N + 1
|
||||
# nodes (N being the document's declared sections) under the naive rule; here the reader appended the line, so the
|
||||
# index is not a guess and carries no tolerance. Empty for a document
|
||||
# that is not STS -- that is "this schema declares no section", and it
|
||||
# must not collapse into "this document has no structure to state".
|
||||
|
|
@ -813,7 +813,7 @@ class _XmlTextExtractor:
|
|||
single sentence" and sets no length anywhere. It is STRUCTURAL rather
|
||||
than a character count, because a cut inside a paragraph writes a
|
||||
sentence the source never wrote. Measured on the one STS document this
|
||||
row has: 2 026 of 2 761 titled sections carry a direct-child spec
|
||||
row has: 2 026 of its N titled sections carry a direct-child spec
|
||||
point; 264 of those points hold more than one `<p>` and 2 hold none;
|
||||
the first `<p>` runs 17 / 109 / 273 / 521 / 942 characters at min /
|
||||
median / p90 / p99 / max.
|
||||
|
|
@ -1008,7 +1008,7 @@ def declared_identity(name: str, data: bytes) -> DeclaredIdentity | None:
|
|||
"""`xml`: the identity a NISO-STS document declares, or `None`.
|
||||
|
||||
MEASURED ON THE ONE STS DOCUMENT THIS ROW HAS: exactly one `<std-ident>`
|
||||
(`<doc-number>P761 Oppskriftsboka</doc-number>` beside `<year>2025</year>`)
|
||||
(`<doc-number>P350 Oppskriftsboka</doc-number>` beside `<year>2025</year>`)
|
||||
and one `<title-wrap>` whose `<full>` is the document's title -- while the
|
||||
file carrying it was named for a delivery path, a UUID occurring 0 times in
|
||||
the document. `<doc-type>` is read by nobody: it said `Innledning` there,
|
||||
|
|
@ -1301,7 +1301,7 @@ class _PdfPage:
|
|||
a per-page check that ships and that decides whether the primary bridge
|
||||
route may be used at all. Appended lines are not in that geometry, so a
|
||||
joined string would fail the check on every page carrying an image and
|
||||
silently demote 2 762 bookmarks to the fallback route.
|
||||
silently demote every bookmark to the fallback route.
|
||||
"""
|
||||
|
||||
number: int
|
||||
|
|
@ -1978,11 +1978,10 @@ class OutlineMark:
|
|||
`level` is what the TREE declares, not a distance normalised against
|
||||
anything: a document whose outline carries its own root node puts its
|
||||
chapters at level 2, and rewriting that here would state a structure the
|
||||
publisher did not. Measured on a 701-page reference standard -- the tree's levels
|
||||
2..8 hold 28/118/500/1141/872/93/9 nodes against the publisher's own
|
||||
NISO-STS depths 1..7 at 28/118/500/1141/868/97/9, so the mapping is level
|
||||
minus one on five rows and the publisher disagrees with the publisher on
|
||||
four nodes. That disagreement is data, and it survives only if the level is
|
||||
publisher did not. Measured on a long reference standard -- the tree's levels
|
||||
2..8 against the publisher's own NISO-STS depths 1..7, the mapping is level
|
||||
minus one row for row on five rows and the publisher disagrees with the
|
||||
publisher on four nodes. That disagreement is data, and it survives only if the level is
|
||||
reported rather than fixed up.
|
||||
"""
|
||||
|
||||
|
|
@ -2008,7 +2007,7 @@ class PdfOutline:
|
|||
|
||||
`collided` is the same principle applied to the OTHER way a node leaves
|
||||
without a boundary. Two bookmarks can resolve to one line -- measured on
|
||||
the 701-page reference standard, its tree's root node and a front-matter
|
||||
the long reference standard, its tree's root node and a front-matter
|
||||
node both
|
||||
land on line 0 -- and only the first can become a mark, because two
|
||||
candidates at one offset give the first an empty span that the orphan check
|
||||
|
|
@ -2071,21 +2070,21 @@ def pdf_outline(
|
|||
|
||||
THE BRIDGE IS THE WHOLE PROBLEM, and both routes are measured rather than
|
||||
argued. A bookmark states a PAGE and a y position; a candidate needs a LINE
|
||||
index. On the 701-page document this was built against, 2 706 of 2 761
|
||||
bookmarks share a destination page with another bookmark, so the page alone
|
||||
index. On the document this was built against, N - 55 of N
|
||||
bookmarks (N its declared sections) share a destination page with another bookmark, so the page alone
|
||||
is never a cut point.
|
||||
|
||||
Y ROUTE (primary). `page.extract_text_lines()` carries a `top` per line,
|
||||
and the mark takes the FIRST line at or below the destination. It needs
|
||||
the line splitting to be the one `page.extract_text()` produced -- an
|
||||
assumption, so it is CHECKED per page and the route is used only where
|
||||
the two strings are identical. Measured: 701 of 701 pages, and the
|
||||
resulting index agrees with the title route on 2 762 of 2 762 nodes,
|
||||
the two strings are identical. Measured: every page, and the
|
||||
resulting index agrees with the title route on every node (N + 1),
|
||||
flat from a 0 pt tolerance to 8 pt and collapsing at 12 (the line
|
||||
spacing). It therefore ships with NO tolerance constant at all.
|
||||
|
||||
TITLE ROUTE (fallback). The bookmark's title, normalised, searched in the
|
||||
destination page's own lines. It resolved 2 762 of 2 763 on that
|
||||
destination page's own lines. It resolved N + 1 of N + 2 on that
|
||||
document, and its weakness is real: a title like `Armering` occurs nine
|
||||
times in that structure, so it is scoped to the destination page and is
|
||||
never asked a question the y route already answered.
|
||||
|
|
@ -2149,7 +2148,7 @@ def pdf_outline(
|
|||
wanted.setdefault(page_number, []).append((int(level), str(title), top))
|
||||
# Geometry is read only for the pages that carry a bookmark, because
|
||||
# `extract_text_lines` costs a second render of every page it is asked
|
||||
# about -- 78 s over 701 pages, and nothing at all over the pages no
|
||||
# about -- 78 s over the whole document, and nothing at all over the pages no
|
||||
# bookmark points at.
|
||||
for page in pdf.pages:
|
||||
number = page.page_number
|
||||
|
|
@ -2330,7 +2329,7 @@ def _rewrite_media_links(text: str, media: dict[str, bytes], collector: _AssetCo
|
|||
UNCONDITIONAL, including the links that cannot be resolved. The converter
|
||||
already emitted a markdown image before this existed -- measured on a
|
||||
hand-laid `.docx`, today's output carries
|
||||
`` with no such file anywhere, which
|
||||
`` with no such file anywhere, which
|
||||
`structure._scan_references` reads as a cross-reference to a concept that
|
||||
cannot exist. Leaving an unresolvable link in place would keep that defect
|
||||
and add a temporary directory name to it.
|
||||
|
|
@ -2343,7 +2342,7 @@ def _rewrite_media_links(text: str, media: dict[str, bytes], collector: _AssetCo
|
|||
if data is not None:
|
||||
# The name the CONTAINER gave it, not the staging path: pandoc
|
||||
# preserves the part name under its own media directory, so
|
||||
# `word/media/tabell-84-2.png` arrives as `media/tabell-84-2.png`.
|
||||
# `word/media/tabell-7-2.png` arrives as `media/tabell-7-2.png`.
|
||||
inside = target.split("/extracted/", 1)[-1]
|
||||
return collector.carry(data, name=inside, label=label)
|
||||
if not target:
|
||||
|
|
|
|||
|
|
@ -220,8 +220,8 @@ def _render_sources(sources: Sequence[Source]) -> str:
|
|||
- **FELL 2026-09-12 (K3-24).** `parse_frontmatter` used to skip indented
|
||||
lines unconditionally, so a block list round-tripped to an EMPTY value
|
||||
with every entry gone, silently. It now decodes a block `sources`
|
||||
sequence, measured against PyYAML 6.0.3 and the pinned guard on 4 609 of
|
||||
4 609 concept files carrying one. A reason that has become false is worse
|
||||
sequence, measured against PyYAML 6.0.3 and the pinned guard on every concept
|
||||
file of four bundles carrying one. A reason that has become false is worse
|
||||
than no reason, so it is struck here rather than left standing.
|
||||
- **STANDS.** The consumer B6 was written for accepts `[{ k: v }, { k: v }]`
|
||||
— plural — and classifies a block sequence as unreadable provenance.
|
||||
|
|
@ -230,7 +230,7 @@ def _render_sources(sources: Sequence[Source]) -> str:
|
|||
for a block value. Block would hand the consumer that asked for
|
||||
multi-source exactly the state it reports as unreadable. This reason
|
||||
alone carries the emission rule, and it is NOT the guard's: guard 1.4.0
|
||||
reads the block form on 4 609 of 4 609 of the same files.
|
||||
reads the block form on every one of the same files.
|
||||
- **FELL with the first.** B6's own acceptance test asks for a round trip
|
||||
through this parser, and a block form now passes it
|
||||
(`test_the_block_form_round_trips_through_the_flat_reader`).
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ _FLOW_KEY = re.compile(r"^[A-Za-z_][A-Za-z0-9_-]*$")
|
|||
def yaml_block_plain(value: str) -> bool:
|
||||
"""Whether `value` reads back verbatim as a plain scalar in a block mapping.
|
||||
|
||||
MEASURED ON A 701-PAGE REFERENCE STANDARD (K3-19): 217 of 2 024 first spec points carry `": "`, and
|
||||
MEASURED ON A LONG REFERENCE STANDARD (K3-19): 217 of 2 024 first spec points carry `": "`, and
|
||||
PyYAML's `safe_load` refused exactly those 217 concepts' frontmatter. K3-22
|
||||
measured the same rule over every top-level value in eleven trees (the K2
|
||||
default bundle, that standard's HTML and XML builds, a five-document project, this
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ RULE_PDF_OUTLINE = "rule:pdf-outline"
|
|||
#: for -- an ATX line reaching the proposer says nothing about who wrote it, so
|
||||
#: a `<sec><title>` and a heading a converter guessed out of a font size were
|
||||
#: indistinguishable in the artifact and were judged by the same two steps.
|
||||
#: Measured on a 701-page reference standard: the orphan check removed 710 of 2 761 declared sections
|
||||
#: Measured on a long reference standard: the orphan check removed 710 of its N declared sections
|
||||
#: (710 of 710 removed are followed immediately by another heading -- they are
|
||||
#: containers) and Arm F folded 2 066 more, 2 089 -> 23 at shipped defaults.
|
||||
RULE_XML_SECTION = "rule:xml-section"
|
||||
|
|
@ -699,19 +699,19 @@ def _sheet_section_rows(lines: list[str]) -> dict[int, tuple[str, str]]:
|
|||
#: its content in its own cells, and the two DECLARED-structure rules because
|
||||
#: 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. Measured on one 701-page reference standard: 683 of 2 762 bookmark nodes
|
||||
#: and 710 of 2 761 STS sections are containers.
|
||||
#: if asked. Measured on one long reference standard with N declared sections: 683 of
|
||||
#: N + 1 bookmark nodes and 710 of N STS sections are containers.
|
||||
_ORPHAN_EXEMPT = (RULE_SHEET_SECTION, RULE_PDF_OUTLINE, RULE_XML_SECTION)
|
||||
|
||||
|
||||
def _split_outline_title(title: str) -> tuple[str | None, str]:
|
||||
"""`("14.121", "Langsg\u00e5ende sikring T1")` -- the number becomes the directory.
|
||||
"""`("3.121", "Langtidshevet grovbr\u00f8d")` -- the number becomes the directory.
|
||||
|
||||
Two grammars, both already in this module and neither invented here:
|
||||
`_NUMBERED` for a dotted section number, then `_OUTLINE`'s bare one- or
|
||||
two-digit form. The second matters because a document's TOP level is where
|
||||
the dot has not appeared yet -- on the corpus this arm was measured
|
||||
against, 59 of 2 761 titled sections carry a dotless token and all 28 of
|
||||
against, 59 of its N titled sections carry a dotless token and all 28 of
|
||||
its chapter-level sections are among them. Without it the whole top level
|
||||
would land with no section number at all, in a bundle whose every other
|
||||
level has one.
|
||||
|
|
@ -829,8 +829,9 @@ def find_candidates(
|
|||
first line, which is the right question for a heading a heuristic GUESSED
|
||||
and the wrong one for a section a publisher DECLARED: a chapter followed
|
||||
immediately by its first subsection is a container, not a false positive.
|
||||
Measured on a 701-page reference standard: 683 of 2 762 marks are containers, and
|
||||
applying the check scores 2 079 of 2 761 boundaries instead of 2 762.
|
||||
Measured on a long reference standard with N declared sections: 683 of N + 1
|
||||
marks are containers, and applying the check scores 2 079 of N boundaries
|
||||
instead of N + 1.
|
||||
|
||||
`sheet_section_rows` is D3's gate and it is OFF at False, where the scan is
|
||||
not run at all. On, a RUN of numbered rows inside an open table block cuts
|
||||
|
|
@ -1562,7 +1563,7 @@ def _link_shells(entries: list[dict[str, Any]], levels: Sequence[int], text: str
|
|||
A reference standard states its lettered points once, on the section that owns
|
||||
them, and every section nested below inherits them. Built faithfully, the
|
||||
nested section is a concept whose body is its heading line and nothing
|
||||
else -- measured on one 2 761-concept standard, **710** of them -- and the
|
||||
else -- measured on one standard of a few thousand concepts, **710** of them -- and the
|
||||
bundle's directory tree is two levels deep, so the path does not name the
|
||||
parent either. `parent_id` is the plan's existing relation: validated
|
||||
against the plan's own ids and written by the door as `parent:`, naming the
|
||||
|
|
|
|||
|
|
@ -21,13 +21,13 @@ bundle alone: no fasit, no model call, no clock, no network. That bounds it
|
|||
sharply, and the bound is measured rather than assumed.
|
||||
`docs/2026-09-12-g37-terskler.md` SS 4 records three candidates measured over
|
||||
the same four bundles and what became of each: duplicate titles WITHIN a
|
||||
document (0 of 3 206 on the known-bad arm against 349 of 2 761 on the known-good
|
||||
one -- the wrong direction) and the share of very short concepts (5.6 % against
|
||||
document (none on the known-bad arm against 13 % of the concepts on the
|
||||
known-good one -- the wrong direction) and the share of very short concepts (5.6 % against
|
||||
14.6 % -- also the wrong direction) are not shipped; duplicate titles across the
|
||||
WHOLE bundle order the four bundles correctly (37.8 / 16.3 / 12.6 / 5.7 %) and
|
||||
are still not shipped, because a bar separating them would have to be placed
|
||||
between the two bundles that define it, which is fitting the bar to the number.
|
||||
The defect that started this work -- 1 148 of 2 761 declared boundaries
|
||||
The defect that started this work -- 41.6 % of the declared boundaries
|
||||
recovered -- needs a fasit and no bundle-only metric reaches it.
|
||||
"""
|
||||
|
||||
|
|
@ -156,7 +156,7 @@ class FasitError(ValueError):
|
|||
|
||||
_WHITESPACE = re.compile(r"\s+")
|
||||
|
||||
#: The numbering token STS glues onto the front of a `<title>` ("11.1Fastmerker").
|
||||
#: The numbering token STS glues onto the front of a `<title>` ("11.1Grunndeiger").
|
||||
_NUMBERING_TOKEN = re.compile(r"^\s*(\d+(?:\.\d+)*)\s*")
|
||||
|
||||
|
||||
|
|
@ -164,8 +164,8 @@ def normalise_title(value: str) -> str:
|
|||
"""Strip ALL whitespace, then lowercase -- the key the fasit is written on.
|
||||
|
||||
Not a guess and not this module's invention: measured over the shipped
|
||||
2 761-row fasit before any of this was written, the rule reproduces every
|
||||
row's own `norm` from its own `title`, **2 761 of 2 761**. It is also the
|
||||
fasit before any of this was written, the rule reproduces every row's own
|
||||
`norm` from its own `title`, **N of N**. It is also the
|
||||
normalisation a downstream consumer's measuring script applies, so a number
|
||||
produced here and a number produced there are the same number.
|
||||
"""
|
||||
|
|
@ -173,7 +173,7 @@ def normalise_title(value: str) -> str:
|
|||
|
||||
|
||||
def _split_numbering(title: str) -> tuple[str, str]:
|
||||
"""`("11.1", "Fastmerker")`, or `("", title)` when there is no token."""
|
||||
"""`("11.1", "Grunndeiger")`, or `("", title)` when there is no token."""
|
||||
match = _NUMBERING_TOKEN.match(title)
|
||||
return (match.group(1), title[match.end() :].strip()) if match else ("", title.strip())
|
||||
|
||||
|
|
@ -248,21 +248,21 @@ class BoundaryThreshold:
|
|||
|
||||
|
||||
#: Measured 2026-09-13 on one pinned reference build: the declared-structure
|
||||
#: (`.xml`) arm of a 701-page reference standard, against that publisher's own
|
||||
#: 2 761 titled `<sec>` elements. Set at the value measured
|
||||
#: there rather than at a rounder number nearby, exactly like the two bars
|
||||
#: above -- a REGRESSION bar against a pinned artifact, and a tight one:
|
||||
#: `docs/2026-09-12-g37-terskler.md` SS 7 records that an older build of the
|
||||
#: same product (2 752 of 2 761) reads FAIL under it, and says so rather than
|
||||
#: moving the bar to admit it.
|
||||
#: (`.xml`) arm of a long reference standard, against that publisher's own N
|
||||
#: titled `<sec>` elements, where the arm recovers all but two. The bar is
|
||||
#: 99.9 %, a round share just under that measured value: it was the measured
|
||||
#: fraction itself, which named the document it was read off, and the round
|
||||
#: share gives every build measured the verdict it had -- the known-good arm
|
||||
#: passes, and `docs/2026-09-12-g37-terskler.md` SS 7's older build of the same
|
||||
#: product (99.7 %) still reads FAIL, and says so rather than moving the bar to
|
||||
#: admit it. A REGRESSION bar against a pinned artifact, and a tight one.
|
||||
BOUNDARY_THRESHOLD = BoundaryThreshold(
|
||||
metric="boundary_share",
|
||||
limit_recovered=2759,
|
||||
limit_declared=2761,
|
||||
limit_recovered=999,
|
||||
limit_declared=1000,
|
||||
corpora=1,
|
||||
source=(
|
||||
"one pinned reference build against its own declared structure "
|
||||
"(2 761 declared STS sections) -- ONE product, N = 1 corpus"
|
||||
"one pinned reference build against its own declared structure -- ONE product, N = 1 corpus"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -325,13 +325,13 @@ class BoundaryReport:
|
|||
"""How many boundaries the source declares became a concept, and by which form.
|
||||
|
||||
**Whole bundle, never per file type.** The fasit names the sections of ONE
|
||||
document; in a bundle those can be spread over 828 source files (they are,
|
||||
on the arm this metric was built to fell), so attributing the share to a
|
||||
document; in a bundle those can be spread over hundreds of source files
|
||||
(they are, on the arm this metric was built to fell), so attributing the share to a
|
||||
file type would put a product's number in a type's row.
|
||||
|
||||
Both match forms are counted separately and printed, because the
|
||||
decomposition is the finding: on the known-good arm the literal form alone
|
||||
reaches 22 of 2 761 and the pair form 2 737, so a gate scoring only the
|
||||
reaches 22 of N and the pair form N − 24, so a gate scoring only the
|
||||
first would report a 99.9 % arm as 0.8 % and call it a segmentation defect.
|
||||
"""
|
||||
|
||||
|
|
@ -482,8 +482,8 @@ def measure_bundle(
|
|||
Reached through the index tree and never `rglob`: the index is the bundle's
|
||||
own statement of what it contains, and `consume.enumerate_concepts` is the
|
||||
one walker in this library that reads it. Controlled 2026-09-12 against the
|
||||
directory listing on four bundles -- 453, 2 761, 3 206 and 446 concepts
|
||||
either way.
|
||||
directory listing on four bundles, from a few hundred to a few thousand
|
||||
concepts, the same count either way.
|
||||
"""
|
||||
root_bundle_id = root_bundle_id_of(bundle_root, profile=profile)
|
||||
concepts_per_extension: Counter[str] = Counter()
|
||||
|
|
@ -546,8 +546,8 @@ def _boundary_verdict(
|
|||
Both forms are needed and neither is a fallback for a defect in the other:
|
||||
the literal form wants the declared title WITH its numbering token, the pair
|
||||
form wants it WITHOUT, and no bundle can offer both. Scoring one alone
|
||||
reports the other route's segmentation as near zero -- measured, 22 of 2 761
|
||||
against 2 737 of 2 761 on the same arm.
|
||||
reports the other route's segmentation as near zero -- measured, 22 of N
|
||||
against N − 24 of N on the same arm.
|
||||
"""
|
||||
declared = len(fasit)
|
||||
literal = sum(1 for row in fasit if row.norm in titles)
|
||||
|
|
|
|||
|
|
@ -242,7 +242,7 @@ def _scan_references(body: str, offset: int, own_number: str | None) -> tuple[st
|
|||
# the index as an edge to a concept that cannot exist -- the same shape
|
||||
# as K3-21's `Enclosing section:` line becoming a second, unresolved
|
||||
# edge. Masked and then skipped, never merely skipped: the file name
|
||||
# carries digits (`tabell-84-2.png`) that the number scan below would
|
||||
# carries digits (`tabell-7-2.png`) that the number scan below would
|
||||
# otherwise read as a document this one refers to.
|
||||
if match.start() > 0 and body[match.start() - 1] == "!":
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -277,7 +277,7 @@ def test_the_threshold_document_carries_the_boundary_bar_and_its_single_corpus()
|
|||
from llm_ingestion_okf.quality import BOUNDARY_THRESHOLD
|
||||
|
||||
text = THRESHOLD_DOCUMENT.read_text(encoding="utf-8")
|
||||
assert f"`{BOUNDARY_THRESHOLD.metric}` | **2 759/2 761**" in text
|
||||
assert f"`{BOUNDARY_THRESHOLD.metric}` | **{BOUNDARY_THRESHOLD.as_share()}**" in text
|
||||
assert "1 corpus" in text
|
||||
assert BOUNDARY_THRESHOLD.corpora == 1
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue