feat(inbox): walk the drop directory recursively
Door B listed `inbox.iterdir()` and kept only top-level files. A file in a subdirectory was neither ingested nor refused: it appeared in none of the result's buckets, so a nested drop produced a bundle that was silently short of what was dropped and no count said so. That broke the K1b identity for any inbox with folders in it. Operator decision 2026-09-06. - `walk_inbox` is the ONE walk rule, shared with `tools/okf_corpus_run.py`: the denominator N is now counted over exactly the set of files the door ingests, rather than over a second listing that happened to agree. - Sorted on the whole relative path, not the basename, so the order is a function of the tree; that is what keeps rebuild-from-scratch byte-equal to an incremental update. - A concept's `source_file` is the path relative to the inbox root, `/`-separated. The concept NAME still comes from the basename, so two folders holding one basename hit the existing §3 collision refusal instead of one silently claiming the other's concept. - Dot-directories and a bundle directory inside the inbox are skipped with a CODE, in a new `InboxResult.skipped`. Recursion makes the door's own output reachable as its own input; a silent skip would be the same absence-without-a-denominator defect one level down. - `--path-prefix` reduces per component and rejoins with `/`, so the caller driving a nested corpus can carry the relative directory. Reducing the whole string folded the separator into a `-` and flattened `sub/sub2`. `tests/test_inbox_flow.py::test_subdirectories_are_not_walked` asserted the opposite and is superseded in place, with the reason written down. Measured on the K2 corpus (flat, N=43): 39/43 merged, 4 coded, K1b holds. The bundle digest is `1472e98aec8643c5beee540f4c42b5e437bd26e7c61d69a91bcff799f06a6d13` over 1108 files -- byte-identical to a run of the same corpus at190086fWITHOUT this change (`diff -r` exit 0), so recursion costs a flat inbox nothing. It differs from the stored 2026-09-03 artifact by one line in `index.md` (`- [Corpus run history](log.md)`), which95eb271added 15 hours after that bundle was built. Suite 1113 passed, `ruff` clean, `mypy --strict src/ tools/` clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
190086fc3d
commit
aa87eb8818
11 changed files with 489 additions and 39 deletions
|
|
@ -304,6 +304,41 @@ def test_a_path_prefix_scopes_every_entry_under_one_directory(tmp_path: Path) ->
|
|||
assert parse_segmentation_plan(payload).entries
|
||||
|
||||
|
||||
def test_a_multi_component_prefix_carries_the_relative_directory(tmp_path: Path) -> None:
|
||||
"""Door B now walks the inbox recursively and records a `source_file`
|
||||
relative to its root, so the caller driving a nested corpus has a
|
||||
DIRECTORY, not a name, to scope by. Each component is reduced on its own
|
||||
and rejoined with `/`: reducing the whole string would fold the separator
|
||||
into a `-` and flatten `sub/sub2` into one component named `sub-sub2`,
|
||||
which is a different bundle shape than the inbox it came from.
|
||||
"""
|
||||
out = tmp_path / "plan.json"
|
||||
assert (
|
||||
okf_propose_segments.main(
|
||||
[str(write(tmp_path)), "--out", str(out), "--path-prefix", "Bilag 3/Del II"]
|
||||
)
|
||||
== 0
|
||||
)
|
||||
payload = json.loads(out.read_text(encoding="utf-8"))
|
||||
assert payload["entries"]
|
||||
for entry in payload["entries"]:
|
||||
assert entry["path"].startswith("bilag-3/del-ii/")
|
||||
assert parse_segmentation_plan(payload).entries
|
||||
|
||||
|
||||
def test_a_prefix_with_one_empty_component_is_refused(tmp_path: Path) -> None:
|
||||
"""`a//b` and `a/###/b` are the multi-component form of the same defect the
|
||||
single-component gate already refuses: an empty component would collapse
|
||||
the path silently rather than scope it.
|
||||
"""
|
||||
for prefix in ("sub//two", "sub/###/two", "/sub", "sub/"):
|
||||
code = okf_propose_segments.main(
|
||||
[str(write(tmp_path)), "--out", str(tmp_path / "p.json"), "--path-prefix", prefix]
|
||||
)
|
||||
assert code == 2, prefix
|
||||
assert not (tmp_path / "p.json").exists(), prefix
|
||||
|
||||
|
||||
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"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue