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
|
|
@ -177,6 +177,12 @@ class SegmentationError(IngestError):
|
|||
offset had silently moved
|
||||
- `segmentation_unsupported_profile` — a plan was passed to a profile that
|
||||
does not declare the segmentation capability
|
||||
- `segmentation_plan_unmatched` — the plan is well-formed but its
|
||||
`source_sha256` matches no dropped file, so nothing would be segmented
|
||||
and the run would report an ordinary success over a flat bundle. A
|
||||
mistyped hash is the likely cause and it is unreadable from the result;
|
||||
refusing is the only way the operator learns that the judgement they
|
||||
adjudicated was never replayed
|
||||
"""
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -531,6 +531,11 @@ def process_inbox(
|
|||
# would silently claim the first's concepts.
|
||||
named: list[tuple[Path, tuple[str, ...], bytes, bool]] = []
|
||||
slug_owners: dict[str, list[Path]] = {}
|
||||
# Recorded at the moment of SELECTION, not after validation: a plan whose
|
||||
# hash matched a drop but whose entry paths were then refused is a covered
|
||||
# document with a bad plan, and it must keep reporting its own per-file
|
||||
# code rather than being re-reported as a plan that matched nothing.
|
||||
plan_matched = False
|
||||
for path in dropped:
|
||||
try:
|
||||
# Read HERE rather than in the write loop: a plan is selected by
|
||||
|
|
@ -550,6 +555,7 @@ def process_inbox(
|
|||
continue
|
||||
try:
|
||||
covering = _plan_covering(segmentation, source_bytes)
|
||||
plan_matched = plan_matched or covering is not None
|
||||
targets: tuple[str, ...]
|
||||
if covering is None:
|
||||
targets = (inbox_filename(inbox_slug(path.name), profile=profile),)
|
||||
|
|
@ -562,6 +568,23 @@ def process_inbox(
|
|||
for target in targets:
|
||||
slug_owners.setdefault(target, []).append(path)
|
||||
|
||||
# A plan that covered nothing is a misuse, not an outcome. `_plan_covering`
|
||||
# selects on content hash, so a mistyped `source_sha256` matches no drop,
|
||||
# every file falls through to the one-concept rule, and the run returns an
|
||||
# ordinary success over a flat bundle -- the silent skip this library
|
||||
# refuses everywhere else. Asked as "was a covering plan actually found?"
|
||||
# rather than "was every file examined?", so a file that could not be read
|
||||
# cannot mask the refusal. Still before any disk mutation: Phase 1 only
|
||||
# named things.
|
||||
if segmentation is not None and not plan_matched:
|
||||
raise SegmentationError(
|
||||
f"the segmentation plan's source_sha256 {segmentation.source_sha256!r} matches "
|
||||
f"none of the {len(dropped)} dropped file(s) — nothing would be segmented and "
|
||||
"the run would report success over a flat bundle; check the hash against the "
|
||||
"bytes it was adjudicated over",
|
||||
code="segmentation_plan_unmatched",
|
||||
)
|
||||
|
||||
contested = {name for name, owners in slug_owners.items() if len(owners) > 1}
|
||||
# One refusal per DOCUMENT, not per contested path: a document expanding to
|
||||
# five colliding paths is one thing the operator has to fix, and five
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue