llm-ingestion-okf/tests/test_propose_segments.py
Kjell Tore Guttormsen bca722fa85 fix(tools): nothing to propose writes no artifact and is not a failure
Measured on the K2 corpus 2026-09-03: 11 of 39 documents proposed zero
segments -- overwhelmingly PDFs with no declared structure, which Topic 1b
had already measured at 23 of 33. The proposer wrote an artifact for each
of them and exited 0.

An empty plan cannot be replayed. `process_inbox` refuses one by design,
because a plan naming no entry would persist nothing for a document that
was dropped, so the only thing a zero-entry file can do is fail a run
later -- and it did: the first segmented corpus run stopped on
`segmentation_plan_invalid` before writing a single concept.

Exit 1 with no artifact, distinct from 2, so a driver can tell "this
document lands as one flat concept" from "stop". The orphan check's test
now observes the same property through the status.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 04:17:54 +02:00

327 lines
13 KiB
Python

"""The proposer: it proposes, a human adjudicates, and it never pretends otherwise.
The research is explicit about why this tool exists and about what it may not
claim. Topic 2 measured that the OKF reference agent splits on **what a thing
is** -- four semantic gates that need a model this run path does not have --
while a handful of MECHANICAL rules port today. Topic 1b measured that heading
derivation is inert on most of the K2 corpus: 23 of 33 PDFs carry no outline at
all and 95 % of the outline entries that exist are CAD export metadata, so the
scoreable denominator is ONE document. A rule validated on n=1 is not validated.
So the tool marks every decision `PROPOSED` and nothing else. The distinction
is the whole design: a proposal a human has not looked at must never be
replayable as an adjudication, because replay is exactly what the run path does
with a plan, deterministically and forever.
It lives outside `src/`, so it never enters a wheel (`tests/test_packaging.py`
enforces that) and no consumer's install surface changes because it exists.
"""
from __future__ import annotations
import json
import socket
import sys
from pathlib import Path
import pytest
from llm_ingestion_okf.segmentation import parse_segmentation_plan
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "tools"))
import okf_propose_segments # noqa: E402
DOCUMENT = """# N500 Vegbygging
Innledende tekst om vegbygging og dens omfang.
## 3.1 Brannkonsept
Krav til seksjonering av bygget over flere etasjer.
## 3.2 Roemning
To uavhengige roemningsveier fra hver branncelle.
## Og
## 3.3 Baereevne
Hovedbaeresystemet skal ha R60 dokumentert baereevne.
"""
def write(tmp_path: Path, text: str = DOCUMENT, name: str = "n500.md") -> Path:
path = tmp_path / name
path.write_text(text, encoding="utf-8", newline="")
return path
def propose(tmp_path: Path, **kwargs: object) -> dict:
source = kwargs.pop("source", None) or write(tmp_path)
out = tmp_path / "plan.json"
code = okf_propose_segments.main([str(source), "--out", str(out)])
assert code == 0, f"proposer exited {code}"
return json.loads(out.read_text(encoding="utf-8"))
# --- the artifact is a valid plan -----------------------------------------
def test_the_emitted_artifact_parses_as_a_segmentation_plan(tmp_path: Path) -> None:
plan = parse_segmentation_plan(propose(tmp_path))
assert plan.entries
def test_it_carries_its_provenance(tmp_path: Path) -> None:
payload = propose(tmp_path)
assert payload["source_sha256"]
assert payload["extractor_id"]
assert payload["extractor_version"]
assert payload["proposed_by"].startswith("okf-propose-segments")
def test_the_source_hash_is_the_hash_of_the_bytes_it_read(tmp_path: Path) -> None:
import hashlib
source = write(tmp_path)
payload = propose(tmp_path, source=source)
assert payload["source_sha256"] == hashlib.sha256(source.read_bytes()).hexdigest()
# --- PROPOSED, never adjudicated ------------------------------------------
def test_every_entry_is_marked_proposed(tmp_path: Path) -> None:
payload = propose(tmp_path)
assert payload["entries"]
for entry in payload["entries"]:
assert okf_propose_segments.PROPOSED_MARKER in entry["derived"]
def test_no_entry_claims_to_be_adjudicated(tmp_path: Path) -> None:
payload = propose(tmp_path)
assert payload["adjudicated"] is False
for entry in payload["entries"]:
assert "adjudicated" not in entry
def test_every_entry_names_the_rule_that_proposed_it(tmp_path: Path) -> None:
# Per-decision provenance. A proposal an operator cannot trace to a rule is
# one they can only accept or reject wholesale.
for entry in propose(tmp_path)["entries"]:
rules = [name for name in entry["derived"] if name.startswith("rule:")]
assert len(rules) == 1
assert rules[0] in okf_propose_segments.RULE_NAMES
# --- the mechanical rules that actually ported -----------------------------
def test_a_heading_of_only_stop_words_is_not_a_boundary(tmp_path: Path) -> None:
# "Og" is a stop word. Without the gate it would open a segment of its own.
titles = [entry["title"] for entry in propose(tmp_path)["entries"]]
assert "Og" not in titles
assert any("Brannkonsept" in title for title in titles)
def test_a_heading_with_no_body_proposes_nothing(tmp_path: Path) -> None:
"""The orphan check, observed through the status now that a zero-entry plan
is no longer written: proposing nothing and writing nothing are the same
outcome, and it is distinct from failing."""
out = tmp_path / "orphans.json"
source = write(tmp_path, "# Tom\n\n## Ogsaa tom\n")
assert okf_propose_segments.main([str(source), "--out", str(out)]) == 1
assert not out.exists()
def test_the_spans_it_proposes_are_slices_of_the_text_it_read(tmp_path: Path) -> None:
from llm_ingestion_okf.extract import extract_text
source = write(tmp_path)
payload = propose(tmp_path, source=source)
text = extract_text(source.name, source.read_bytes())
for entry in payload["entries"]:
start, end = entry["span"]
assert text[start:end].strip()
def test_every_entry_carries_an_anchor_quoting_its_own_span(tmp_path: Path) -> None:
"""Written at proposal time, when text and offsets are known to agree.
Reconstructed later it would quote whatever the extraction had already
become, which is precisely the drift the anchor exists to survive.
"""
from llm_ingestion_okf.extract import extract_text
source = write(tmp_path)
payload = propose(tmp_path, source=source)
text = extract_text(source.name, source.read_bytes())
assert payload["entries"]
for entry in payload["entries"]:
start, end = entry["span"]
anchor = entry["anchor"]
assert anchor["quote"] == text[start:end]
assert text[start - len(anchor["prefix"]) : start] == anchor["prefix"]
assert text[end : end + len(anchor["suffix"])] == anchor["suffix"]
def test_a_proposed_plan_survives_a_shift_of_the_text_it_was_made_against(
tmp_path: Path,
) -> None:
"""The round trip the anchor exists for, measured end to end.
Prepend a paragraph -- exactly the benign edit that leaves every offset
short -- and every body must still be the text the proposer quoted. Before
anchors this cut each segment early and landed on real prose, with nothing
anywhere able to tell.
"""
from llm_ingestion_okf.extract import extract_text
from llm_ingestion_okf.segmentation import slice_segments
source = write(tmp_path)
plan = parse_segmentation_plan(propose(tmp_path, source=source))
text = extract_text(source.name, source.read_bytes())
quoted = [text[start:end] for start, end in (item.span for item in plan.entries)]
shifted = "Et nytt avsnitt foran alt annet.\n\n" + text
assert [body for _, body in slice_segments(shifted, plan)] == quoted
def test_the_paths_it_proposes_are_unique_and_hierarchical(tmp_path: Path) -> None:
plan = parse_segmentation_plan(propose(tmp_path))
paths = [entry.path for entry in plan.entries]
assert len(paths) == len(set(paths))
assert any("/" in path for path in paths)
# --- it does not reach the network ----------------------------------------
def test_it_makes_no_network_call(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
def refuse(*args: object, **kwargs: object) -> None:
raise AssertionError("the proposer opened a socket")
monkeypatch.setattr(socket, "socket", refuse)
monkeypatch.setattr(socket, "create_connection", refuse)
parse_segmentation_plan(propose(tmp_path))
def test_the_socket_guard_can_actually_fire(monkeypatch: pytest.MonkeyPatch) -> None:
# The known-positive control. Without it, a guard that patched the wrong
# name would make "no network call" a statement about nothing.
def refuse(*args: object, **kwargs: object) -> None:
raise AssertionError("the proposer opened a socket")
monkeypatch.setattr(socket, "socket", refuse)
with pytest.raises(AssertionError, match="opened a socket"):
socket.socket()
# --- failure is loud ------------------------------------------------------
def test_a_missing_source_exits_two_and_says_so(
tmp_path: Path, capsys: pytest.CaptureFixture[str]
) -> None:
code = okf_propose_segments.main([str(tmp_path / "absent.md"), "--out", str(tmp_path / "o")])
assert code == 2
assert "okf-propose-segments" in capsys.readouterr().err
def test_an_unknown_file_type_exits_two(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None:
source = write(tmp_path, "body\n", name="thing.unknown")
code = okf_propose_segments.main([str(source), "--out", str(tmp_path / "o")])
assert code == 2
assert "okf-propose-segments" in capsys.readouterr().err
# --- the per-document scope a multi-document corpus needs ------------------
def test_a_path_prefix_scopes_every_entry_under_one_directory(tmp_path: Path) -> None:
"""Measured on the K2 corpus, 2026-09-03: 39 documents proposed 618 entries
under 601 distinct paths -- **17 paths were claimed by two documents each**.
Every one of them would hit Door B's collision gate, and the gate refuses
per DOCUMENT, so those documents would land as coded rejections instead of
concepts. Section numbering is document-local (`1 Innledning` is in most of
them), so the collision is structural rather than unlucky.
The scope is an argument and not something this tool invents: the proposer
sees one document and has no way of knowing what else is in the bundle, so
the caller who does supplies the prefix.
"""
out = tmp_path / "plan.json"
assert (
okf_propose_segments.main(
[str(write(tmp_path)), "--out", str(out), "--path-prefix", "Del II Bilag 3.1"]
)
== 0
)
payload = json.loads(out.read_text(encoding="utf-8"))
assert payload["entries"]
for entry in payload["entries"]:
assert entry["path"].startswith("del-ii-bilag-3-1/")
# Still a valid plan: the prefix passes the same path grammar as any other
# component, rather than being spliced in behind the validator's back.
assert parse_segmentation_plan(payload).entries
def test_without_a_prefix_the_artifact_is_byte_identical(tmp_path: Path) -> None:
"""Additive. Every plan already produced stays exactly what it was."""
first = tmp_path / "a.json"
second = tmp_path / "b.json"
source = write(tmp_path)
okf_propose_segments.main([str(source), "--out", str(first)])
okf_propose_segments.main([str(source), "--out", str(second), "--path-prefix", ""])
assert first.read_bytes() == second.read_bytes()
def test_a_prefix_that_reduces_to_nothing_is_refused(tmp_path: Path) -> None:
"""A prefix of punctuation would otherwise become an empty component and
silently produce the unscoped paths the caller asked to avoid."""
code = okf_propose_segments.main(
[str(write(tmp_path)), "--out", str(tmp_path / "p.json"), "--path-prefix", "###"]
)
assert code == 2
assert not (tmp_path / "p.json").exists()
# --- nothing to propose is an outcome, and it is not an artifact -----------
def test_a_document_with_no_proposable_structure_writes_no_artifact(tmp_path: Path) -> None:
"""Measured on the K2 corpus 2026-09-03: 11 of 39 documents proposed zero
segments -- overwhelmingly PDFs with no declared structure, which Topic 1b
already measured at 23 of 33.
An empty artifact is not a plan a run can replay: `process_inbox` refuses
one by design, because an empty plan would persist nothing for a document
that was dropped. So the only use a zero-entry file has is to fail a run
later, and writing it converts "this document proposes no split" into "this
corpus cannot be built". It gets its own exit status instead: distinct from
2, which means the tool could not do its job at all.
"""
flat = write(tmp_path, "Loepende tekst uten overskrifter i det hele tatt.\n", name="flat.md")
out = tmp_path / "flat.json"
assert okf_propose_segments.main([str(flat), "--out", str(out)]) == 1
assert not out.exists()
def test_nothing_to_propose_is_not_reported_as_a_failure(
tmp_path: Path, capsys: pytest.CaptureFixture[str]
) -> None:
"""The two outcomes must be distinguishable by a driver reading the status:
one means "this document lands as one flat concept", the other means "stop"."""
flat = write(tmp_path, "Loepende tekst uten overskrifter i det hele tatt.\n", name="flat.md")
assert okf_propose_segments.main([str(flat), "--out", str(tmp_path / "f.json")]) == 1
nothing = capsys.readouterr().err
assert "FAILED" not in nothing
assert "nothing to propose" in nothing.lower()
assert (
okf_propose_segments.main([str(tmp_path / "absent.md"), "--out", str(tmp_path / "a.json")])
== 2
)
assert "FAILED" in capsys.readouterr().err