Round 6 left two things behind: a measured coverage hole with the repair built
but not the number to move it, and a retrieval regression that kept two
spreadsheet rules off the default. Both are measured; four defaults move.
THE HOLE IS BIGGER THAN THE RULE BUILT FOR IT. Measured over the 39-document
corpus, the pre-move default left 207 435 characters -- 11.92 % -- in no
segment: 163 804 above the first entry, 26 041 between entries, 17 590 after
the last. `--first-span-from-zero` closes the first part entirely, 79 % of the
whole, leaving 43 631 (2.51 %) over 8 of 32 documents. It adds no boundary and
the K2 concept count is identical with and without it (425 = 425); hit@8 holds
[1,1,1,1,1,-] under both tie-breaks and the 12-position reference does not move
one cell.
THE RANKER WAS PUNISHING FINE-GRAINING FOR BEING FINE-GRAINED. Round 6 held
`--sheet-section-rows --keep-table-heading` back because on a bundle built with
them row 1 fell rank 1 -> 2, and ruled out the document prior because its rank
over 39 DOCUMENTS was 1 on both bundles. RRF reads a rank over CONCEPTS:
splitting the gold document 1 -> 12 puts its own twelve concepts in that
signal's whole top tie group, so the one leading the body signal takes position
11, contributing 1/71 where the undivided concept contributed 1/61. A signal
that scored them all EQUALLY still emitted twelve ranks, ordered by concept_id,
and the fusion read alphabetical order as a measurement.
AND THE REPAIR WAS ALREADY IN THE TREE, OFF FOR A COST THAT HAD BEEN REMOVED.
`--tie-shared-rank` shipped off 2026-09-08 because hit@8 fell 5 of 6 to 4 of 6.
Swept over 2 prior exponents x 3 bundles x 6 rows: that fall exists only at
DOCUMENT_PRIOR_EXPONENT 1.0. Round 6 moved the exponent to 0.5 for an unrelated
reason and correctly reported it moved no hit@8 row -- nobody measured the
pair. A flag's "off by measurement" is a measurement of a CONFIGURATION, not a
property of the flag.
Shipped, each with an explicit opt-out and both directions measured rather than
asserted: --no-first-span-from-zero --no-sheet-section-rows
--no-keep-table-heading reproduces the previous bundle byte for byte (diff -rq,
0 differences), and the no-flag build equals the explicit-flag build.
--no-tie-shared-rank reproduces the previous excerpt order -- the one change
here that alters a payload with no bundle changing.
K2 default: 436 concepts / 832 files, digest 8dff8a8e6c15d2f7..., hit@8 5 of 6
ranks [1,1,1,1,1,-]. The pin now holds its own known-negative on those bytes:
read with --no-tie-shared-rank the same bundle gives [2,1,1,1,1,-], so the
green assertion names its cause. Consumer cost is a re-run: 425/810 -> 436/832
on the reference corpus, 15/30 -> 26/52 on a five-document folder.
pptx and md measured end to end for the first time, on two hand-built
documents: md recovers 3 of 4 declared headings, pptx segments per slide only
where the converter recognised a title placeholder. A converter attribute leaks
into titles ({#slide-N}, {#sheet-1}) on 2 of 810 K2 files and 1 of 30 on a
five-document folder -- not fixed, because a filename is reduced from its title
and the fix renames concept ids a consumer has cited.
Suite 1486 (1478 before), ruff and mypy --strict clean over 21 files.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
304 lines
16 KiB
Markdown
304 lines
16 KiB
Markdown
# K3 round 7: the first span, and the fusion that punished fine-graining
|
||
|
||
Round 6 shipped the outline gate and left two things behind: a measured
|
||
coverage hole it had built the repair for but not the number to move it, and a
|
||
retrieval regression that kept two spreadsheet rules off the default. This
|
||
round measured both. Three defaults moved and one file type turned out never to
|
||
have been measured at all.
|
||
|
||
Everything here is measured on this repository at `38104b7` plus this round's
|
||
changes, with `~/repos/llm-ingestion-okf/.venv/bin/python`, against the cached
|
||
rendering `/tmp/k3r3/text43` (39 readable documents) and the criterion in
|
||
`/tmp/k3r5/score5.py` imported unchanged. Bundle digests are
|
||
`find . -type f | sort | xargs shasum -a 256 | shasum -a 256` from inside the
|
||
bundle, so they are path-independent.
|
||
|
||
## 0. One divergence from the numbers this round was handed
|
||
|
||
The order states the coverage loss as **159 704 characters, 9.18 % of the
|
||
corpus**. Measured here with the shipped default over the same 39 documents,
|
||
the text above every plan's first entry is **163 804 characters, 9.42 %** — a
|
||
difference of 4 100 characters, 2.5 % of the figure. Both agree on the
|
||
denominator (39 documents, 32 with a plan) and on the shape of the finding.
|
||
The method used here is in § 1 and the 4 100 are not chased further; where the
|
||
two disagree the number in this report is the one measured today.
|
||
|
||
The same measurement also found more of the hole than the figure it was handed
|
||
covers, which is § 1.
|
||
|
||
## 1. Coverage: the hole is bigger than the first span, and the first span was 79 % of it
|
||
|
||
Characters lying in NO segment, over the 39-document corpus (1 739 511
|
||
characters, 32 documents with a plan):
|
||
|
||
| configuration | in no segment | share | documents | above first | between | after last |
|
||
|---|---|---|---|---|---|---|
|
||
| pre-round-7 default | 207 435 | **11.92 %** | 32 of 32 | 163 804 | 26 041 | 17 590 |
|
||
| + `--first-span-from-zero` | 43 631 | 2.51 % | 8 of 32 | **0** | 26 041 | 17 590 |
|
||
| round 7 default (all three) | 43 631 | **2.51 %** | 8 of 32 | 0 | 26 041 | 17 590 |
|
||
|
||
`--first-span-from-zero` closes **163 804 of 207 435**, 79 % of the hole, and
|
||
it closes the whole of the part that hits every document. What remains is
|
||
**43 631 characters, 2.51 % of the corpus, over 8 of 32 documents**, in two
|
||
named parts that are NOT the same defect and are not fixed here:
|
||
|
||
* **26 041 characters between one entry's end and the next one's start.** The
|
||
mechanism is § 4's: a `rule:table-block` candidate opens BELOW the heading
|
||
line that introduces it, so the heading's own line falls between two spans.
|
||
* **17 590 characters after the last entry ends.** Not investigated this round.
|
||
|
||
Both are written into STATE with their mechanism. Neither is a ceiling; each is
|
||
a measurement with a denominator and a named cause.
|
||
|
||
### 1.1 What it cost to move it
|
||
|
||
`--first-span-from-zero` adds no boundary, and the measurement says so twice
|
||
over. On the 12-position reference it changes **not one cell** — `pdf` 7 of 8,
|
||
`docx` 3 of 3, `xlsx` 0 of 1, sheet 10 of 12, identical to the default without
|
||
it, per position and per label. On a K2 bundle built with it: **425 concepts /
|
||
810 files**, the same count as the default without it, and hit@8 `[1, 1, 1, 1,
|
||
1, None]` = 5 of 6 — **under both tie-breaks**, so its acceptance does not
|
||
borrow from § 2's move.
|
||
|
||
## 2. The fusion punished fine-graining, and the repair was already in the tree
|
||
|
||
### 2.1 The decomposition
|
||
|
||
Round 6 measured that a K2 bundle built with `--sheet-section-rows
|
||
--keep-table-heading` takes row 1 of the hit@8 set from rank 1 to rank 2, and
|
||
held the two rules back for it. It also measured that the DOCUMENT-prior rank
|
||
is 1 on both bundles, which ruled the prior out — correctly, and that is why
|
||
the cause stayed unfound: the prior's rank over 39 DOCUMENTS is not the prior
|
||
signal's rank over 425 CONCEPTS, and RRF reads the second.
|
||
|
||
Per-signal, row 1, gold concept against the concept that takes fused rank 1:
|
||
|
||
| bundle | gold doc's units | gold: title / body / prior rank | competitor: title / body / prior | fused |
|
||
|---|---|---|---|---|
|
||
| default (425) | 1 | 6 / 4 / **1** | — (gold IS rank 1) | gold 1 |
|
||
| D1+D3 (436) | 12 | 16 / 4 / **11** | 5 / 9 / 13 | gold **2** |
|
||
|
||
The prior signal on the split bundle scores all twelve of the gold document's
|
||
concepts identically (2.8735) and they take positions **1 through 12**. The
|
||
one that leads the body signal lands at position 11 and contributes `1/71`
|
||
where the undivided concept contributed `1/61`. The competitor is a
|
||
one-concept document that leads nothing and is beaten in every signal it can
|
||
be compared on — it wins because its evidence is in one unit and the gold's is
|
||
spread over twelve, and because the gold's own siblings fill the tie group
|
||
ahead of it.
|
||
|
||
So the order's hypothesis is right in its conclusion and needed one correction
|
||
in its mechanism: it is not that twelve concepts are each individually weaker.
|
||
It is that a signal which scores them all EQUALLY still emits twelve distinct
|
||
ranks, ordered by `concept_id`, and the fusion reads that alphabetical order as
|
||
if it were a measurement.
|
||
|
||
### 2.2 That rule already existed and was off for a cost that had been removed
|
||
|
||
`--tie-shared-rank` — shipped 2026-09-08, off by default — is exactly this
|
||
repair. Its published reason for being off is that hit@8 fell 5 of 6 to 4 of 6.
|
||
**That figure is real and it is conditional.** Swept over the prior exponent,
|
||
3 bundles x 6 rows:
|
||
|
||
| exponent | bundle | `tie_shared_rank` off | on |
|
||
|---|---|---|---|
|
||
| 1.0 | Arm B (629) | `1,1,1,1,1,–` 5/6 | `1,–,1,1,1,–` **4/6** |
|
||
| 1.0 | default (425) | `1,1,1,1,1,–` 5/6 | `1,1,1,1,1,–` 5/6 |
|
||
| 1.0 | D1+D3 (436) | `2,1,1,1,1,–` 5/6 | `1,1,1,1,1,–` 5/6 |
|
||
| **0.5** | Arm B (629) | `1,1,1,1,1,–` 5/6 | `1,1,1,1,1,–` **5/6** |
|
||
| **0.5** | default (425) | `1,1,1,1,1,–` 5/6 | `1,1,1,1,1,–` 5/6 |
|
||
| **0.5** | D1+D3 (436) | `2,1,1,1,1,–` 5/6 | `1,1,1,1,1,–` 5/6 |
|
||
|
||
Round 6 moved `DOCUMENT_PRIOR_EXPONENT` from 1.0 to 0.5 for an unrelated
|
||
reason and reported, correctly, that it moved no hit@8 row end to end. What
|
||
nobody measured is the PAIR: the exponent move removed the only cost keeping
|
||
`--tie-shared-rank` off. A rule sat behind a published number that had stopped
|
||
being true in the same commit that was reported as changing nothing.
|
||
|
||
**The lesson is the durable one from this round**: a flag's "off by
|
||
measurement" is a measurement of a configuration, not a property of the flag.
|
||
When a constant that flag interacts with moves, its default is unmeasured
|
||
again — and nothing in the tree said so, because the two decisions live in
|
||
different files.
|
||
|
||
### 2.3 What shipped, and the control in the same measurement
|
||
|
||
`consume.DEFAULT_TIE_SHARED_RANK = True`, opt-out `--no-tie-shared-rank`. With
|
||
it, all three of the round's acceptance conditions hold at once, and the
|
||
build-side rules could move:
|
||
|
||
| bundle | flags over the pre-round-7 default | concepts | files | digest | hit@8 | ranks |
|
||
|---|---|---|---|---|---|---|
|
||
| pre-round-7 default | — | 425 | 810 | `bdf4977ca5a443c4…` | 5 of 6 | 1,1,1,1,1,– |
|
||
| + first span | `--first-span-from-zero` | 425 | 810 | — | 5 of 6 | 1,1,1,1,1,– |
|
||
| **round 7 default** | the above `--sheet-section-rows --keep-table-heading` | **436** | **832** | `8dff8a8e6c15d2f7…` | **5 of 6** | **1,1,1,1,1,–** |
|
||
| round 7 default, `--no-tie-shared-rank` | (same bytes, read differently) | 436 | 832 | same | 5 of 6 | **2**,1,1,1,1,– |
|
||
|
||
The last row is the known-negative and it is on the SHIPPED bytes rather than a
|
||
fixture: the regression round 6 refused to ship is reproduced on the bundle
|
||
that now ships, by turning off the one thing that removes it.
|
||
`tests/test_default_bundle_pin.py` holds both rows.
|
||
|
||
## 3. The reference sheet, and the two readings of `xlsx`
|
||
|
||
Reproduced before any code moved, criterion unchanged:
|
||
|
||
| configuration | `pdf` | `docx` | `xlsx` | sheet |
|
||
|---|---|---|---|---|
|
||
| pre-round-7 default | 7 of 8 | 3 of 3 | 0 of 1 | 10 of 12 |
|
||
| + `--first-span-from-zero` | 7 of 8 | 3 of 3 | 0 of 1 | 10 of 12 (not one cell moves) |
|
||
| + `--sheet-section-rows --keep-table-heading`, `|F|` = 11 | 7 of 8 | 3 of 3 | 0 of 1 | 10 of 12 |
|
||
| + the same, `|F|` = 12 | 7 of 8 | 3 of 3 | **1 of 1** | **11 of 12** |
|
||
|
||
Per position on the default, all at distance 0: pos 1 `treffer`/15, pos 2 /9,
|
||
pos 4 /1, pos 6 /6, pos 7 /30, pos 8 /8, pos 9 /4, pos 5, 10, 11 `docx`
|
||
`treffer`. Pos 0 is `for grovt` with U=1 and `|F|` undefined. Pos 3 is
|
||
`for grovt` with U=0, S=1.
|
||
|
||
**D-2 is unanswered and this report does not answer it.** `F[3]` was not
|
||
touched in the criterion. What the measurement adds is that the choice between
|
||
the two readings does not decide whether the rules ship: under `|F|` = 11 the
|
||
sheet rules leave the sheet at 10 of 12 and take position 3's distance from
|
||
**12 to 1** (U=12 against 11, `for fint`); under `|F|` = 12 they take it to
|
||
`treffer`. Neither reading is worse off with them on.
|
||
|
||
## 4. `pptx` and `md`: the mandate's weakest axis, now measured
|
||
|
||
`extract.py:76` marks `.pptx` `unmeasured` and the 12-position reference is 8
|
||
`pdf`, 3 `docx`, 1 `xlsx` — no `pptx`, no `md`. Two documents with a
|
||
hand-known structure were built for this (a markdown file with 1 + 3 declared
|
||
headings, and the same content written to `.pptx` through the converter). The
|
||
fasit is NOT invented: each document's structure is what its author wrote, and
|
||
the question asked is only whether the default recovers it.
|
||
|
||
**`pptx` extracts, and its segmentation depends on a property of the deck.**
|
||
Two decks, both extracted with rc 0 and no rejection code:
|
||
|
||
| deck | structure | concepts | matches |
|
||
|---|---|---|---|
|
||
| section slide + 3 content slides | 4 slides | **1** | no |
|
||
| 4 title slides | 4 slides | **4** | yes |
|
||
|
||
The mechanism: the converter emits one ATX heading per slide **that carries a
|
||
title placeholder it recognised**, and plain body lines for the rest. In deck 1
|
||
only slide 1 got a heading, so the document DECLARES one heading — and the
|
||
outline gate, correctly by its own rule, then admits none of Arm D's recovered
|
||
ones. The whole deck lands as one concept. This is round 6's principle working
|
||
exactly as designed on an input its measurement never contained.
|
||
|
||
**`md` extracts and loses one of four declared headings**: 3 concepts against
|
||
the 4 the document declares. Decomposed below.
|
||
|
||
**A converter attribute leaks into concept titles, and it is not a `pptx`
|
||
curiosity.** Every `pptx` concept measured carries `{#slide-N}` in its `title`,
|
||
and on real corpora the same leak appears as `{#sheet-1}`: **2 of 810** files
|
||
on the previous default K2 bundle, **2 of 1108** on Arm B, and **1 of 30** on
|
||
the operator's own test folder. Because a concept's FILENAME is reduced from
|
||
its title, the leak is in a concept id a consumer has already cited by name.
|
||
Not fixed here — see § 6.
|
||
|
||
## 5. § 4's `md` defect is § 1's remaining 26 041 characters
|
||
|
||
The markdown document declares `# Prosjektbeskrivelse` and `## 1`, `## 2`,
|
||
`## 3`. Candidates, decomposed:
|
||
|
||
* with `--no-unit-fold`: four candidates, and the fourth is
|
||
`rule:table-block` starting at 503 — **not** `rule:heading` at 489. The
|
||
heading line `## 3 Prising` is 14 characters that belong to no segment.
|
||
* with the fold on (the default): three candidates. The table block, having no
|
||
heading of its own, is folded into section 2, whose span now runs to the end
|
||
of the file. The boundary is gone.
|
||
|
||
The root cause is in the first step, not the fold: **a table-block candidate
|
||
displaces a heading the document DECLARED**, and opens below it. That is the
|
||
same principle round 6 established for Arm D against declared headings —
|
||
recovery yields to declaration — not yet applied between the table rule and the
|
||
heading rule.
|
||
|
||
Reach over the 39-document corpus, so this is a measurement and not an
|
||
anecdote: **5 of 39** documents carry a table-block candidate at all; the fold
|
||
changes the entry count on **17 of 39**, removing **198** entries; and with the
|
||
fold off, **18 527 characters over 15 of 39 documents** sit between one
|
||
candidate's end and the next one's start. Under the shipped default that
|
||
between-gap is **26 041 characters**.
|
||
|
||
Not fixed this round: it changes what the proposer emits on 5 of 39 documents,
|
||
so it needs its own K2 build and its own hit@8 read, which is one full arm's
|
||
worth of acceptance. It is in STATE with this mechanism and these numbers.
|
||
|
||
## 6. What did NOT move, and why
|
||
|
||
* **The `{#…}` leak.** The fix is small; the consequence is not. A concept's
|
||
filename is reduced from its title, so stripping the attribute RENAMES the
|
||
affected concepts — including one an external consumer has already cited by
|
||
its full id in writing. Renaming a concept id another repository names is not
|
||
this round's call to make; it is a question, with the exposure measured (2 of
|
||
810, 2 of 1108, 1 of 30).
|
||
* **`MIN_SHARED_PREFIX` and the prefix over-match.** Untouched, and
|
||
deliberately: § 2 changes the FUSION and the rarity weight changes a signal's
|
||
scores. Moving both would measure two changes with one number. The df figure
|
||
it rests on (159 of 270) is round 5's and is still not re-measured.
|
||
* **The remaining 17 590 characters after the last entry.** Named, not
|
||
investigated.
|
||
* **`--pdf-headings`, `--ocr`, `--rarity-weight`, `--cost-vocabulary`,
|
||
`--reserve-top-rank`, `--withheld-titles`, `--max-segment-chars`.** All
|
||
unchanged and all still off.
|
||
|
||
## 7. Cost to a consumer
|
||
|
||
A re-run, and it is not small. The 43-document reference corpus:
|
||
|
||
* **629 concepts / 1 108 files** (Arm B, delivered 2026-09-03)
|
||
* **492 / 944** after the 2026-09-08 move
|
||
* **425 / 810** after the 2026-09-09 move
|
||
* **436 / 832** after this one — digest `8dff8a8e6c15d2f7…`, default flags,
|
||
default epoch stamp.
|
||
|
||
On the operator's own five-document test folder the same move is **15 concepts
|
||
/ 30 files → 26 / 52**.
|
||
|
||
Every moved default has an explicit opt-out, and the opt-outs were measured
|
||
rather than asserted: `--no-first-span-from-zero --no-sheet-section-rows
|
||
--no-keep-table-heading` reproduces the pre-round-7 bundle **byte for byte**
|
||
(`diff -rq`, 0 differences), and `--no-tie-shared-rank` reproduces the previous
|
||
excerpt order.
|
||
|
||
The reading-side change is the one to flag hardest, because it has no bundle to
|
||
diff: `okf consume` now returns a different excerpt ORDER on any bundle with a
|
||
tied signal, without any bundle changing. `--no-tie-shared-rank` is the way
|
||
back.
|
||
|
||
## 8. Honesty limits
|
||
|
||
* **n = 12 positions on the reference sheet**, stratified 8 `pdf`, 3 `docx`,
|
||
1 `xlsx`. Every per-type fraction here is a fraction of 8, 3 or 1. A single
|
||
`xlsx` position carries the entire `xlsx` claim.
|
||
* **n = 6 questions on the retrieval side, one rater, one gold set.** Row 6
|
||
misses on every bundle and every configuration measured so far, so "5 of 6"
|
||
has always been "5 of 5 reachable rows plus one that no configuration has
|
||
reached".
|
||
* **Every bundle here is the SAME 43-document corpus read again.** "General"
|
||
in this report means "holds across configurations of one corpus", never
|
||
"holds across corpora". The `pptx`/`md` documents in § 4 are the only inputs
|
||
measured this round that are not from it, and they were written for it.
|
||
* **`pptx`, `md`, `odt` and `rtf` remain outside the reference sheet.** § 4
|
||
measures two hand-built documents, which is more than zero and is not a
|
||
fasit. `odt` and `rtf` are still `unmeasured` with no document behind them
|
||
at all.
|
||
* **`|F|` for position 0 is undefined and position 3's is unanswered.** Both
|
||
are operator decisions with the numbers already delivered; neither is
|
||
assumed here.
|
||
* **"Delivered" is not "answered".** hit@8 measures whether a concept from the
|
||
gold document reaches the payload, not whether the payload answers the
|
||
question. The last measurement of that, on 3 bundles, was rank 1 of 8 on 3 of
|
||
3 and a correct answer on 1 of 3.
|
||
* **The synthetic adverse case for § 2 is recorded, not hidden.** On a
|
||
30-concept fixture where one signal separates and two do not, shared ranks
|
||
move a gold from rank 18 to rank 30. It is in
|
||
`tests/test_okf_consume.py` with that number. Nothing like it appears in the
|
||
three real bundles, which is an absence over three corpora, not a proof.
|
||
* **`docs/2026-09-08-sjeldenhetsvekt.md`'s figures were measured under the
|
||
pre-2026-09-10 tie-break** and are not re-measured here. On one fixture the
|
||
change takes the weight's gold from fused rank 18 to rank 1, so at least one
|
||
of that report's numbers no longer describes the default.
|
||
* **The 4 100-character divergence in § 0 is unexplained.**
|