P21/C2 made a refusal for an absent path name the ancestor's SUBDIRECTORIES, and it bought what it was built for: read_dir against a level the base does not hold went from 16 of 104 to 8 of 128. It did nothing for documents -- read_file against a document the base does not hold went 2 of 38 to 7 of 52 -- and the reason is structural: the nearest listable ancestor of a guessed DOCUMENT path often holds documents and no subdirectories, and then the neighbour clause was omitted, deliberately, because an empty list is a sentence with nothing in it. Measured over round 5's six read_file misses, THREE land on such an ancestor: krav/N100 with 445 documents, and R761/1 with exactly ONE -- which two separate guesses in one run were both reaching for. The other three have subdirectories and were already answered. okf.nearest_documents is the sibling of nearest_subdirectories, never a widening of it: never both clauses, and the subdirectory branch stays FIRST, which is what keeps every C2 refusal byte-identical. Built from context_files and through the same in_dimension predicate the listing uses, so a refusal can never advertise the type: verdict layer by path, and every name it hands back resolves -- measured by feeding each one back into read_file, not by asserting the list is non-empty. A MUTATION FOUND THE RANKING UNWITNESSED, and that is recorded rather than dropped: replacing _shared_prefix with a plain reverse sort left the whole suite green. The bound, the source and the resolve property were all gated; the ORDER was not. For R761/1 that costs nothing, but a level of a delivered corpus can hold 445, and then which five it names is the whole value of the clause. The new arm builds a level where the closest name is also the LONGEST, so a length rule puts it last and an alphabetical one puts another first -- only the prefix rule puts it first. Load-bearing MEASURED (tests/test_document_neighbours_loadbearing.py, 10 arms), seven mutations all red against the WHOLE suite + green control 1891/5 (from 1881/5, superset, 0 removed) and golden demo-transcript.stdout BYTE-UNCHANGED (shasum -a 1 of the CONTENT = ea8c534773acdbe41ae68f2c55724d69aaf8be4f): C1 detach the document branch in read_file (5 red) - C2 detach it in read_dir (1) - C3 build from files (1) - C4 ignore the dimension (1) - C5 no bound (1) - C6 both clauses at once (1) - C7 a second ranking rule (1, after the test was fixed; green before, which is the finding). Honesty limits, stated: the foreign-dimension arm was VACUOUSLY green before this change (nothing was named, so nothing could leak) and is gated only now -- C4 is what makes it real; no LIVE model has read the new clause (DEL D is the measurement); and the clause is help text, not a gate -- it cannot make a guessed path right, only cheaper to correct. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
245 lines
12 KiB
Python
245 lines
12 KiB
Python
"""P22 DEL C — when the nearest listable ancestor holds documents and no subdirectories, name them.
|
|
|
|
P21/C2 made a refusal for an absent path name the ancestor's SUBDIRECTORIES, and it bought what it
|
|
was built for: ``read_dir`` against a level the base does not hold went from 16 of 104 to 8 of 128.
|
|
It did nothing for documents. Re-measured at the head of okt 126 over round 5's six debate traces,
|
|
``read_file`` against a document the base does not hold went from 2 of 38 to **7 of 52** under the
|
|
``context_files`` definition (2 of 38 to 6 of 52 under the judge's filesystem one — P21 declared
|
|
both definitions, and this file uses the ``context_files`` one because that is the property the
|
|
refusal is built from).
|
|
|
|
The reason is structural: the nearest listable ancestor of a guessed DOCUMENT path often holds
|
|
documents and no subdirectories, and then the neighbour clause was omitted — deliberately, because
|
|
an empty list is a sentence with nothing in it. Measured over those six misses, THREE land on such
|
|
an ancestor (``krav/N100`` with 445 documents; ``R761/1`` with exactly ONE, which two separate
|
|
guesses — ``R761/1/1-1.md`` and ``R761/1/R761-1-1_id-...md`` — were both reaching for) and three
|
|
have subdirectories and were already answered.
|
|
|
|
Same source and same property as its sibling: built from ``context_files`` through ``in_dimension``,
|
|
so **every name it gives back resolves** — arm (c) measures that by feeding each one back, rather
|
|
than asserting the list is non-empty.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from portfolio_optimiser import okf
|
|
from portfolio_optimiser.explore import navigator_tools
|
|
|
|
_EXAMPLES = Path(__file__).resolve().parents[1] / "shared" / "examples"
|
|
_NEIGHBOUR_MAX = 5
|
|
|
|
|
|
def _write_tree(base: Path, *, dirs: int, per_dir: int, top: int = 2) -> Path:
|
|
"""A base shaped like an ingested corpus: a root holding documents AND subdirectories, each
|
|
subdirectory holding documents and NOTHING else. The two branches this file separates."""
|
|
base.mkdir(parents=True)
|
|
root_lines = [f"# {base.name}", ""]
|
|
for t in range(top):
|
|
root_lines.append(f"- [Toppdokument {t}](topp-{t}.md)")
|
|
(base / f"topp-{t}.md").write_text(
|
|
f"---\ntype: concept\ntitle: Toppdokument {t}\n---\n\nkort tekst.\n", encoding="utf-8"
|
|
)
|
|
for d in range(dirs):
|
|
sub = base / f"kategori-{d:02d}"
|
|
sub.mkdir()
|
|
root_lines.append(f"- [kategori-{d:02d} (index)](kategori-{d:02d}/index.md)")
|
|
sub_lines = [f"# kategori-{d:02d}", ""]
|
|
for i in range(per_dir):
|
|
sub_lines.append(f"- [Dokument {i}](dok-{i:02d}.md)")
|
|
(sub / f"dok-{i:02d}.md").write_text(
|
|
f"---\ntype: concept\ntitle: Dokument {d:02d}-{i:02d}\n---\n\nkort tekst.\n",
|
|
encoding="utf-8",
|
|
)
|
|
(sub / "index.md").write_text(
|
|
"---\ntype: index\n---\n\n" + "\n".join(sub_lines) + "\n", encoding="utf-8"
|
|
)
|
|
(base / "index.md").write_text(
|
|
"---\ntype: index\n---\n\n" + "\n".join(root_lines) + "\n", encoding="utf-8"
|
|
)
|
|
return base
|
|
|
|
|
|
def _tools(base: Path) -> dict[str, Any]:
|
|
return {t.name: t for t in navigator_tools((str(base),))}
|
|
|
|
|
|
def _read_file(base: Path, path: str) -> str:
|
|
return str(_tools(base)["read_file"].func(bundle_id=base.name, path=path))
|
|
|
|
|
|
def _read_dir(base: Path, path: str) -> Any:
|
|
return _tools(base)["read_dir"].func(bundle_id=base.name, path=path)
|
|
|
|
|
|
# --- (a) the KNOWN-POSITIVE: a level with documents and no subdirectories ------------------------
|
|
|
|
|
|
def test_a_missing_document_is_answered_with_the_names_that_level_holds(tmp_path: Path) -> None:
|
|
"""RED before DEL C: the ancestor ``kategori-00`` holds documents and no subdirectories, so
|
|
P21/C2's clause was omitted and the refusal said only which rung to go back to. It now names
|
|
the documents that rung holds."""
|
|
base = _write_tree(tmp_path / "korpus", dirs=3, per_dir=4)
|
|
refusal = _read_file(base, "kategori-00/dok-99-gjettet.md")
|
|
|
|
assert refusal.startswith("REFUSED ("), refusal
|
|
assert "it holds the documents" in refusal, refusal
|
|
assert "kategori-00/dok-00.md" in refusal, refusal
|
|
|
|
|
|
def test_a_level_with_exactly_one_document_names_it(tmp_path: Path) -> None:
|
|
"""The measured ``R761/1`` case, which is the sharpest one in the round-5 trace: TWO separate
|
|
guesses at one document's name, in one run, at a level that holds exactly that one document.
|
|
Naming it answers both in a single step."""
|
|
base = _write_tree(tmp_path / "korpus", dirs=1, per_dir=1)
|
|
for guess in ("kategori-00/1-1.md", "kategori-00/kat-1-1_id-abc.md"):
|
|
refusal = _read_file(base, guess)
|
|
assert "kategori-00/dok-00.md" in refusal, (guess, refusal)
|
|
|
|
|
|
# --- (b) the KNOWN-NEGATIVE: a level that HAS subdirectories is answered as before ---------------
|
|
|
|
|
|
def test_a_level_with_subdirectories_still_names_the_subdirectories(tmp_path: Path) -> None:
|
|
"""The control, and the half that keeps DEL C from being a rewrite of C2. The root holds BOTH
|
|
documents and subdirectories; the subdirectory branch stays first, so every C2 refusal is
|
|
unchanged — and the two clauses are never both present, because the ancestor is one level and
|
|
naming its documents when it also has subdirectories answers a different question."""
|
|
base = _write_tree(tmp_path / "korpus", dirs=3, per_dir=4)
|
|
refusal = _read_file(base, "finnes-ikke.md")
|
|
|
|
assert "its subdirectories include" in refusal, refusal
|
|
assert "it holds the documents" not in refusal, refusal
|
|
|
|
|
|
# --- (c) every name it gives back RESOLVES -------------------------------------------------------
|
|
|
|
|
|
def test_every_named_document_can_then_be_read(tmp_path: Path) -> None:
|
|
"""The ``_index_excerpt`` property, measured rather than assumed: a refusal that hands back a
|
|
path the next call refuses is worse than one that hands back nothing. Each named document is
|
|
fed straight back into ``read_file``."""
|
|
base = _write_tree(tmp_path / "korpus", dirs=2, per_dir=4)
|
|
refusal = _read_file(base, "kategori-01/dok-99.md")
|
|
named = refusal.split("it holds the documents ")[1].split(")")[0].split(", ")
|
|
|
|
assert named, refusal
|
|
for path in named:
|
|
answer = _read_file(base, path.strip())
|
|
assert not answer.startswith("REFUSED ("), f"{path!r} does not resolve: {answer}"
|
|
|
|
|
|
# --- (d) the bound ------------------------------------------------------------------------------
|
|
|
|
|
|
def test_the_document_list_is_bounded(tmp_path: Path) -> None:
|
|
"""A level of a delivered corpus can hold hundreds — ``krav/N100`` holds 445. The list is the
|
|
same fixed five its sibling uses, for the same reason: this is help text on a refusal, and its
|
|
price must not be set by how much the level contains."""
|
|
base = _write_tree(tmp_path / "korpus", dirs=1, per_dir=40)
|
|
refusal = _read_file(base, "kategori-00/dok-99-gjettet.md")
|
|
named = refusal.split("it holds the documents ")[1].split(")")[0].split(", ")
|
|
assert len(named) == _NEIGHBOUR_MAX, named
|
|
|
|
|
|
def test_the_closest_name_comes_first(tmp_path: Path) -> None:
|
|
"""The RANKING, and it is here because a mutation found it unwitnessed.
|
|
|
|
Replacing ``_shared_prefix`` with a plain reverse sort left the whole suite green: the bound,
|
|
the source and the resolve property were all gated, and the ORDER was not. For the measured
|
|
``R761/1`` case that costs nothing — one document, one answer — but a level of a delivered
|
|
corpus can hold 445, and then which five it names is the whole value of the clause.
|
|
|
|
The rule is the sibling's, through the SAME ``_shared_prefix`` helper rather than a second copy
|
|
(kø-(p)): longest common prefix with the segment that failed, then shortest, then name. A
|
|
ranking on help text cannot refuse anything, so its failure direction stays benign — at worst
|
|
it names five real documents that are not the one meant.
|
|
"""
|
|
base = tmp_path / "korpus"
|
|
base.mkdir(parents=True)
|
|
names = ["asfalt-slitelag-2024.md", "asfalt-dekke.md", "betong.md", "grus.md"]
|
|
lines = [f"# {base.name}", ""]
|
|
for name in names:
|
|
lines.append(f"- [{name}]({name})")
|
|
(base / name).write_text(
|
|
f"---\ntype: concept\ntitle: {name}\n---\n\nkort tekst.\n", encoding="utf-8"
|
|
)
|
|
(base / "index.md").write_text(
|
|
"---\ntype: index\n---\n\n" + "\n".join(lines) + "\n", encoding="utf-8"
|
|
)
|
|
|
|
refusal = _read_file(base, "asfalt-slitelag.md")
|
|
named = refusal.split("it holds the documents ")[1].split(")")[0].split(", ")
|
|
# It shares the longest prefix AND is the longest name, so a length-only rule puts it last and
|
|
# a reverse-alphabetical one puts ``grus.md`` first. Only the prefix rule puts it first.
|
|
assert named[0] == "asfalt-slitelag-2024.md", named
|
|
assert named[1] == "asfalt-dekke.md", named
|
|
|
|
|
|
# --- (e) the other call site ---------------------------------------------------------------------
|
|
|
|
|
|
def test_read_dir_gets_the_same_answer(tmp_path: Path) -> None:
|
|
"""ONE helper for both refusal sites (kø-(p)): ``read_dir``'s own absent-path refusal and
|
|
``read_file``'s answer the same question about the same base, and two copies would be free to
|
|
give a caller two answers about one level."""
|
|
base = _write_tree(tmp_path / "korpus", dirs=1, per_dir=4)
|
|
answer = _read_dir(base, "kategori-00/finnes-ikke")
|
|
blob = json.dumps(answer, ensure_ascii=False)
|
|
assert "it holds the documents" in blob, blob
|
|
assert "kategori-00/dok-00.md" in blob, blob
|
|
|
|
|
|
# --- (f)/(g) built from context_files, through in_dimension --------------------------------------
|
|
|
|
|
|
def test_the_verdict_layer_is_never_named_in_a_refusal(tmp_path: Path) -> None:
|
|
"""Built from ``context_files``, never ``files``. A list read off the filesystem would
|
|
advertise, in a REFUSAL, the one layer no listing mentions and ``read_file`` then refuses
|
|
outright — the same leak in an apology's clothing."""
|
|
base = _write_tree(tmp_path / "korpus", dirs=1, per_dir=2)
|
|
verdict = base / "kategori-00" / "dom.md"
|
|
verdict.write_text(
|
|
"---\ntype: verdict\ntitle: En tidligere dom\n---\n\nkropp.\n", encoding="utf-8"
|
|
)
|
|
index = base / "kategori-00" / "index.md"
|
|
index.write_text(index.read_text(encoding="utf-8") + "- [Dom](dom.md)\n", encoding="utf-8")
|
|
|
|
refusal = _read_file(base, "kategori-00/dok-99.md")
|
|
assert "dom.md" not in refusal, refusal
|
|
assert "kategori-00/dok-00.md" in refusal, refusal
|
|
|
|
|
|
def test_a_foreign_dimension_document_is_never_named(tmp_path: Path) -> None:
|
|
"""Through the SAME ``in_dimension`` predicate the listing uses. Naming a document the run
|
|
would refuse to open a moment later is S2c's "a filter in name only", one rung over."""
|
|
base = _write_tree(tmp_path / "korpus", dirs=1, per_dir=1)
|
|
foreign = base / "kategori-00" / "fremmed.md"
|
|
foreign.write_text(
|
|
"---\ntype: concept\ndimension: asfalt\ntitle: Fremmed\n---\n\nkropp.\n", encoding="utf-8"
|
|
)
|
|
index = base / "kategori-00" / "index.md"
|
|
index.write_text(
|
|
index.read_text(encoding="utf-8") + "- [Fremmed](fremmed.md)\n", encoding="utf-8"
|
|
)
|
|
|
|
tools = {t.name: t for t in navigator_tools((str(base),), dimension="energi")}
|
|
refusal = str(tools["read_file"].func(bundle_id=base.name, path="kategori-00/dok-99.md"))
|
|
assert "fremmed.md" not in refusal, refusal
|
|
|
|
|
|
# --- (h) the shipped nested golden is untouched --------------------------------------------------
|
|
|
|
|
|
def test_the_shipped_nested_golden_still_navigates_unchanged() -> None:
|
|
"""A commons-owned base, read the way the nav-goldens read it: the helper is additive, and the
|
|
property the byte-exact goldens rest on is that nothing about navigation moved."""
|
|
bundle = okf.navigate_bundle(str(_EXAMPLES / "nav-golden-hierarchy" / "bundle"))
|
|
assert [f.name for f in bundle.context_files] == [
|
|
"overview.md",
|
|
"a/doc-a.md",
|
|
"a/b/doc-b.md",
|
|
]
|