feat(p22): a refusal names the DOCUMENTS when the ancestor has no subdirectories

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>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-16 01:17:39 +02:00
commit f8709ec228
5 changed files with 373 additions and 14 deletions

View file

@ -3054,6 +3054,42 @@ Python ≥3.10. MAF (`agent-framework-core` 1.16.0, `-orchestrations` 1.1.1 —
(structured-output-grensens klasse) — DEL D er maalingen; rapporten kan ikke si at et krav ER
riktig, bare at det ikke deler ett ord med retningen; og funn 4 (`named` 1/20) er denne saken
sett fra den andre siden, saa den maales av DEL D og ikke av en egen soem.
- **Naar forfaren ikke har underkataloger, navngir nekten DOKUMENTENE den holder (P22 DEL C,
16.09):** P21/C2 lot en nekt for en fravaerende sti navngi forfarens UNDERKATALOGER, og den kjoepte
hva den var bygget for — `read_dir` mot et nivaa basen ikke holder gikk 16/104 → 8/128. Den gjorde
INGENTING for dokumenter: `read_file` mot et dokument basen ikke holder gikk **2/38 → 7/52**.
Grunnen er strukturell, ikke tilfeldig: den naermeste listbare forfaren til en GJETTET dokumentsti
holder ofte dokumenter og ingen underkataloger, og da ble naboklausulen utelatt — BEVISST, fordi en
tom liste er en setning uten innhold. **MAALT over runde 5s seks `read_file`-bom: TRE lander paa en
slik forfar** (`krav/N100` med 445 dokumenter; `R761/1` med NOEYAKTIG ETT — som to separate
gjetninger i samme kjoering, `R761/1/1-1.md` og `R761/1/R761-1-1_id-...md`, begge strakte seg
etter), og tre har underkataloger og var alt besvart. `okf.nearest_documents` er soesknet til
`nearest_subdirectories`, ALDRI en utvidelse av den: **aldri begge klausuler** (forfaren er ETT
nivaa, og aa navngi dens dokumenter naar den ogsaa har underkataloger besvarer et annet spoersmaal
enn det kalleren stilte), og underkatalog-grenen staar FOERST, hvilket er dét som holder hver
C2-nekt byte-identisk (M C6 → 1 roed). **Bygget fra `context_files`, ALDRI `files`** — en liste
lest av filsystemet ville reklamert, i en NEKT, for `type: verdict`-laget som ingen listing nevner
og `read_file` straks avviser blankt: samme lekkasje i unnskyldningens klaer (M C3 → 1 roed) — og
gjennom SAMME `in_dimension`-predikat listingen bruker (M C4 → 1 roed). **Hvert navn RESOLVERER**,
maalt ved aa mate hvert av dem tilbake inn i `read_file`, aldri ved aa asserte at lista er
ikke-tom. Bundet til fem (M C5 → 1 roed), og **ÉN hjelper for BEGGE nekt-steder** (`read_dir`s egen
og `read_file`s — kø-(p): ett spoersmaal om én base maa ikke ha to svar; M C1 → 5 roede, M C2 → 1).
**`_shared_prefix` er den ene rangeringsregelen, delt av begge soesken**, og det staar her fordi en
MUTASJON FANT DEN UVITNET: aa bytte den mot en ren revers-sortering lot HELE suiten staa groenn
(1890/5) — bindingen, kilden og resolver-egenskapen var alle gatet, og REKKEFOELGEN var det ikke.
For `R761/1` koster det ingenting (ett dokument, ett svar), men et nivaa i et levert korpus kan
holde 445, og da ER hvilke fem den navngir hele verdien av klausulen. Den nye armen bygger et nivaa
der det naermeste navnet ogsaa er det LENGSTE, saa en lengde-regel legger det sist og en
alfabetisk legger et annet foerst — bare prefiks-regelen legger det foerst (M C7 → 1 roed etter
rettelsen, groenn foer). Load-bearing MAALT
(`tests/test_document_neighbours_loadbearing.py`, 10 armer), **sju mutasjoner alle roede mot HELE
suiten** + groenn kontroll **1891/5** (fra 1881/5, supersett, 0 fjernet) og golden BYTE-UENDRET
(`shasum -a 1` av INNHOLDET = `ea8c534773acdbe41ae68f2c55724d69aaf8be4f`). **AErlighets-grenser,
uttalt:** armen for fremmed dimensjon var VAKUOEST GROENN foer endringen (ingenting ble navngitt,
saa ingenting kunne lekke) og er gatet foerst naa — M C4 er dét som gjoer den ekte; ingen LEVENDE
modell har lest den nye klausulen (structured-output-grensens klasse), DEL D er maalingen; og
klausulen er hjelpetekst, ikke en gate — den kan ikke gjoere en gjettet sti riktig, bare billigere
aa rette.
- **STATE.md er local-only** (gitignored). Voyage session-state er efemert; STATE.md er kanonisk kontinuitet.
- Prosess: Voyage-plugin (`/trekbrief → /trekplan → /trekexecute → /trekreview`) per større fase.

View file

@ -796,7 +796,11 @@ when the seam is detached, so the loop cannot silently degrade into theater.
none of the thirteen named a right one. The refusal is a turn the model can correct, and it
carries both numbers. A path the base does not hold is likewise refused by name, and the refusal
now lists up to five subdirectories of the nearest directory that does hold documents — every one
of them a path `read_dir` will answer for.
of them a path `read_dir` will answer for. When that directory has no subdirectories at all, the
refusal names up to five of the DOCUMENTS it holds instead, on the same rule and with the same
property: each name resolves. (Measured over one stress round, three of six guessed document
paths landed on such a directory and got no suggestion at all — including two separate guesses,
in one run, at the name of the single document that level holds.)
Every tool call recorded in `{run_id}-exploration.json` and `{run_id}-debate.json` says HOW the
level was asked for — `filter`, `offset` and `limit` beside the tool name, the base and the path

View file

@ -1087,7 +1087,9 @@ def _refusal_kind(exc: Exception) -> str:
return type(exc).__name__
def _neighbours(bundle_dir: str, path: str, dimension: str | None) -> tuple[str, tuple[str, ...]]:
def _neighbours(
bundle_dir: str, path: str, dimension: str | None
) -> tuple[str, tuple[str, ...], tuple[str, ...]]:
"""``(nearest listable ancestor, up to five of its subdirectories)`` for a path that is absent.
ONE navigation for both halves, and BOTH read off ``okf`` rather than reimplemented here: this
@ -1099,6 +1101,7 @@ def _neighbours(bundle_dir: str, path: str, dimension: str | None) -> tuple[str,
return (
okf.nearest_listable_directory(bundle, path, dimension=dimension),
okf.nearest_subdirectories(bundle, path, dimension=dimension),
okf.nearest_documents(bundle, path, dimension=dimension),
)
@ -1383,14 +1386,22 @@ def navigator_tools(
# link -- still propagates untouched, because a refusal is a statement about the CALLER's
# path and a failure to read something that IS there is not one.
if not resolved.exists():
ancestor, neighbours = _neighbours(bundle_dir, path, dimension)
ancestor, neighbours, documents = _neighbours(bundle_dir, path, dimension)
# P21/C2: the nearest listable ancestor AND up to five of its own subdirectories. The
# ancestor alone says which rung to go back to; the neighbours say which names that
# rung actually uses — measured, one run spent eleven calls walking ``R761/4-3``,
# ``4.3``, ``4-2``, ``4-1``, ``4-0``, ``4-5``, ``4-6`` while the real names were
# ``R761/4``, ``R761/41``, ``R761/42``. Omitted when the ancestor has no
# subdirectories: an empty list would be a sentence with nothing in it.
nearby = f" (its subdirectories include {', '.join(neighbours)})" if neighbours else ""
# P22 DEL C: subdirectories when that rung has any, otherwise the DOCUMENTS it
# holds - measured, three of round 5's six misses landed on an ancestor with no
# subdirectories, and the clause was omitted for all three. Never both, and the
# subdirectory branch stays first, which is what keeps every C2 refusal unchanged.
nearby = ""
if neighbours:
nearby = f" (its subdirectories include {', '.join(neighbours)})"
elif documents:
nearby = f" (it holds the documents {', '.join(documents)})"
raise okf.BundlePathNotFound(
f"knowledge base {bundle_id!r} has no document {path!r}; nearest directory that "
f"holds documents: {ancestor!r}{nearby} — list it "

View file

@ -1317,6 +1317,21 @@ def nearest_listable_directory(bundle: Bundle, path: str, *, dimension: str | No
return ""
def _shared_prefix(name: str, missing: str) -> int:
"""How many leading characters ``name`` shares with the path segment that failed.
The ONE ranking rule behind both neighbour helpers (ko-(p)). Two copies of "which of these
names did the caller mean" would be free to rank one base's level two ways in two refusals
about the same call.
"""
shared = 0
for a, b in zip(name, missing):
if a != b:
break
shared += 1
return shared
def nearest_subdirectories(
bundle: Bundle,
path: str,
@ -1360,18 +1375,56 @@ def nearest_subdirectories(
if len(rest) > 1:
children.add(rest[0])
def _shared(name: str) -> int:
n = 0
for a, b in zip(name, missing):
if a != b:
break
n += 1
return n
ranked = sorted(children, key=lambda n: (-_shared(n), len(n), n))
ranked = sorted(children, key=lambda n: (-_shared_prefix(n, missing), len(n), n))
return tuple(f"{prefix}{name}" for name in ranked[:limit])
def nearest_documents(
bundle: Bundle,
path: str,
*,
dimension: str | None = None,
limit: int = _NEIGHBOUR_LIMIT,
) -> tuple[str, ...]:
"""The concept documents sitting directly in the nearest listable ancestor of an absent path.
**The measured gap (P22 DEL C).** P21/C2 named the ancestor's SUBDIRECTORIES, and it moved
``read_dir`` against a level the base does not hold 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,
because the nearest listable ancestor of a guessed DOCUMENT path often holds documents and no
subdirectories at all - and then the neighbour clause was omitted, deliberately, since an empty
list is a sentence with nothing in it. Measured over round 5's six ``read_file`` misses: three
have an ancestor with no subdirectories (``krav/N100`` with 445 documents, and ``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). The other three have subdirectories and
are already answered by ``nearest_subdirectories``.
**Every name it returns RESOLVES**, the same property and by the same construction as its
sibling: built from ``context_files`` and through the SAME ``in_dimension`` predicate the
listing uses, so a name handed back in a refusal is one ``read_file`` will then serve - and can
never be a ``type: verdict`` document, the one layer no listing mentions.
**Ranked by the same rule**, through the same ``_shared_prefix`` helper rather than a second
copy of it (ko-(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 is benign.
"""
ancestor = nearest_listable_directory(bundle, path, dimension=dimension)
prefix = f"{ancestor}/" if ancestor else ""
depth = len(prefix.split("/")) - 1 if prefix else 0
segments = path.strip("/").split("/")
missing = segments[depth] if len(segments) > depth else ""
here = [
file.name
for file in bundle.context_files
if in_dimension(file, dimension)
and file.name.startswith(prefix)
and "/" not in file.name[len(prefix) :]
]
ranked = sorted(here, key=lambda n: (-_shared_prefix(n[len(prefix) :], missing), len(n), n))
return tuple(ranked[:limit])
def directory_listing(
bundle: Bundle,
path: str = "",
@ -1493,7 +1546,17 @@ def directory_listing(
# one this same function would then answer for.
neighbours = nearest_subdirectories(bundle, path, dimension=dimension)
ancestor = nearest_listable_directory(bundle, path, dimension=dimension)
nearby = f"; its subdirectories include {', '.join(neighbours)}" if neighbours else ""
# P22 DEL C: subdirectories when that rung has any, otherwise the DOCUMENTS it holds. Never
# both - the ancestor is one level, and naming its documents when it also has
# subdirectories would answer a different question than the one the caller asked. The
# branch order is what keeps every C2 refusal byte-identical.
nearby = ""
if neighbours:
nearby = f"; its subdirectories include {', '.join(neighbours)}"
else:
documents = nearest_documents(bundle, path, dimension=dimension)
if documents:
nearby = f"; it holds the documents {', '.join(documents)}"
raise BundlePathNotFound(
f"knowledge base {bundle.dir!r} has no directory {path!r}; it holds no concept "
f"document under that path. Nearest directory that does: {ancestor!r}{nearby}"

View file

@ -0,0 +1,245 @@
"""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
(-(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",
]