feat(propose,extract,cli): a title that ends in a number, and a converter's own anchor in a concept id
Round 9: the four rests in STATE's NESTE that needed no operator decision.
CLAUSE 1 CLASSIFIED BY THE NUMBER, NOT THE TITLE. `_TRAILING_PAGE_NUMBER`
admitted a candidate into a contents run by asking whether the title ended in
an integer -- a question about the number. A drawing's dimension chain, a
schematic's labels, a door schedule, a coordinate column and a soil-layer
table all end in integers and name nothing. Measured over the 43-document
corpus: 68 candidates discarded over 11 of 39 readable documents, of which
19 over 5 documents are data rows.
That corrects round 8's own decomposition. Its "four misclassified numeric
tables and seven real contents listings" needs each document on one side, and
two of the eleven are both. Read across all 68 titles rather than the
three-title sample: 5 documents carry a data row, 8 carry a real entry.
`--contents-name` requires a NAME to survive stripping the page number. The
threshold is SWEPT, not chosen, and collapses at both ends: at an alphabetic
run of 1 a door schedule keeps a stray `V` and 13 of 19 are rescued; at 3 the
two-letter section name `VA` stops being a name, falls out of run membership,
and takes `RIB`, `MMI` and `Tittelfelt` below `CONTENTS_RUN` with it -- one
acronym costing four REAL entries. At 2: 16 of 19 rescued, 0 of 49 regressed.
The three not rescued carry a real word and are named rather than rounded off.
THE CONVERTER'S ANCHOR WAS IN THE CONCEPT ID. Pandoc writes a sheet as
`## <name> {#sheet-N}` and a titled slide as `## <title> {#slide-N}`. Because
a filename is reduced FROM the title, the anchor reached both. Operator
authorised the strip 2026-09-09 after the exposure was counted: 2 of 810
concepts on the previous default bundle, 2 of 1108 on Arm B, 1 of 26 on the
operator's folder. Two ids renamed, one of which `portfolio-optimiser` has
cited in writing; both are in the report so that message can be sent.
One rule in one function, read by BOTH title-forming sites -- a rule in only
one would leave the id and the title naming the same concept differently. The
known-negative is the point: `Mal for {kundenavn}` is a title an author wrote.
odt/rtf/pptx MEASURED END TO END FOR THE FIRST TIME, on hand-built documents,
because the corpus denominator is genuinely zero (86 files: 66 pdf, 10 docx,
4 xlsx, 2 zip, 2 smc, 2 doc). `_EVIDENCE` gains a third class rather than
stretching an existing one: `constructed` means the row has met a document,
but not one anyone wrote for their own purposes. odt 1 of 1 declared headings;
pptx 2 of 2 on a deck that declares slide titles and 0 of 2 on one that does
not -- round 7's reading of pptx was a fixture property, not the format; rtf
0 segments, because the container has no heading style and the author's title
is bold text. rtf is the one open finding.
ACCEPTANCE, all four. The 12-position reference is label-identical in BOTH
readings (pdf 7/8, docx 3/3, xlsx 0/1 or 1/1, sheet 10/12 or 11/12). One K2
bundle carrying both changes: 453 concepts / 865 md, hit@8 [1,1,1,1,1,None]
on it AND on Arm B, with the known-negative still reproducing on the new
bytes. `okf project` byte-equal to `okf build`, `diff -r` empty. Consumer
cost is a re-run: 436/832 -> 453/865, digest 21af4a1aa98315cf.
Three published numbers corrected: README's 596 tests (1515), README's "15
concepts out" for `okf project` (that was the O6 defect; it is 26), and O6's
print-mode method, which does not reproduce without --allowedTools.
Report: docs/2026-09-09-k3-runde9-restene.md
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
fbad5b0eab
commit
6ff18fd703
15 changed files with 964 additions and 67 deletions
80
README.md
80
README.md
|
|
@ -49,7 +49,8 @@ a profile rather than by constants (see [Upstream OKF
|
|||
versions](#upstream-okf-versions)). Binary extraction runs behind the
|
||||
optional `[extract]` extra: `pdf` through a PDF parser, and five office
|
||||
formats through a vendored document converter. Three of those five office
|
||||
rows are **unmeasured** — see [Binary extraction](#binary-extraction). Phase 4
|
||||
rows are **constructed** rather than measured — see
|
||||
[Binary extraction](#binary-extraction). Phase 4
|
||||
(the Node half) is planned (see `docs/plan/`).
|
||||
|
||||
## Install in detail
|
||||
|
|
@ -370,15 +371,31 @@ read, what it wrote, and which documents a question cannot reach. Start `claude`
|
|||
in that directory and ask in plain language; the generated skill runs the
|
||||
pre-pass and the contract check itself and marks every claim with its source.
|
||||
|
||||
**Running non-interactively.** In print mode the skill needs its tools named
|
||||
explicitly, or the model answers without ever reading the bundle and marks
|
||||
every premise `undecidable-from-bundle`:
|
||||
|
||||
```sh
|
||||
claude -p --allowedTools=Bash,Read,Grep,Glob "<the question>"
|
||||
```
|
||||
|
||||
Add `Write,Edit` for the mode that produces a document. `--permission-mode
|
||||
acceptEdits` alone does **not** do it — measured 2026-09-09 over four runs, the
|
||||
`okf consume` call is refused without the explicit tool list. The interactive
|
||||
`claude` above needs none of this.
|
||||
|
||||
`<id>` is the folder's name reduced to `[a-z0-9-]`. Run it once per folder with
|
||||
`--id <name>` to have several bundles reachable at once — each skill carries its
|
||||
own `bundle_id`, which is what lets a model pick between them. `--out <dir>`
|
||||
puts the project somewhere other than the current directory.
|
||||
|
||||
Measured 2026-09-08 from a fresh `uv tool install` with this repository nowhere
|
||||
on the path: 5 documents in, 15 concepts out, a skill carrying **0** paths into
|
||||
any checkout, and `okf check` conformant on its own payload (15 rules, 0
|
||||
findings). Before that day the same result took a `PYTHONPATH`, a snapshot of a
|
||||
Measured 2026-09-09 from a fresh `uv tool install` with this repository nowhere
|
||||
on the path: 5 documents in, **26** concepts out, a skill carrying **0** paths
|
||||
into any checkout, and `okf check` conformant on its own payload (15 rules, 0
|
||||
findings). The 2026-09-08 run of the same measurement reported 15 concepts, and
|
||||
that number was the defect rather than the result: `okf project` was calling
|
||||
`build()` as a function and reading its signature's defaults, which disagreed
|
||||
with argparse's on two flags. Two tests now hold the two default sets equal. Before that day the same result took a `PYTHONPATH`, a snapshot of a
|
||||
clone, and a generated skill that named that clone by absolute path on four
|
||||
lines — so it could not be moved, shared, or run by anyone else.
|
||||
|
||||
|
|
@ -646,14 +663,23 @@ From a checkout, the test suite runs with:
|
|||
.venv/bin/python -m pytest
|
||||
```
|
||||
|
||||
The suite is the verification surface for everything above: 596 tests, run on
|
||||
2026-08-21 against this branch with the `[extract]` extra installed. Without
|
||||
the extra the same suite is 589 passed and 7 skipped, measured the same day:
|
||||
the seven cover the parser path, and the tests holding the fail-fast rejection
|
||||
for an uninstalled extra run in both. It is not shipped in an installed
|
||||
The suite is the verification surface for everything above: **1515 tests**,
|
||||
run on 2026-09-09 against this branch with the `[extract]` extra installed.
|
||||
(The figure published here until that day was 596, measured 2026-08-21 and
|
||||
never updated as the suite grew — a count is a measurement with a date on it.)
|
||||
Without the extra the same suite skips the tests covering the parser path;
|
||||
that split was last counted on 2026-08-21 as 589 passed and 7 skipped and has
|
||||
**not** been re-measured since. The tests holding the fail-fast rejection for
|
||||
an uninstalled extra run in both. The suite is not shipped in an installed
|
||||
distribution — `tests/` lives at the repository root, so this command needs a
|
||||
clone rather than a `pip install`.
|
||||
|
||||
**Lint is measured against the lockfile.** `uv sync --frozen` resolves ruff
|
||||
0.15.22, under which `ruff check src tests tools` is clean. A loose install
|
||||
resolves ruff 0.16.6, under which the same tree reports 144 lint errors and 2
|
||||
files that would be reformatted — new rules, not new defects. Add
|
||||
`--extra extract` to that sync or `mypy src` cannot find `pdfplumber`.
|
||||
|
||||
A git URL is a PEP 508 direct reference and pins one exact tag, so it is an
|
||||
install-time *channel*, not the pin: the range above stays the declared
|
||||
dependency — a wheel built from this branch carries `Requires-Dist:
|
||||
|
|
@ -678,15 +704,33 @@ deterministic within a converter version and not across one.
|
|||
| `pdf` | `pdfplumber` | measured |
|
||||
| `docx` | converter | measured |
|
||||
| `xlsx` | converter | measured |
|
||||
| `pptx` | converter | **unmeasured** |
|
||||
| `odt` | converter | **unmeasured** |
|
||||
| `rtf` | converter | **unmeasured** |
|
||||
| `pptx` | converter | **constructed** |
|
||||
| `odt` | converter | **constructed** |
|
||||
| `rtf` | converter | **constructed** |
|
||||
|
||||
**`unmeasured` means what it says.** The corpus this work was measured on
|
||||
contains **zero** `pptx`, `odt` and `rtf` files, so those three rows work by
|
||||
construction and have never been checked against a document anyone wrote.
|
||||
They are not known to be broken; they are not known to be right either, and
|
||||
the distinction is the point.
|
||||
**`constructed` means what it says, and it is a weaker word than `measured`
|
||||
on purpose.** The corpus this work was measured on contains **zero** `pptx`,
|
||||
`odt` and `rtf` files. Until 2026-09-09 those three rows were `unmeasured` —
|
||||
they worked by construction and had never been checked against a document
|
||||
anyone wrote. They have now each been put through end to end on a hand-built
|
||||
document with a hand-written fasit, which is more than nothing and is not a
|
||||
corpus:
|
||||
|
||||
- `odt` — **1 of 1** declared headings recovered, 1 concept, 0 characters in
|
||||
no segment. N = 1 document.
|
||||
- `pptx` — **2 of 2** declared slide titles recovered on a deck that declares
|
||||
them (a real `<p:ph type="title"/>` placeholder); **0 of 2** on a deck that
|
||||
does not, where the converter writes `Slide 1` / `Slide 2` because it has no
|
||||
title to use. That is the converter naming an unnamed slide, not a
|
||||
segmentation failure. N = 2 decks.
|
||||
- `rtf` — **0** declared headings, because the container has no heading style
|
||||
and the author's title is bold text. The proposer therefore proposes
|
||||
nothing, and the document reaches the bundle inbox as one whole concept:
|
||||
content preserved, structure zero. N = 1 document. This is the one open
|
||||
finding of the three.
|
||||
|
||||
They are not known to be broken; a single constructed document is not a
|
||||
denominator, and the distinction is the point.
|
||||
|
||||
**What stays out.** `.doc` (Word 97) is not supported — the converter does not
|
||||
read it. Rastered or scanned PDFs are refused rather than persisted as empty
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue