"""`md` recovers every heading it declares, and D1 is what makes that true. Round 7 reported this cell as **3 of 4**, with the mechanism decomposed: a `rule:table-block` candidate opens BELOW `## 3 Prising` instead of a `rule:heading` opening ON it, so the heading is bodiless, the orphan check drops it, and the fold then merges the nameless block into the section above. Re-measured on `a364ef4` (round 7's own commit), the default recovers **4 of 4**. The report's ยง 5 is a measurement of the configuration that existed BEFORE the same round moved `--keep-table-heading` into the default -- D1 is precisely the repair for that mechanism, and it shipped in the commit the report describes. Nothing in the tree said so, because the decomposition and the default move live in different files. That is the round-7 trap restated: a number about a rule is a measurement of a CONFIGURATION. This file is the missing pin. The cell is asserted here, and so is its cause -- turn D1 off and the third heading becomes a table block again. """ from __future__ import annotations from llm_ingestion_okf import cli from llm_ingestion_okf.propose import RULE_HEADING, RULE_TABLE_BLOCK, find_candidates DEFAULT = dict( outline_run=cli.DEFAULT_OUTLINE_RUN, table_grid=cli.DEFAULT_TABLE_GRID, unit_fold=cli.DEFAULT_UNIT_FOLD, keep_table_heading=cli.DEFAULT_KEEP_TABLE_HEADING, sheet_section_rows=cli.DEFAULT_SHEET_SECTION_ROWS, drop_wrapped_outline=cli.DEFAULT_DROP_WRAPPED_OUTLINE, outline_gate=cli.DEFAULT_OUTLINE_GATE, first_span_from_zero=cli.DEFAULT_FIRST_SPAN_FROM_ZERO, close_span_gaps=cli.DEFAULT_CLOSE_SPAN_GAPS, ) #: The same structure round 7 built: three chapters, the last one a heading #: with a table directly under it and a closing line after the table. DOCUMENT = """# Prosjektbeskrivelse Testdokument Dette dokumentet har en kjent struktur: tre kapitler paa niva 1, hvert med en kort brodtekst, samt en innledning over det forste kapitlet. ## 1 Omfang Leveransen omfatter tre delytelser. Hver delytelse har egen frist og egen akseptansetest. Teksten her er brodtekst og skal ikke bli en overskrift. ## 2 Krav til dokumentasjon Dokumentasjon leveres i PDF. Tegninger leveres i DWG. Modeller leveres i IFC. Krav nummer 2.1 gjelder alle tre formatene. ## 3 Prising | Post | Beskrivelse | Enhet | Mengde | | --- | --- | --- | --- | | 1 | Riving av eksisterende dekke | m2 | 420 | | 2 | Ny baerekonstruksjon | tonn | 38 | | 3 | Tekniske installasjoner | RS | 1 | Prisene fylles ut av tilbyder ved tilbudsfrist. """ DECLARED = ( "Prosjektbeskrivelse Testdokument", "1 Omfang", "2 Krav til dokumentasjon", "3 Prising", ) def _declared_headings(**kwargs: object) -> list[str]: candidates = find_candidates(DOCUMENT, **{**DEFAULT, **kwargs}) # type: ignore[arg-type] return [c.title for c in candidates if c.rule == RULE_HEADING] def test_the_default_recovers_all_four_declared_headings() -> None: assert _declared_headings() == list(DECLARED) def test_the_priced_section_is_a_heading_and_not_a_table_block() -> None: """The specific cell: `## 3 Prising` keeps its own line and its own name.""" candidates = find_candidates(DOCUMENT, **DEFAULT) prising = [c for c in candidates if c.title == "3 Prising"] assert len(prising) == 1 assert prising[0].rule == RULE_HEADING assert DOCUMENT[prising[0].start : prising[0].end].startswith("## 3 Prising") def test_without_d1_the_cell_falls_back_to_three_of_four() -> None: """The known-negative: the pin names its own cause instead of being green for an unstated reason. Without `--keep-table-heading` the heading is orphaned and a table block carries its name from a line below it.""" assert _declared_headings(keep_table_heading=False) == list(DECLARED[:3]) candidates = find_candidates(DOCUMENT, **{**DEFAULT, "keep_table_heading": False}) carrier = [c for c in candidates if c.title == "3 Prising"] assert len(carrier) == 1 assert carrier[0].rule == RULE_TABLE_BLOCK assert not DOCUMENT[carrier[0].start : carrier[0].end].startswith("## 3 Prising")