feat(propose,cli): the coverage gap had one cause, and round 7's own decomposition did not reproduce
Round 7 named two open items: a table-block candidate displacing a declared
heading (26 041 characters between entries, `md` at 3 of 4 declared headings)
and 17 590 characters after the last entry, never examined. Measured on
`a364ef4`, the first premise does not reproduce and the second is not a
separate mechanism.
`md` recovers 4 of 4, not 3 of 4. D1 (`--keep-table-heading`) is the repair
for exactly the mechanism round 7 § 5 describes, and round 7 moved it into
the default in the same commit -- so § 5 is a pre-move measurement presented
as the post-move state. That is round 7's own trap, stated in its own report:
a number about a rule is a measurement of a configuration. Nothing held the
cell, which is why it could be wrong and stay wrong.
The remaining gap has ONE cause. Every rule closes a span against the NEXT
MARK; three steps then remove a mark after its neighbour's `end` was fixed
against it. The orphan check leaks 18 527 characters over 15 of 39 documents,
`fold_units` clause 1 leaks 7 514 between entries, and the same clause on the
last run leaks all 17 590 tail characters -- with `unit_fold=False` the corpus
tail gap is 0. Round 6 already established the principle (filter at admission,
let the mark above carry the text) and it was never applied post-filter.
`--close-span-gaps` states it once, after the fold, where every removal has
happened: a span runs to the next SURVIVOR, the last to the end of the text.
It adds no boundary. Measured: 43 631 characters (2.51 %) -> 0, entries
429 = 429, K2 concepts 436 = 436, the operator's folder 52 md = 52.
All four acceptance conditions hold at once. The 12-position reference is
label-identical -- 11 of 12 under |F|[3]=12 and 10 of 12 under |F|[3]=11,
both readings reported, not one position moved. hit@8 holds [1,1,1,1,1,-] on
the round 8 bundle (436), the round 7 default (436) and Arm B (629), and the
known-negative reproduces on the new bytes: `--no-tie-shared-rank` gives
[2,1,1,1,1,-].
The pin is rebuilt against the default that actually ships
(`K2-bundle-default-20260911`, 832 files, digest 8c93e5e3...); two independent
builds of it differ in nothing, `log.md` included.
Two measurement failures are recorded rather than hidden. The corpus identity
check first used `xargs` without `-print0`, every filename split on its
spaces, `shasum` read nothing, and both sides agreed on a digest of the
failure; a control against the digest of empty input separated them. And the
smoke build cannot prove the flag arrives -- the operator's folder has a
coverage gap of zero already, so `diff -rq` is 0 either way; the plumbing has
its own test on a document that has the defect.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
a364ef4c7d
commit
956714594d
8 changed files with 592 additions and 22 deletions
224
docs/2026-09-11-k3-runde8-tabellblokk-og-siste-spenn.md
Normal file
224
docs/2026-09-11-k3-runde8-tabellblokk-og-siste-spenn.md
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
# K3 round 8: the coverage gap had one cause, and it was not the one named
|
||||
|
||||
Round 7 closed 79 % of the corpus coverage gap with `--first-span-from-zero`
|
||||
and named the remaining **43 631 characters, 2.51 %** as two open items: a
|
||||
table-block candidate displacing a declared heading (26 041 characters between
|
||||
entries, and the reason `md` was reported at 3 of 4 declared headings), and
|
||||
17 590 characters after the last entry that had never been examined.
|
||||
|
||||
Measured here, one of those two premises does not reproduce and the other is
|
||||
not a separate mechanism. The gap has **one** cause, the whole of it is now
|
||||
closed, and the reference sheet and every hit@8 row are unchanged.
|
||||
|
||||
Basis: `a364ef4` (round 7). Every number below was re-measured on that commit
|
||||
before anything was changed.
|
||||
|
||||
## 0. Two divergences from the numbers this round was handed
|
||||
|
||||
**The order's headline defect does not reproduce.** Round 7 § 5 reports that
|
||||
the default emits `rule:table-block` at 503 rather than `rule:heading` at 489
|
||||
for `## 3 Prising`, and that `md` therefore recovers 3 of 4 declared headings.
|
||||
On `a364ef4` — round 7's own commit, the document round 7 built, through the
|
||||
same extraction path — the default recovers **4 of 4**, and `okf build`
|
||||
emits all four as concepts.
|
||||
|
||||
The cause is round 7's own trap, stated in its own report and then walked
|
||||
into: **a number about a rule is a measurement of a configuration.** D1
|
||||
(`--keep-table-heading`) is precisely the repair for the mechanism § 5
|
||||
describes, and round 7 moved it into the default in the same commit. § 5 is a
|
||||
pre-move measurement presented as the post-move state. Isolated per flag on
|
||||
that document:
|
||||
|
||||
| configuration | declared headings recovered |
|
||||
|---|---|
|
||||
| default | **4 of 4** |
|
||||
| default, `--no-keep-table-heading` | 3 of 4 |
|
||||
| every other flag turned off individually | 4 of 4 |
|
||||
| all arms off (pre-arm baseline) | 3 of 4 |
|
||||
|
||||
Nothing in the tree held that cell, which is why it could be reported wrong
|
||||
and stay wrong. `tests/test_md_declared_headings.py` pins it now, with the
|
||||
known-negative in the same file: turn D1 off and `3 Prising` becomes a table
|
||||
block carrying a name from a line below itself.
|
||||
|
||||
**The two remaining halves are one mechanism, not two.** Decomposed below.
|
||||
|
||||
## 1. The gap has one cause: marks removed after spans were closed
|
||||
|
||||
Every rule in `find_candidates` closes a span against the NEXT MARK. Three
|
||||
steps then remove a mark after its neighbour's `end` was already fixed against
|
||||
it, and the removed mark's text is in no segment. Measured over the
|
||||
39-document corpus (1 739 511 characters, 32 documents with a plan):
|
||||
|
||||
| removing step | characters | where |
|
||||
|---|---|---|
|
||||
| orphan check (bodiless heading dropped) | **18 527** over 15 of 39 documents | between entries |
|
||||
| `fold_units` clause 1, contents run discarded | **7 514** | between entries |
|
||||
| `fold_units` clause 1, discarding the LAST run | **17 590** | after the last entry |
|
||||
| | **43 631 (2.51 %)** | over 8 of 32 documents |
|
||||
|
||||
The decomposition that settles it: with `unit_fold=False` the corpus tail gap
|
||||
is **0** and the between-gap is 18 527. So the entire tail is clause 1's, and
|
||||
the between-gap is the orphan check plus clause 1. The 26 041 and the 17 590
|
||||
are the same defect seen at two positions.
|
||||
|
||||
**This is round 6's own principle, not applied where it also holds.** The
|
||||
outline gate filters at ADMISSION, "before spans close, so the text a removed
|
||||
mark opened is carried by the mark above"; the post-filter form scored
|
||||
identically and lost that text. The orphan check and clause 1 are post-filter
|
||||
forms that were never converted.
|
||||
|
||||
### 1.1 What the between-gap is, and why the order's mechanism is not it
|
||||
|
||||
Clause 1's between-gap concentrates almost entirely in one document: of the
|
||||
26 041 characters, **23 259 sit in a single document** and the remaining 2 782
|
||||
are spread over seven. That document is a geotechnical borehole log whose data
|
||||
rows end in an integer, so `_TRAILING_PAGE_NUMBER` reads a run of them as a
|
||||
contents listing and discards it. Across the corpus, clause 1 drops **68
|
||||
candidates over 11 documents**; most are genuine contents listings with prose
|
||||
titles, and four documents are misclassified numeric tables (`40.23`,
|
||||
`300 1`, `3000 V 1`, `619649.097`).
|
||||
|
||||
The misclassification is real and is NOT repaired here: a title predicate that
|
||||
requires a name would rescue the three all-numeric cases and would still not
|
||||
rescue the borehole log, whose rows do carry words. Carrying the text instead
|
||||
of dropping it fixes all of them, at the level where the defect actually is.
|
||||
|
||||
### 1.2 What the tail is
|
||||
|
||||
The 17 590 characters fall on exactly **two** documents (11 016 and 6 574),
|
||||
and both are drawing-heavy PDFs. Profiled against the SAME document's
|
||||
segmented text as a known-positive — the control that matters, because the
|
||||
question is not whether the tail is good prose but whether it is the kind of
|
||||
text the bundle already carries:
|
||||
|
||||
| document | part | lines | % of lines with ≥3 words | % of characters in them |
|
||||
|---|---|---|---|---|
|
||||
| A | segmented | 1485 | 6.4 % | 34.1 % |
|
||||
| A | tail | 448 | **8.5 %** | **33.6 %** |
|
||||
| B | segmented | 883 | 50.3 % | 80.7 % |
|
||||
| B | tail | 160 | **17.5 %** | 56.0 % |
|
||||
|
||||
On document A the tail is **indistinguishable** from what the bundle already
|
||||
keeps; on B it is poorer but not empty. The honest reading is that this is
|
||||
not a separate "is it content" question — the tail is the same kind of text,
|
||||
arriving through the same defect, and the answer is the same rule.
|
||||
|
||||
## 2. The rule: `--close-span-gaps`
|
||||
|
||||
Each span is closed against the next SURVIVING candidate's start, and the last
|
||||
against the end of the text. It runs LAST, after the fold, because that is the
|
||||
one point at which every removing step has already run.
|
||||
|
||||
It **adds no boundary and removes none** — only `end` moves. Measured
|
||||
consequences, both directions:
|
||||
|
||||
| | entries | lead | between | tail | gap |
|
||||
|---|---|---|---|---|---|
|
||||
| round 7 default | 429 | 0 | 26 041 | 17 590 | 43 631 (2.51 %) |
|
||||
| + `--close-span-gaps` | **429** | 0 | **0** | **0** | **0 (0.00 %)** |
|
||||
|
||||
The entry count is identical, which is the same property `--first-span-from-zero`
|
||||
has and the reason this can be measured by coverage rather than by count.
|
||||
|
||||
## 3. Acceptance: all four conditions at once
|
||||
|
||||
The criterion was cells up with no cell lost, and no gold row losing rank 1 on
|
||||
either K2 bundle.
|
||||
|
||||
**The reference sheet is label-identical**, both readings reported:
|
||||
|
||||
| configuration | `pdf` | `docx` | `xlsx` | sheet |
|
||||
|---|---|---|---|---|
|
||||
| round 7 default, `|F|`[3] = 11 | 7/8 | 3/3 | 0/1 | 10/12 |
|
||||
| round 8, `|F|`[3] = 11 | 7/8 | 3/3 | 0/1 | **10/12** |
|
||||
| round 7 default, `|F|`[3] = 12 | 7/8 | 3/3 | 1/1 | 11/12 |
|
||||
| round 8, `|F|`[3] = 12 | 7/8 | 3/3 | 1/1 | **11/12** |
|
||||
|
||||
Not one of the twelve positions changes label. Position 0 stays `for grovt`
|
||||
(its `|F|` is undefined, an operator decision), position 3 stays `for fint` at
|
||||
distance 1 under the authored fasit and `treffer` under the working answer.
|
||||
|
||||
**hit@8, six rows, three bundles:**
|
||||
|
||||
| bundle | concepts | ranks | hit@8 |
|
||||
|---|---|---|---|
|
||||
| round 8 default | **436** | `[1, 1, 1, 1, 1, –]` | 5 of 6 |
|
||||
| round 7 default | 436 | `[1, 1, 1, 1, 1, –]` | 5 of 6 |
|
||||
| Arm B | 629 | `[1, 1, 1, 1, 1, –]` | 5 of 6 |
|
||||
|
||||
**The known-negative reproduces on the new bytes**: read with
|
||||
`--no-tie-shared-rank`, the round 8 bundle gives `[2, 1, 1, 1, 1, –]`, the
|
||||
same fall round 7's pin was built to name. The pin still names its own cause.
|
||||
|
||||
The concept count is **436 = 436**. The rule adds no boundary on the real
|
||||
corpus either, which the corpus measurement predicted and the build confirmed.
|
||||
|
||||
## 4. Cost to a consumer
|
||||
|
||||
A re-run, and this time a small one. The 43-document reference corpus:
|
||||
|
||||
* 2026-09-03 delivered tree: 629 concepts / 1108 files
|
||||
* after 2026-09-08: 492 / 944
|
||||
* after 2026-09-09: 425 / 810
|
||||
* after 2026-09-10: 436 / 832
|
||||
* **after 2026-09-11: 436 / 832** — unchanged in count; concept BODIES grow by
|
||||
the 43 631 characters that were previously in no segment.
|
||||
|
||||
On the operator's five-document folder: **52 md, unchanged**, and byte-identical
|
||||
with and without the flag — that folder has a coverage gap of **zero** under
|
||||
round 7's default already, so there is nothing there to carry. That is the
|
||||
rule behaving correctly on an input with nothing to do, and it is also why the
|
||||
smoke build cannot prove the flag arrives; `tests/test_span_gaps.py` proves
|
||||
that on a document that does have a gap.
|
||||
|
||||
The opt-out `--no-close-span-gaps` reproduces the previous spans.
|
||||
|
||||
## 5. What did NOT move
|
||||
|
||||
* **Clause 1's misclassification of numeric tables** (§ 1.1). Four documents,
|
||||
named with their titles. Carrying the text removes the damage; it does not
|
||||
remove the wrong classification, and a contents run is still discarded as a
|
||||
concept. Whether a run of numeric rows should be a contents listing at all
|
||||
is a separate question with its own measurement.
|
||||
* **`MIN_SHARED_PREFIX`** (round 5's `undersjøisk`/`under`, df 159 of 270).
|
||||
Not re-measured. It stands.
|
||||
* **The `{#…}` leak, the `pptx` fasit, `|F|` for positions 0 and 3.** All four
|
||||
are with the operator with their numbers delivered; none was re-opened here.
|
||||
|
||||
## 6. 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, and a
|
||||
single `xlsx` position carries the whole `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"
|
||||
is "5 of 5 reachable rows plus one nothing has reached".
|
||||
* **"General" rests on ONE corpus of 43 documents.** Re-measured
|
||||
independently this round: `K2/trinn2` is byte-identical to `K2/trinn1` —
|
||||
both `5131f367...` over `find . -type f -print0 | sort -z | xargs -0 shasum
|
||||
-a 256 | shasum -a 256`, 43 files each — and `K1` is empty (0 entries). The
|
||||
null-separated form is not decoration: the first attempt used `xargs`
|
||||
without it, every filename split on its spaces, `shasum` read nothing, and
|
||||
the two sides agreed on a digest of the failure. A control against the
|
||||
digest of empty input (`e3b0c442...`) is what separated the two runs. Both K2 bundles, arms A–F, the
|
||||
whole 12-position reference and all six gold rows are the same 43 documents
|
||||
read again. This is the mandate's fourth acceptance criterion and it is not
|
||||
a weakness in the method — it is a weakness in the basis. See the operator
|
||||
request that accompanies this report.
|
||||
* **`pptx` and `md` are measured on two hand-built documents** and hold **zero**
|
||||
positions on the reference sheet. The `md` cell in § 0 is one document.
|
||||
* **`odt` and `rtf` are unmeasured**, with no document behind them at all.
|
||||
* **"Delivered" is not "answered".** hit@8 measures whether a concept from the
|
||||
gold document reaches the payload, not whether the payload answers. The last
|
||||
measurement of that was rank 1 of 8 on 3 of 3 bundles and a correct answer
|
||||
on 1 of 3.
|
||||
* **Position 0 is blocked on an operator decision, not on code.** Its `|F|` is
|
||||
undefined, so `for grovt` is a label the sheet cannot currently improve.
|
||||
* **The tail profile in § 1.2 is a two-document measurement** with a
|
||||
hand-chosen metric (lines carrying at least three four-letter words). It
|
||||
distinguishes A's tail from nothing, because A's segmented text scores the
|
||||
same. That is the finding, and it is not a general prose detector.
|
||||
* **The between-gap decomposition rests on one dominant document.** 23 259 of
|
||||
26 041 characters are one borehole log. The mechanism generalises; the
|
||||
magnitude is one document's.
|
||||
Loading…
Add table
Add a link
Reference in a new issue