"""The font reader as a RESERVE: typography only where the outline gate is empty. Round 4 measured the font reader two ways and shipped neither as a default. On TOP of Arm D it took `pdf` from 2 of 8 to 0 of 8, because the documents it broke already numbered their own chapters and the second heading source could only ADD a title page and a contents listing. INSTEAD of Arm D it scored 1 of 8 but landed one position exactly on its reference count, which is the observation this flag comes from: use typography only where the document does not number itself. The mechanism is one condition, and these tests pin the condition rather than the outcome: the reserve reads a rendering it may not have asked for only when `outline_runs` admits nothing at the configured minimum. Everything else -- which arm ships, whether the reserve helps -- is a measurement, and the measurement is in `docs/2026-09-08-k3-runde5-hitat8-og-skriftakse.md`. **The reserve is OFF by default and the measurement says it must stay off**: on the twelve-position reference it changes not one cell, because the only positions where it fires are a PDF whose glyphs carry no ToUnicode mapping and four office documents the PDF reader never touches. """ from __future__ import annotations import warnings from pathlib import Path from llm_ingestion_okf import cli from llm_ingestion_okf.extract import extract_text from llm_ingestion_okf.propose import build_plan, heading_reserve_applies FIXTURES = Path(__file__).parent / "fixtures" NUMBERED_PDF = FIXTURES / "numbered-font-krav.pdf" FONT_PDF = FIXTURES / "font-heading-krav.pdf" def _extract(path: Path, **kwargs: object) -> str: with warnings.catch_warnings(): warnings.simplefilter("ignore") return extract_text(path.name, path.read_bytes(), **kwargs) # type: ignore[arg-type] # --- the condition ---------------------------------------------------------- def test_the_reserve_is_silent_where_the_document_numbers_itself() -> None: """The whole point: a document with its own chapter run keeps its own run.""" assert heading_reserve_applies(_extract(NUMBERED_PDF), outline_run=3) is False def test_the_reserve_fires_where_the_outline_gate_admits_nothing() -> None: assert heading_reserve_applies(_extract(FONT_PDF), outline_run=3) is True def test_a_run_shorter_than_the_minimum_is_not_a_run() -> None: """The condition reads the gate as CONFIGURED, not a fixed grammar. The same three-chapter document is a run at 3 and not one at 4, so raising the arm's minimum widens the reserve rather than leaving it behind. """ text = _extract(NUMBERED_PDF) assert heading_reserve_applies(text, outline_run=4) is True def test_with_arm_d_off_the_reserve_fires_everywhere() -> None: """`--outline-run 0` admits no run at all, so the reserve is unconditional. Stated rather than left to the reader: that configuration IS round 4's "font instead of Arm D", which measured 1 of 8. A caller combining the two flags gets that, and gets it deliberately. """ assert heading_reserve_applies(_extract(NUMBERED_PDF), outline_run=0) is True # --- the effect on a plan --------------------------------------------------- def _entries(text: str) -> list[str]: plan = build_plan( Path("x.pdf"), text, b"", okf_type="reference", proposed_at="1970-01-01T00:00:00Z", outline_run=3, table_grid=True, unit_fold=True, ) return [str(entry["title"]) for entry in plan["entries"]] def test_the_numbered_document_is_segmented_by_its_own_numbering() -> None: """The reserve buys nothing here, and that is the guarantee, not a defect. Measured rather than assumed, and the measurement is stronger than the guarantee: at the build default this fixture lands on the same three units from BOTH renderings, because the outline arm's run wins the document and the fold puts the font-inferred `Forord` inside the first unit rather than beside it. So the reserve's silence here has two independent reasons, and only one of them -- the condition -- is what this flag controls. The condition itself is pinned above, on the extraction, where it is decided. """ assert _entries(_extract(NUMBERED_PDF)) == ["Generelle krav", "Merking", "Vedlegg"] assert _entries(_extract(NUMBERED_PDF, pdf_headings=True)) == [ "Generelle krav", "Merking", "Vedlegg", ] # --- the flag --------------------------------------------------------------- def test_the_build_default_does_not_reach_the_reserve() -> None: """Every byte-pinned golden depends on this, so it is a test and not a note.""" assert cli.DEFAULT_PDF_HEADINGS is False assert cli.DEFAULT_PDF_HEADINGS_RESERVE is False def test_the_reserve_is_reachable_from_the_build_command(tmp_path: Path) -> None: """`--pdf-headings font-reserve` is a third value on the same axis. One axis, three values, because `none`, `font` and `font-reserve` are three answers to one question -- how a PDF's headings are recovered -- and a separate boolean flag would let a caller ask for two of them at once. """ base = [ "build", str(tmp_path), "--bundle", str(tmp_path / "b"), "--bundle-id", "x", "--okf-version", "0.2", ] assert cli.parse_args([*base, "--pdf-headings", "font-reserve"]).pdf_headings == "font-reserve" assert cli.parse_args([*base, "--pdf-headings", "font"]).pdf_headings == "font" assert cli.parse_args(base).pdf_headings == "none" def test_the_reserve_reaches_the_proposer_and_the_door_alike(tmp_path: Path) -> None: """Propose and apply must choose the SAME rendering, or nothing applies. A plan indexes the exact string it was proposed against (`text_sha256`), so a reserve that fired on one side and not the other would turn every document it touches into a coded rejection. The build is the test: the unnumbered PDF must come back segmented by its typography, and the numbered one by its own chapters, in ONE run with ONE flag. """ inbox = tmp_path / "in" inbox.mkdir() (inbox / "numbered.pdf").write_bytes(NUMBERED_PDF.read_bytes()) (inbox / "unnumbered.pdf").write_bytes(FONT_PDF.read_bytes()) with warnings.catch_warnings(): warnings.simplefilter("ignore") report = cli.build( inbox, tmp_path / "bundle", bundle_id="reserve-fixture", okf_version="0.2", pdf_headings_reserve=True, ) assert report.rejected == 0 names = sorted(path.name for path in (tmp_path / "bundle").rglob("*.md")) # The unnumbered PDF: the default leaves it one flat concept (round 4 pins # that), and the reserve gives it the unit its typography names. assert "generelle-tekniske-krav.md" in names, names # The numbered PDF: its own chapters, and no `Forord` the reserve added. assert "generelle-krav.md" in names, names assert "merking.md" in names, names assert "vedlegg.md" in names, names assert "forord.md" not in names, names