fix(inbox): refuse a segmentation plan that matches no dropped file
A plan is selected by content hash, so a mistyped source_sha256 matched nothing, every dropped file fell through to the one-concept rule, and process_inbox returned an ordinary success over a flat bundle. The operator asked for segmentation, got none, and had no error to read -- the silent skip this library refuses everywhere else. vegnormal-okf is about to run an N500 corpus through this path, where a silent zero would read as "the corpus has no concepts". The refusal asks whether a covering plan was FOUND, not whether every file was examined, so an unreadable drop cannot mask it; and coverage is recorded at selection, not after path validation, so a matched plan with a refused entry path still reports its own per-file code. The first cut got that second question wrong and an existing collision test caught it; the case is now pinned by its own test, verified red against the earlier form. New code segmentation_plan_unmatched, registered in the SegmentationError docstring register in the same commit. Fail-fast before any disk mutation. The four existing goldens are byte-identical to baseline. Co-Authored-By: Claude <claude-opus-5>
This commit is contained in:
parent
36af65c0c9
commit
8f9b4c8cca
3 changed files with 123 additions and 0 deletions
|
|
@ -347,3 +347,97 @@ def test_a_plan_covering_one_of_two_documents_leaves_the_other_flat(tmp_path: Pa
|
|||
assert set(PATHS) <= set(found)
|
||||
assert "inbox-v720.md" in found
|
||||
assert len(found) == 6
|
||||
|
||||
|
||||
# --- a plan that matches nothing is refused, never silent ------------------
|
||||
|
||||
|
||||
def test_a_plan_matching_no_dropped_file_is_refused(tmp_path: Path) -> None:
|
||||
"""The silent skip this library refuses everywhere else.
|
||||
|
||||
`_plan_covering` selects on content hash, so a mistyped `source_sha256`
|
||||
matches nothing, every dropped file falls through to the one-concept rule,
|
||||
and the run reports a perfectly ordinary success. The operator asked for
|
||||
segmentation and got a flat bundle with no error to read. `vegnormal-okf`
|
||||
is about to put an N500 corpus through this path; a silent zero there would
|
||||
read as "the corpus has no concepts".
|
||||
"""
|
||||
source = drop(tmp_path / "round", "n500.md", DOCUMENT)
|
||||
plan = build_plan(source.read_bytes(), DOCUMENT, source_sha256="0" * 64)
|
||||
with pytest.raises(SegmentationError) as excinfo:
|
||||
run(tmp_path, plan=plan)
|
||||
assert excinfo.value.code == "segmentation_plan_unmatched"
|
||||
assert tree(tmp_path / "bundle") == {}
|
||||
|
||||
|
||||
def test_a_plan_matching_one_of_several_dropped_files_is_not_refused(tmp_path: Path) -> None:
|
||||
"""The negative control for the check above.
|
||||
|
||||
A run may legitimately drop many documents while only one is plan-covered
|
||||
-- that is the whole point of hash selection. A check that fired here would
|
||||
have replaced a silent skip with a refusal of the normal case, so this test
|
||||
is what keeps the new gate honest rather than merely loud.
|
||||
"""
|
||||
source = drop(tmp_path / "round", "n500.md", DOCUMENT)
|
||||
drop(tmp_path / "round", "v720.md", "V720 Tunnel: egne krav.\n")
|
||||
run(tmp_path, plan=build_plan(source.read_bytes(), DOCUMENT))
|
||||
assert len(concepts(tmp_path / "bundle")) == 6
|
||||
|
||||
|
||||
def test_an_unreadable_dropped_file_does_not_mask_an_unmatched_plan(tmp_path: Path) -> None:
|
||||
"""A file the run could not read is not evidence that the plan matched.
|
||||
|
||||
Phase 1 skips a file it cannot read and records a `FailedFile`. If the
|
||||
unmatched-plan check asked "was any file left unexamined?" instead of "was
|
||||
a covering plan actually found?", an unreadable drop would suppress the
|
||||
refusal and restore exactly the silence this closes. The plan here is
|
||||
hashed over the unreadable file's OWN bytes, so it is the only drop that
|
||||
could ever have matched.
|
||||
"""
|
||||
drop(tmp_path / "round", "n500.md", DOCUMENT)
|
||||
other = "V720 Tunnel: egne krav.\n"
|
||||
unreadable = drop(tmp_path / "round", "locked.md", other)
|
||||
unreadable.chmod(0o000)
|
||||
try:
|
||||
# The premise, asserted rather than assumed: a test that ran as root
|
||||
# would read the file fine and pass for the wrong reason.
|
||||
with pytest.raises(OSError):
|
||||
unreadable.read_bytes()
|
||||
plan = build_plan(other.encode("utf-8"), other, paths=("krav/tunnel.md",))
|
||||
with pytest.raises(SegmentationError) as excinfo:
|
||||
run(tmp_path, plan=plan)
|
||||
assert excinfo.value.code == "segmentation_plan_unmatched"
|
||||
assert tree(tmp_path / "bundle") == {}
|
||||
finally:
|
||||
unreadable.chmod(0o644)
|
||||
|
||||
|
||||
def test_a_matched_plan_with_a_refused_path_keeps_its_own_per_file_code(tmp_path: Path) -> None:
|
||||
"""A matched plan with bad entries is not an unmatched plan.
|
||||
|
||||
Written because the first cut of the check got this backwards: it asked
|
||||
whether any document reached the naming stage as covered, so a plan whose
|
||||
hash matched but whose entry paths were then refused looked identical to a
|
||||
plan that matched nothing. The operator would have been told to check a
|
||||
hash that was already correct. Coverage is recorded at SELECTION.
|
||||
"""
|
||||
source = drop(tmp_path / "round", "n500.md", DOCUMENT)
|
||||
plan = build_plan(source.read_bytes(), DOCUMENT, paths=("krav/" + "a" * 300 + ".md",))
|
||||
result = run(tmp_path, plan=plan)
|
||||
assert {entry.error.code for entry in result.failed} == {"inbox_slug_too_long"}
|
||||
assert tree(tmp_path / "bundle") == {}
|
||||
|
||||
|
||||
def test_without_the_capability_an_unmatched_plan_is_still_the_earlier_refusal(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""Order matters: the profile check runs first and keeps its own code.
|
||||
|
||||
Both conditions hold in this call -- no capability AND no matching file --
|
||||
and the operator's first problem is the profile, not the hash.
|
||||
"""
|
||||
source = drop(tmp_path / "round", "n500.md", DOCUMENT)
|
||||
plan = build_plan(source.read_bytes(), DOCUMENT, source_sha256="0" * 64)
|
||||
with pytest.raises(SegmentationError) as excinfo:
|
||||
run(tmp_path, plan=plan, profile=DEFAULT, values={})
|
||||
assert excinfo.value.code == "segmentation_unsupported_profile"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue