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
|
|
@ -66,16 +66,42 @@ _PANDOC_FORMATS: dict[str, str] = {
|
|||
}
|
||||
|
||||
# What each row's behaviour actually rests on, asserted in the suite rather
|
||||
# than written in a comment that rots. `measured` means real corpus files and a
|
||||
# hand-counted fasit; `unmeasured` means the corpus contains ZERO files of that
|
||||
# type, so the row works by construction and has never been checked against a
|
||||
# document anyone wrote. An unmeasured row must not read as a supported one.
|
||||
# than written in a comment that rots. THREE classes, and the third exists
|
||||
# because the first two could not tell the truth about these rows:
|
||||
#
|
||||
# measured real CORPUS files and a hand-counted fasit. Someone wrote the
|
||||
# document for their own purposes and we counted what we got.
|
||||
# constructed hand-built or generator-built documents with a hand-written
|
||||
# fasit, and no corpus file at all. The row has now met a
|
||||
# document end to end -- it is not `unmeasured` -- but a
|
||||
# document written to exercise it is not a document anyone
|
||||
# wrote, so it is not `measured` either.
|
||||
# unmeasured the corpus contains ZERO files of the type AND no document
|
||||
# has ever been put through the row. It works by construction.
|
||||
#
|
||||
# An unmeasured row must not read as a supported one, and neither must a
|
||||
# constructed one.
|
||||
#
|
||||
# THE THREE OFFICE ROWS MOVED unmeasured -> constructed ON 2026-09-09, each on
|
||||
# its own hand-built document (N = 1, except `pptx` at N = 2):
|
||||
#
|
||||
# .odt 1 of 1 declared headings recovered, 1 concept, 0 characters in no
|
||||
# segment.
|
||||
# .pptx 2 of 2 declared slide titles recovered on a deck that DECLARES them
|
||||
# (`<p:ph type="title"/>`), 0 of 2 on one that does not -- the latter
|
||||
# lands as `Slide 1`/`Slide 2`, which is the converter naming a slide
|
||||
# it has no title for, not a segmentation failure.
|
||||
# .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 Door B's INBOX as one concept --
|
||||
# content preserved, structure zero. That is the row's honest result
|
||||
# and it is the one open finding of the three.
|
||||
_EVIDENCE: dict[str, str] = {
|
||||
".docx": "measured",
|
||||
".xlsx": "measured",
|
||||
".pptx": "unmeasured",
|
||||
".odt": "unmeasured",
|
||||
".rtf": "unmeasured",
|
||||
".pptx": "constructed",
|
||||
".odt": "constructed",
|
||||
".rtf": "constructed",
|
||||
}
|
||||
|
||||
# Load-bearing, all three, and none of them hygiene:
|
||||
|
|
@ -703,6 +729,20 @@ _OPTIONAL_EXTRACTORS: dict[str, Callable[[bytes], str]] = {
|
|||
# containing a `#` cannot be read as a sheet.
|
||||
_SHEET_HEADING = re.compile(r"^#{1,6} (?P<name>.*?) \{#sheet-\d+\}$")
|
||||
|
||||
# Pandoc's ATTRIBUTE syntax at the end of a heading, which is what the sheet
|
||||
# and slide anchors above are an instance of. Deliberately NARROW, because the
|
||||
# known-negative is the whole point: an author writing `Mal for {kundenavn}` or
|
||||
# `Feltet {"id": 4}` wrote a title, and stripping that would be this same
|
||||
# defect pointed the other way.
|
||||
#
|
||||
# The three narrowings, each doing work: the block must be at the END of the
|
||||
# title (`$`), it must OPEN with `#` (pandoc's identifier -- `{.class}` and
|
||||
# `{key=val}` alone are not what any converter here emits, and matching them
|
||||
# would reach further than measured), and the identifier is the restricted
|
||||
# character set pandoc actually generates, so a brace holding a space, a quote
|
||||
# or a colon is not an attribute.
|
||||
_CONVERTER_ATTRIBUTE = re.compile(r"\s*\{#[A-Za-z0-9_.:-]+\}\s*$")
|
||||
|
||||
# A line the converter wrote as part of a pipe table. Whether one of them is
|
||||
# the table's SEPARATOR is decided by POSITION, never by content: an empty
|
||||
# spreadsheet row renders as `| | |` and a separator as `|----|----|`, and
|
||||
|
|
@ -713,6 +753,29 @@ _SHEET_HEADING = re.compile(r"^#{1,6} (?P<name>.*?) \{#sheet-\d+\}$")
|
|||
_TABLE_LINE = "|"
|
||||
|
||||
|
||||
|
||||
def strip_converter_attribute(title: str) -> str:
|
||||
"""Remove a trailing pandoc attribute anchor from a heading's title.
|
||||
|
||||
ONE definition, read by both title-forming sites: `propose` names a
|
||||
segment from an ATX heading, `structure` derives a document title from its
|
||||
leading heading, and a rule living in only one of them would strip the
|
||||
attribute on one path and leave it on the other -- with the id and the
|
||||
title then disagreeing about the same concept.
|
||||
|
||||
Lives HERE because the attribute is a CONVERTER artefact: `_SHEET_HEADING`
|
||||
above is the same syntax read for a different purpose, and this module is
|
||||
the one that knows what pandoc writes. That reading must keep its
|
||||
attribute, which is why the strip is applied to a title downstream and
|
||||
never to the extracted text.
|
||||
|
||||
RENAMES CONCEPT IDS, by design and with the operator's authorisation
|
||||
(2026-09-09): a filename is reduced FROM the title, so the two move
|
||||
together. Measured exposure at the time: 2 of 810 concepts on the default
|
||||
K2 bundle and 2 of 1108 on Arm B.
|
||||
"""
|
||||
return _CONVERTER_ATTRIBUTE.sub("", title)
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SourceUnits:
|
||||
"""Where in the ORIGINAL each stretch of the extracted text came from.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue