"""The outline-reach instrument: phase 2 of order 20260906T213322Z. Measures how far Arm D's outline rule reaches into a corpus, before and after the orphan check that deletes a third of what it proposes. The instrument exists because `docs/2026-09-04-k3-arm-c.md` published figures a reader could not re-derive: a number without a committed script cannot be reproduced, and a K3 row resting on one is an assertion rather than a measurement. The identity test below is the one that matters most. An instrument that re-implements the grammar it measures is measuring a SECOND definition, free to drift from the shipped one without a single test going red -- so the instrument imports `outline_lines`, `outline_runs` and `find_candidates` from the tool, and this file pins that they are the same objects. The negative control matters as much as the positive one, for the reason `test_cid_measure.py` states: an instrument reporting reach on a corpus that has none would inflate every number it ever produces. Here the control is sharper than "zero" -- it is zero WITH a nonzero denominator, because "found nothing" and "measured nothing" are different results and only one of them is evidence. """ from __future__ import annotations import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "tools")) import okf_outline_measure # noqa: E402 import okf_propose_segments # noqa: E402 OUTLINE_DOC = """1 Innledning Bakgrunn for prosjektet og omfanget. 2 Krav Krav til seksjonering av bygget. 3 Gjennomfoering Framdrift, faser og overlevering. """ NO_OUTLINE_DOC = """# Teknisk grunnlag Innledende tekst uten nummerering i det hele tatt. ## 3.1 Brannkonsept To uavhengige roemningsveier fra hver branncelle. """ def test_the_instrument_and_the_tool_are_one_definition() -> None: """Identity, not equality: the cheapest proof there is no second grammar.""" assert okf_outline_measure.outline_runs is okf_propose_segments.outline_runs assert okf_outline_measure.outline_lines is okf_propose_segments.outline_lines assert okf_outline_measure.find_candidates is okf_propose_segments.find_candidates def test_a_hand_computed_document_reports_its_boundaries_both_sides_of_the_gate() -> None: """Three chapters, each with a body, so the gate deletes none of them.""" result = okf_outline_measure.measure_document(OUTLINE_DOC, "doc", run_length=3) assert result.pre_gate_boundaries == 3 assert result.post_gate_boundaries == 3 assert result.arm_b_entries == 0 assert result.arm_d_entries == 3 assert result.deleted_arm_b == 0 def test_the_gate_deletes_an_arm_b_heading_the_outline_immediately_follows() -> None: """The 34 %-deletion mechanism, measured on a document small enough to check. `# Teknisk grunnlag` is followed immediately by `1 Innledning`, so its body is empty and the orphan check drops it. Arm B had one entry; Arm D has three, and the one Arm B had is gone. """ text = "# Teknisk grunnlag\n\n1 Innledning\n\nA.\n\n2 Krav\n\nB.\n\n3 Slutt\n\nC.\n" result = okf_outline_measure.measure_document(text, "doc", run_length=3) assert result.arm_b_entries == 1 assert result.deleted_arm_b == 1 assert result.post_gate_boundaries == 3 def test_a_corpus_with_no_outline_reports_zero_with_a_nonzero_denominator() -> None: """The negative control. Zero reach is only evidence if something was measured.""" result = okf_outline_measure.measure_document(NO_OUTLINE_DOC, "doc", run_length=3) assert result.pre_gate_boundaries == 0 assert result.post_gate_boundaries == 0 # The denominator: the document WAS measured, and Arm B did find boundaries # in it, so a zero here is the rule declining rather than the probe failing. assert result.arm_b_entries > 0 assert result.arm_d_entries == result.arm_b_entries def test_an_empty_document_does_not_crash() -> None: result = okf_outline_measure.measure_document("", "doc", run_length=3) assert result.pre_gate_boundaries == 0 assert result.arm_b_entries == 0 assert result.arm_d_entries == 0 assert result.unique_paths == 0 def test_the_sample_draw_reproduces_a_known_ordering() -> None: """The draw is the method's, re-derived: hex SHA-256 of the NFC filename. The expected list is computed by hand from the digests, not by calling the function under test -- otherwise the assertion would only prove the code agrees with itself. """ names = [ "alfa.pdf", "beta.pdf", "gamma.pdf", "delta.docx", "epsilon.docx", "zeta.xlsx", "eta.xlsx", "theta.pdf", ] drawn = okf_outline_measure.draw_sample(names, {"pdf": 2, "docx": 1, "xlsx": 1}) assert drawn == ["theta.pdf", "gamma.pdf", "eta.xlsx", "delta.docx"] def test_the_draw_normalises_to_nfc_before_hashing() -> None: """macOS hands filenames over decomposed; the two forms hash differently. Without this the same visual corpus would draw a different sample depending on which normalisation the filenames arrived in -- the same defect the library already fixed in `reduce_to_id_grammar`. """ # Escapes, not literals: a source file is stored in ONE normalisation, so # writing both forms as literals would silently make them the same string # and the control below would be green for the wrong reason. `oe` is used # because U+00F8 has no canonical decomposition at all -- picking it would # make this test vacuous in a second, quieter way. composed = "caf\u00e9.pdf" # NFC: e-acute as one code point decomposed = "cafe\u0301.pdf" # NFD: `e` plus combining acute assert composed != decomposed # the control: they really are different strings assert okf_outline_measure._draw_key(composed) == okf_outline_measure._draw_key(decomposed) # And a second control: two names that are genuinely different still differ. assert okf_outline_measure._draw_key("a.pdf") != okf_outline_measure._draw_key("b.pdf") def test_a_title_with_no_alphabetic_word_is_counted_as_junk() -> None: """11 of the 95 surviving outline titles are junk; the report needs the count.""" text = "1 477 3 025\n\nA.\n\n2 Krav\n\nB.\n\n3 D L\n\nC.\n" result = okf_outline_measure.measure_document(text, "doc", run_length=3) assert result.post_gate_boundaries == 3 assert result.junk_titles == 2 # `477 3 025` and `D L` -- `Krav` is a word