152 lines
6.3 KiB
Python
152 lines
6.3 KiB
Python
"""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
|