Every fixture, test document, tool example and document now uses an invented kitchen-and-baking handbook series, written in this repository. The package's behaviour is unchanged; src/ changes are comments and help text only. - Generated fixtures are regenerated from their generators. Their structural counts are identical before and after: elements, images, rows, cells, headings, bookmarks and the witness inventory's per-document totals. The image-inbox and accounting documents are renamed kapittel-84-*. - tools/okf_accounting_gate.py: the two options that named one real corpus each are replaced by a generic, repeatable --corpus PATH with no default. Row 5 compares the PDF pair alone. Gate verdict unchanged: RED rows 2, 3, 6. - tools/okf_witness.py: the STS JSON reader for one publisher's delivery is removed, along with its three twins and five tests. The mutation harness loses W09. - docs/: 13 dated reports that documented runs on a retired reference corpus are removed, and 40 are neutralized. Dead links are removed, and no new dangling path is introduced. - The synthetic MCP-gate corpus and the residual probe words are neutral. Valgt: keep the `okf quality --fasit` bar value (the measured fraction, one corpus) and rewrite only its provenance, because the verdict stays unchanged and the number names nothing. Term check with the local list: 0 of 411 tracked files, 0 file names, 0 of 27 binary fixtures. Suite after git add: 2457 passed, 1 skipped. The base tree had 2460 passed and 2 skipped; five tests went with the JSON reader and four were added by the term check. ruff, ruff format and mypy --strict src/ are clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
120 lines
5.7 KiB
Markdown
120 lines
5.7 KiB
Markdown
# What a derived `references` subject is
|
|
|
|
Record of the decision taken 2026-08-29 on order `…507632913`, and of the
|
|
measurement it rests on. Supersedes the reference half of
|
|
`docs/plan/structure-derivation.md`; nothing else in that document moves.
|
|
|
|
## The defect
|
|
|
|
`STRUCTURED_V1` derived `references` by taking every number-shaped token in a
|
|
document body. A consumer measured it on their own corpus and reported 12 false
|
|
references out of 12, ten of them version numbers carried in titles and read as
|
|
document numbers.
|
|
|
|
The order was not to inherit that figure. It was measured again here, on two
|
|
corpora this repository can reach, before anything was changed.
|
|
|
|
## Measured before the change
|
|
|
|
Both figures come from `derive_document_structure` itself, run over every
|
|
unique document (deduplicated by sha256) and counting the subjects it returns
|
|
with `references` marked derived.
|
|
|
|
| corpus | documents | documents with derived `references` | subjects |
|
|
|---|---|---|---|
|
|
| A — a consumer's normative bundle set, six bundles, Norwegian | 2 561 | 1 010 | 2 838 |
|
|
| B — this repository's own `docs/`, English and Norwegian technical prose | 28 | 27 | 559 |
|
|
|
|
What those subjects were, classified against the position the scanner actually
|
|
lifted each one from:
|
|
|
|
- **Corpus A.** 702 of 2 838 (24.7 %) were hexadecimal fragments of UUIDs read
|
|
out of `id="…"` attribute values inside embedded HTML and MathML. The rest of
|
|
the false population was unit symbols (`kN/m2` reduced to `M2`), table row
|
|
and class labels, and formula numbers.
|
|
- **Corpus B.** Dominated by software versions (`v0.3.0`, `3.14.0`, `0.11.10`,
|
|
`1.28.2`), a licence identifier (`AGPL-3.0` → `3.0`), a container format
|
|
version (`%PDF-1.6` → `1.6`), a hash algorithm name (`SHA256`), and tokens
|
|
lifted out of two-character escape sequences inside quoted source (`\n60` →
|
|
`N60`).
|
|
|
|
The consumer's reported class reproduces. Two classes they did not report —
|
|
identifier fragments inside markup, and escape-sequence artefacts — are larger.
|
|
|
|
## The decision
|
|
|
|
**A derived reference subject is one of exactly two things: a bundle-local
|
|
markdown link target, or a number immediately preceded by a reference cue from
|
|
a closed set.** Everything else number-shaped in body prose is not a reference.
|
|
|
|
The rule is positive rather than a blacklist, and that is forced by the data,
|
|
not chosen for taste: corpus A proves `W221` and `W240` are genuine document
|
|
numbers, corpus B proves `V0.3.0` is a software version, and the two are the
|
|
same token shape. Nothing structural separates them. Only the words in front of
|
|
them do, so only a positive cue can be defended.
|
|
|
|
The cue must match at a word boundary. Without that guard a Norwegian compound
|
|
ending in `-klasse` satisfies the cue `se`, which admitted 86 class
|
|
designations on corpus A. The window is normalised to NFC first, so a cue
|
|
carrying a Norwegian letter matches whether the file arrived composed or
|
|
decomposed.
|
|
|
|
Two link targets are also excluded, on the same ground rather than a new one: a
|
|
fragment-only target (`#anchor`) points inside the document it appears in, and
|
|
a target carrying a brace is a template placeholder from prose *about* links.
|
|
Neither can name a concept — `reduce_to_id_grammar` emits lowercase
|
|
alphanumerics and hyphens — so neither is a pointer awaiting resolution. It is
|
|
a claim that cannot come true.
|
|
|
|
## Measured after the change
|
|
|
|
Same script, same corpora, `HEAD` against the working tree.
|
|
|
|
| corpus | subjects before | subjects after | dropped |
|
|
|---|---|---|---|
|
|
| A | 2 838 | 1 279 | 1 559 (54.9 %) |
|
|
| B | 559 | 72 | 487 (87.1 %) |
|
|
|
|
Precision was checked by hand against the occurrence that actually passed the
|
|
gate, not the subject's first occurrence in the file — reading the first one
|
|
mis-attributes context and was a real error in an earlier pass of this
|
|
measurement.
|
|
|
|
- Corpus A: 30 subjects drawn at random from 1 279. **30 of 30 genuine.**
|
|
- Corpus B: all 60 cue-gated subjects. **60 of 60 genuine.**
|
|
|
|
**Residual known false positives: 9 of corpus B's 72**, all in the link
|
|
channel, all illustrative targets in prose about link syntax (`link`, `target`,
|
|
`/sti`, `subdir/`). They are indistinguishable from real relative paths without
|
|
guessing at path semantics, so they stay. Corpus A's link channel is empty, so
|
|
this residue is not visible there at all.
|
|
|
|
## The cost, stated rather than hidden
|
|
|
|
A corpus whose cross-references are phrased outside this vocabulary — a third
|
|
language, a house style — derives **nothing**, and must declare `references`
|
|
itself. That is the intended failure: a missing reference is visible to the
|
|
reader, a false one is not.
|
|
|
|
Two recall losses inside the corpora measured, both accepted: a reference whose
|
|
cue is a preposition (`i henhold til …` where the noun is absent) and one
|
|
written as a prefixed compound (`Q-W240`) are dropped.
|
|
|
|
## What was deliberately not done
|
|
|
|
**The prefix-resolution rule (`Q130` in a body not finding a document numbered
|
|
`Q130.2`) stays open.** The order allowed it in the same pass only if the data
|
|
bore it. It does not: of 2 589 documents across both corpora, 2 562 carry a
|
|
number and **0** of those numbers are dotted, so a unique-prefix match has
|
|
nothing here to be defended against. Closing two things badly is worse than
|
|
closing one well.
|
|
|
|
## Adjacent defect found, not fixed — needs its own order
|
|
|
|
The line-oriented frontmatter parser lets a **nested key overwrite a top-level
|
|
key of the same name**. A `sources:` block whose entry carries its own `title:`
|
|
replaces the document's `title`, and the `number` derived from that title
|
|
follows it. This is the same family as the known block-list finding but strictly
|
|
worse: that one drops a value silently, this one *substitutes* one. It changes
|
|
`title`, `number` and `parent`, so it is outside this order's scope, and it is
|
|
why corpus A's numbers above are what they are.
|