feat(explore): stigen faar sitt manglende trinn - read_bundle gir ETT nivaa, read_dir det neste
S7a-3 pkt. 2. MAJOR-3 bygde read_bundle om fra HELE basen til en oppfoering per konseptfil. Saa kom det foerste ekte korpuset: K2 navigerer til 629 konsepter bak 478 nestede indekser, og en listing av 629 koster 42 761 o200k-tokens som rir i 7 av 12 prompter = 89 % av alle prompt-tokens. Bindingen holdt asymptotisk og priset likevel hele korpuset. De 478 indeksene ble bygget, konsumert og flatet ut - agenten saa 629 soesken og fikk aldri vite at korpuset hadde en form. MAALT (BEFORE og AFTER i samme oekt, samme kode, BEFORE som mutasjon): read_bundle-nyttelast 110 581 tegn / 42 761 tok -> 3 954 tegn / 1 495 tok listing-tokens totalt 307 573 (89 %) -> 12 595 (26 %) prompt-tokens i kjoeringen 343 826 -> 49 225 (-86 %) BEFORE reproduserer S7a-2s publiserte tall til 0,03 % - kjent-positiv kontroll paa instrumentet, som ogsaa maatte rettes (resultatet baerer name=None, saa en sonde nøklet paa verktoeynavn rapporterer 0 kopier og leses som en ekte null). - okf.directory_listing er ENESTE renderer; begge verktoey ER den paa hvert sitt nivaa. Kataloger utledes av STIER, aldri av index.md. Bygget av context_files, ALDRI files. Hver sti er bundle-relativ, brukbar ordrett i neste kall. - Ukjent sti NEKTES ved navn (BundlePathNotFound) - en tom listing er umulig aa skille fra en katalog som finnes og er tom. - Verktoeybeskrivelsene og navigatoerinstruksjonen flyttet i SAMME commit. PREMISS FELT FOER BYGGING: context_files har aldri holdt hierarkiet tilbake - navnene er fulle bundle-relative stier; det var RENDERINGEN som flatet det ut. Derfor er bundle_context og begge nav-goldenene byte-identiske, gratis. AVVIK fra ordren, uttalt: K2 kan ikke vaere testavhengighet (utenfor repoet), og 1 500 tegn er ikke oppnaaelig for en rot med 39 identifiserbare oppfoeringer (maalt 3 954). Gaten binder 1 500 tegn per listing over basene den KAN se, pluss egenskapen, med en FLAT kontroll over 5x taket. Load-bearing MAALT: 9 mutasjoner alle roede mot HELE suiten, groenn kontroll 1252 passed / 5 skipped, golden byte-uendret. N1 4 / N2 7 / N3 12 / N4 5 / N5 1 / N6 6 / N7 1 / N8 2 / N9 1. Maaling: docs/2026-09-03-hierarkisk-navigasjon-k2.md Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
baae7507a9
commit
baa6f450e8
8 changed files with 584 additions and 40 deletions
|
|
@ -244,10 +244,10 @@ def test_explore_read_bundle_reconciles_the_base_it_opens(_spy: list[str]) -> No
|
|||
"""(h) The tool is driven DIRECTLY: measured in okt 56, a scripted client returns TEXT and never
|
||||
emits a tool call, so a gate that only drove ``explore()`` would never enter this body."""
|
||||
tools = {t.name: t for t in explore.navigator_tools((str(_BYGG),))}
|
||||
# See test_read_bundle_cost_loadbearing: the tool returns a LISTING since S2c, and ``!= ""``
|
||||
# holds for every list ever built — this arm is about the reconciliation, so the call must
|
||||
# still be a call that DID something.
|
||||
assert [e["name"] for e in tools["read_bundle"].func(bundle_id=_BYGG.name)]
|
||||
# See test_read_bundle_cost_loadbearing: the tool returns a LISTING since S2c and ONE LEVEL of
|
||||
# one since S7a-3, and ``!= ""`` holds for every container ever built — this arm is about the
|
||||
# reconciliation, so the call must still be a call that DID something.
|
||||
assert [e["name"] for e in tools["read_bundle"].func(bundle_id=_BYGG.name)["documents"]]
|
||||
assert str(_BYGG) in _spy
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -683,9 +683,13 @@ async def test_an_exploration_leaves_the_knowledge_base_byte_identical(tmp_path:
|
|||
# Every read tool, called on the same base, with model-shaped arguments.
|
||||
tools = {t.name: t for t in explore.navigator_tools((str(base),))}
|
||||
assert tools["list_bundles"].func()[0]["id"] == base.name
|
||||
# A LISTING since S2c, so ``!= ""`` would be vacuously true for any list at all — assert the
|
||||
# shape the tool now promises instead of the shape it used to.
|
||||
assert [e["name"] for e in tools["read_bundle"].func(bundle_id=base.name)]
|
||||
# A LISTING since S2c and ONE LEVEL of one since S7a-3, so ``!= ""`` would be vacuously true
|
||||
# for any container at all — assert the shape the tool now promises instead of the one it used
|
||||
# to. ``read_dir`` is driven too: this arm's whole claim is that EVERY read tool leaves the base
|
||||
# untouched, so a tool it does not call is a hole in it. ``path=""`` is the root level, which is
|
||||
# the only path a flat base has.
|
||||
assert [e["name"] for e in tools["read_bundle"].func(bundle_id=base.name)["documents"]]
|
||||
assert tools["read_dir"].func(bundle_id=base.name, path="")["documents"]
|
||||
assert tools["read_file"].func(bundle_id=base.name, path="index.md") != ""
|
||||
explore.quick_validate_tool((str(base),)).func(
|
||||
bundle_id=base.name, proposal_json=json.dumps(_micro_projection())
|
||||
|
|
|
|||
291
tests/test_hierarchical_navigation_loadbearing.py
Normal file
291
tests/test_hierarchical_navigation_loadbearing.py
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
"""S7a-3 pkt. 2 - the ladder gains its missing rung: ``read_bundle`` -> ``read_dir`` -> ``read_file``.
|
||||
|
||||
**The measurement** (``docs/2026-09-03-syretest-s7a2-k2.md``). MAJOR-3 (session 77) rebuilt
|
||||
``read_bundle`` from "the whole navigated base" into "one entry per concept document", and on the
|
||||
three example bases that was a 77-91 % cut. Then the first real corpus arrived: K2 navigates to
|
||||
**629 concept documents behind 478 nested indexes**, and a listing of 629 documents costs
|
||||
**42 761 o200k tokens** and rides in 7 of 12 prompts - **90 % of every prompt token in the run**.
|
||||
The bound held asymptotically and still priced the whole corpus, because the rung was priced by the
|
||||
size of the base rather than by what the navigator had asked to see.
|
||||
|
||||
Worse, the 478 nested indexes were BUILT by the navigation, CONSUMED by it, and then **flattened
|
||||
away**: the agent saw 629 sibling documents and was never told the corpus had a shape.
|
||||
|
||||
**A premise of the order, corrected by measurement.** The order says ``context_files`` must stop
|
||||
discarding the hierarchy. It never held it back: every ``BundleFile.name`` is already the full
|
||||
bundle-relative posix path (``del-ii-bilag-7-prisskjema/prissammenstilling-sheet-1.md``), so the
|
||||
tree was always recoverable from the names alone. What flattened it was the RENDERING. That matters
|
||||
because it is why ``okf.bundle_context`` and both nav-goldens are byte-identical after this change -
|
||||
the order requires exactly that, and it comes for free rather than by care.
|
||||
|
||||
So: ``okf.directory_listing`` is ONE renderer, and BOTH tools are it at a different path.
|
||||
``read_bundle`` is the root level (subdirectories with a document count, plus the documents that sit
|
||||
at the top), ``read_dir(id, path)`` is one level down, ``read_file(id, path)`` is unchanged. Every
|
||||
byte is still exactly one call away; what changed is that a navigator pays for the level it asked
|
||||
for.
|
||||
|
||||
**Directories are derived from PATHS, never from ``index.md`` files.** A nested index is navigation,
|
||||
not content (``context_files`` drops it at every level, and must keep doing so or the verdict layer
|
||||
comes with it). Deriving the tree from names means a directory reached by a link that skipped its
|
||||
index is still visible - and it means the two renderings of one bundle cannot disagree about what
|
||||
the concepts are.
|
||||
|
||||
**DEVIATION from the order, stated and measured.** The order words the ceiling as "``read_bundle``
|
||||
over the K2 bundle < 1 500 chars". K2 lives outside the repository (``~/corpora/``) and cannot be a
|
||||
test dependency - the MAJOR-3 gate had the same constraint and solved it the same way, by bounding
|
||||
the largest base that IS shipped. And the number does not hold: K2's root level carries **39
|
||||
identifiable entries** (28 directories + 11 top-level documents) and measures **3 954 chars ~ 1 366
|
||||
o200k tokens**. Fitting 1 500 would mean dropping titles or dropping entries, which is the
|
||||
vacuous-gate class this repo has been bitten by twelve times. The measured drop is from 42 761 to
|
||||
1 366 tokens (-97 %); the K2 numbers live in the report, and the gate here bounds what it can
|
||||
actually see, plus the PROPERTY that produced the drop (arm (b)).
|
||||
|
||||
Arms: (a) a FLAT base lists exactly what it listed before * (b) cost tracks entries at ONE LEVEL,
|
||||
not documents in the base - with the flat control that proves the hierarchy did it * (c) ``read_dir``
|
||||
over the biggest directory is bounded too * (d) anti-vacuity: every document is still reachable, and
|
||||
the counts add up * (e) the verdict layer stays out of both halves * (f) the ladder is intact *
|
||||
(g) an unknown path is refused BY NAME, never rendered as an empty directory * (h) the navigator's
|
||||
instruction and the tool descriptions describe the ladder they now have.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
from portfolio_optimiser import explore, okf
|
||||
from portfolio_optimiser.explore import navigator_tools
|
||||
|
||||
_EXAMPLES = Path(__file__).resolve().parents[1] / "shared" / "examples"
|
||||
#: The largest FLAT base shipped - the one the MAJOR-3 gate bounds.
|
||||
_TUNNEL = _EXAMPLES / "tunnel-hauglia"
|
||||
#: The only NESTED base shipped, and it is tiny: three levels, six concepts. It proves the shape is
|
||||
#: read correctly; it cannot prove the shape pays for itself, which is what arm (b) is for.
|
||||
_NESTED = _EXAMPLES / "nav-golden-hierarchy" / "bundle"
|
||||
|
||||
#: Characters ONE listing may cost - test-owned, for the catalogue gate's reason: a test that
|
||||
#: imported the implementation's budget would move with it, and raising the budget is precisely the
|
||||
#: regression this file exists to catch. Same number as the MAJOR-3 gate, deliberately: this rung's
|
||||
#: price must not creep just because it now has a rung below it.
|
||||
_CEILING_CHARS = 1_500
|
||||
|
||||
|
||||
def _tools(bundle_dir: Path) -> dict[str, Any]:
|
||||
return {t.name: t for t in navigator_tools((str(bundle_dir),))}
|
||||
|
||||
|
||||
def _read_bundle(bundle_dir: Path) -> dict[str, Any]:
|
||||
return _tools(bundle_dir)["read_bundle"].func(bundle_id=bundle_dir.name)
|
||||
|
||||
|
||||
def _read_dir(bundle_dir: Path, path: str) -> dict[str, Any]:
|
||||
return _tools(bundle_dir)["read_dir"].func(bundle_id=bundle_dir.name, path=path)
|
||||
|
||||
|
||||
def _blob(payload: object) -> str:
|
||||
return json.dumps(payload, ensure_ascii=False)
|
||||
|
||||
|
||||
def _entry(f: okf.BundleFile, name: str) -> dict[str, Any]:
|
||||
"""The four-field document entry MAJOR-3 introduced, written out here rather than imported: an
|
||||
assert against the implementation's own helper could not tell the two shapes apart."""
|
||||
return {
|
||||
"name": name,
|
||||
"type": f.type or "document",
|
||||
"title": okf.unquote_scalar(f.frontmatter.get("title", f.name)),
|
||||
"chars": len(f.body),
|
||||
}
|
||||
|
||||
|
||||
def _write_tree(root: Path, name: str, *, dirs: int, per_dir: int, top: int = 2) -> Path:
|
||||
"""A base shaped like a real ingested corpus: a root index linking to per-directory indexes,
|
||||
each linking to its own documents. Bodies are small on purpose - this fixture is about COUNT."""
|
||||
base = root / name
|
||||
base.mkdir(parents=True)
|
||||
root_lines = [f"# {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
|
||||
|
||||
|
||||
# --- (a) a flat base is untouched -----------------------------------------------------------------
|
||||
|
||||
|
||||
def test_a_flat_base_lists_exactly_what_it_listed_before() -> None:
|
||||
"""(a) The order names this one explicitly. Every base shipped in this repo except one is flat,
|
||||
and the whole MAJOR-3 measurement was taken over them: if the new rung changed what a flat base
|
||||
answers, this change would be a rewrite of that result rather than a level above it."""
|
||||
bundle = okf.navigate_bundle(str(_TUNNEL))
|
||||
listing = _read_bundle(_TUNNEL)
|
||||
|
||||
assert listing["documents"] == [_entry(f, f.name) for f in bundle.context_files]
|
||||
assert listing["directories"] == [], "a flat base has no subdirectories to report"
|
||||
assert len(_blob(listing)) <= _CEILING_CHARS
|
||||
|
||||
|
||||
# --- (b)/(c) the price is the LEVEL, not the base -------------------------------------------------
|
||||
|
||||
|
||||
def test_the_root_listing_costs_the_level_not_the_base(tmp_path: Path) -> None:
|
||||
"""(b) The headline, and the control that proves the hierarchy is what bounded it.
|
||||
|
||||
240 documents behind 20 directories: the root level is 22 entries and fits, while the FLAT
|
||||
listing of the same base - what ``read_bundle`` returned until today - is many times the
|
||||
ceiling. Without the flat control, a green bound here could just as well mean the fixture was
|
||||
small (Verifiseringsloven face 4: a gate that can only pass proves nothing).
|
||||
"""
|
||||
base = _write_tree(tmp_path, "stort-korpus", dirs=20, per_dir=12)
|
||||
bundle = okf.navigate_bundle(str(base))
|
||||
|
||||
listing = _read_bundle(base)
|
||||
flat = [_entry(f, f.name) for f in bundle.context_files]
|
||||
|
||||
assert len(bundle.context_files) == 242
|
||||
assert len(_blob(listing)) <= _CEILING_CHARS, (
|
||||
f"the root level costs {len(_blob(listing))} chars over the ceiling {_CEILING_CHARS}"
|
||||
)
|
||||
assert len(_blob(flat)) > 5 * _CEILING_CHARS, (
|
||||
"the CONTROL is inert: the flat listing must be the thing that does not fit, or the bound "
|
||||
"above is measuring a small fixture rather than the hierarchy"
|
||||
)
|
||||
|
||||
|
||||
def test_read_dir_over_the_biggest_directory_is_bounded_too(tmp_path: Path) -> None:
|
||||
"""(c) A rung that is only cheap at the top is not a ladder. The order asks for this one by
|
||||
name: ``read_dir`` over the largest directory must be bounded as well."""
|
||||
base = _write_tree(tmp_path, "stort-korpus", dirs=20, per_dir=12)
|
||||
|
||||
listing = _read_dir(base, "kategori-00")
|
||||
|
||||
assert len(listing["documents"]) == 12 and listing["directories"] == []
|
||||
assert len(_blob(listing)) <= _CEILING_CHARS
|
||||
|
||||
|
||||
# --- (d)/(e) bounded is not the same as empty -----------------------------------------------------
|
||||
|
||||
|
||||
def test_every_document_is_still_reachable_and_the_counts_add_up(tmp_path: Path) -> None:
|
||||
"""(d) The anti-vacuity arm. Returning ``{"directories": [], "documents": []}`` passes every
|
||||
bound above perfectly and hands the navigator nothing. Two things are asserted: each directory
|
||||
entry PRICES its subtree (so a navigator can choose against a budget), and the whole base is
|
||||
accounted for - every concept document is in exactly one level."""
|
||||
base = _write_tree(tmp_path, "stort-korpus", dirs=20, per_dir=12)
|
||||
bundle = okf.navigate_bundle(str(base))
|
||||
|
||||
root = _read_bundle(base)
|
||||
counted = len(root["documents"]) + sum(int(d["documents"]) for d in root["directories"])
|
||||
|
||||
assert counted == len(bundle.context_files) == 242
|
||||
assert {str(d["path"]) for d in root["directories"]} == {f"kategori-{d:02d}" for d in range(20)}
|
||||
assert all(int(d["documents"]) == 12 for d in root["directories"])
|
||||
for entry in root["documents"]:
|
||||
assert set(entry) == {"name", "type", "title", "chars"}
|
||||
assert str(entry["title"]).strip() and int(entry["chars"]) > 0
|
||||
|
||||
|
||||
def test_the_verdict_layer_is_excluded_from_both_halves(tmp_path: Path) -> None:
|
||||
"""(e) MAJOR-3's rule, extended to the new half: a listing built from ``bundle.files`` would
|
||||
route prior verdicts in front of the navigator AROUND the gated ExpeL fold (maalbilde §4) - and
|
||||
a DIRECTORY COUNT built from ``files`` would tell the navigator a subtree holds documents it is
|
||||
not allowed to be shown."""
|
||||
base = _write_tree(tmp_path, "med-dommer", dirs=2, per_dir=2, top=0)
|
||||
sub = base / "kategori-00"
|
||||
(sub / "dom-0.md").write_text(
|
||||
"---\ntype: verdict\ntitle: Dom\n---\n\nEksperten godkjente.\n", encoding="utf-8"
|
||||
)
|
||||
index = sub / "index.md"
|
||||
index.write_text(index.read_text(encoding="utf-8") + "- [dom](dom-0.md)\n", encoding="utf-8")
|
||||
|
||||
root = _read_bundle(base)
|
||||
inner = _read_dir(base, "kategori-00")
|
||||
|
||||
assert len(okf.navigate_bundle(str(base)).verdicts) == 1, "the fixture must HAVE one to leak"
|
||||
assert [d["documents"] for d in root["directories"] if d["path"] == "kategori-00"] == [2]
|
||||
assert not [e for e in inner["documents"] if e["type"] == "verdict"]
|
||||
assert len(inner["documents"]) == 2
|
||||
|
||||
|
||||
# --- (f)/(g) the ladder, and its fail-closed edge -------------------------------------------------
|
||||
|
||||
|
||||
def test_the_whole_document_is_still_one_call_away() -> None:
|
||||
"""(f) The bound is a disclosure LEVEL, not data loss - MAJOR-3's arm (e), re-asserted over the
|
||||
new shape because a listing that no longer names documents the way ``read_file`` takes them
|
||||
would have broken the ladder while every cost arm stayed green."""
|
||||
listing = _read_bundle(_TUNNEL)
|
||||
biggest = max(listing["documents"], key=lambda e: int(e["chars"]))
|
||||
|
||||
whole = _tools(_TUNNEL)["read_file"].func(bundle_id=_TUNNEL.name, path=str(biggest["name"]))
|
||||
|
||||
assert len(whole) > _CEILING_CHARS
|
||||
assert int(biggest["chars"]) <= len(whole)
|
||||
|
||||
|
||||
def test_a_nested_document_is_reachable_through_the_rung_below() -> None:
|
||||
"""(f, nested) The three rungs composed on the only nested base shipped: the root names a
|
||||
directory, ``read_dir`` names a document in it, ``read_file`` returns that document whole."""
|
||||
root = _read_bundle(_NESTED)
|
||||
first_dir = str(root["directories"][0]["path"])
|
||||
|
||||
inner = _read_dir(_NESTED, first_dir)
|
||||
name = str(inner["documents"][0]["name"])
|
||||
whole = _tools(_NESTED)["read_file"].func(bundle_id=_NESTED.name, path=name)
|
||||
|
||||
assert root["documents"] and root["directories"], "the fixture must have BOTH halves"
|
||||
assert name.startswith(f"{first_dir}/"), (
|
||||
"every path a listing hands out must be usable AS IT IS: a level-relative name would have "
|
||||
"to be composed by a model, and a path that never existed is worse than no path"
|
||||
)
|
||||
assert whole.strip(), "the path the listing gave must resolve to the document itself"
|
||||
|
||||
|
||||
def test_an_unknown_directory_is_refused_by_name(tmp_path: Path) -> None:
|
||||
"""(g) Fail-closed, and it is the vacuity trap in its own right: an unknown path rendered as an
|
||||
empty listing is indistinguishable from a directory that exists and holds nothing. Validation,
|
||||
never invention (``write_concept_file``'s rule)."""
|
||||
base = _write_tree(tmp_path, "korpus", dirs=2, per_dir=2)
|
||||
|
||||
with pytest.raises(ValueError, match="kategori-99"):
|
||||
_read_dir(base, "kategori-99")
|
||||
|
||||
|
||||
# --- (h) the description must not lie about the body ----------------------------------------------
|
||||
|
||||
|
||||
def test_the_navigator_is_told_about_the_rung_it_now_has() -> None:
|
||||
"""(h) The Fase-3 class applied to a tool: a description that lies about the body IS the
|
||||
model's instruction. MAJOR-3 had to move this same prose when ``read_bundle`` stopped returning
|
||||
the navigated context; the ladder is three rungs now, and all three must be named."""
|
||||
instruction = explore._INSTRUCTIONS[explore.NAVIGATOR_ROLE]
|
||||
tools = {t.name: t for t in navigator_tools((str(_NESTED),))}
|
||||
|
||||
assert set(tools) == {"list_bundles", "read_bundle", "read_dir", "read_file"}
|
||||
for rung in ("list_bundles", "read_bundle", "read_dir", "read_file"):
|
||||
assert rung in instruction, f"the navigator is never told that {rung} exists"
|
||||
assert "read_dir" in tools["read_bundle"].description, (
|
||||
"read_bundle now answers with directories; a description that does not name the tool that "
|
||||
"opens one leaves the navigator holding a path it cannot use"
|
||||
)
|
||||
|
|
@ -39,6 +39,14 @@ o200k tokens** (2.89 chars/token for this Norwegian markdown), so 1 500 characte
|
|||
growth does not force a rewrite. The order's own criterion is verified directly, once, by the
|
||||
instrument in the measurement doc.
|
||||
|
||||
**AMENDED 2026-09-03 (S7a-3 pkt. 2), and the amendment is one level, not a rewrite.** The listing
|
||||
this file bounds is now the base's TOP LEVEL rather than every document in it: on K2 the flat form
|
||||
still cost 42 761 o200k tokens across 629 documents, because O(documents in the base) is only cheap
|
||||
while the base is small. ``read_dir`` is the rung that was missing. Every arm below still holds over
|
||||
the flat example bases -- a flat base has no subdirectories, so its document list is unchanged, byte
|
||||
for byte -- and the hierarchy's own gate is ``tests/test_hierarchical_navigation_loadbearing.py``.
|
||||
``_documents()`` exists because ``len()`` of the payload is now the number of KEYS.
|
||||
|
||||
What the arms pin, and what each one refuses:
|
||||
|
||||
(a) the bound itself, over the REAL base the order names — refuses the unbounded form. A synthetic
|
||||
|
|
@ -82,10 +90,18 @@ def _tools(bundle_dir: Path) -> dict[str, Any]:
|
|||
return {t.name: t for t in navigator_tools((str(bundle_dir),))}
|
||||
|
||||
|
||||
def _listing(bundle_dir: Path) -> list[dict[str, Any]]:
|
||||
def _listing(bundle_dir: Path) -> dict[str, Any]:
|
||||
"""The WHOLE payload one ``read_bundle`` costs — since S7a-3 that is one LEVEL of the base
|
||||
(subdirectories plus the documents at this one), not every document in it."""
|
||||
return _tools(bundle_dir)["read_bundle"].func(bundle_id=bundle_dir.name)
|
||||
|
||||
|
||||
def _documents(bundle_dir: Path) -> list[dict[str, Any]]:
|
||||
"""The document half alone. A separate helper because ``len()`` of the payload is the number of
|
||||
KEYS — three, always — and an arm counting entries through it would be vacuously green."""
|
||||
return list(_listing(bundle_dir)["documents"])
|
||||
|
||||
|
||||
def _blob(payload: object) -> str:
|
||||
return json.dumps(payload, ensure_ascii=False)
|
||||
|
||||
|
|
@ -129,7 +145,7 @@ def test_read_bundle_cost_does_not_track_document_size(tmp_path: Path) -> None:
|
|||
small = _write_base(tmp_path, "small", body_chars=500)
|
||||
large = _write_base(tmp_path, "large", body_chars=5_000)
|
||||
|
||||
small_entries, large_entries = _listing(small), _listing(large)
|
||||
small_entries, large_entries = _documents(small), _documents(large)
|
||||
|
||||
# Same document COUNT, same number of entries — and the only field that grew is the honest,
|
||||
# logarithmic ``chars`` digit, so the payloads differ by a handful of characters at most.
|
||||
|
|
@ -149,7 +165,7 @@ def test_the_listing_still_identifies_every_document(tmp_path: Path) -> None:
|
|||
context files rather than to a number written here.
|
||||
"""
|
||||
bundle = okf.navigate_bundle(str(_TUNNEL))
|
||||
entries = _listing(_TUNNEL)
|
||||
entries = _documents(_TUNNEL)
|
||||
|
||||
assert len(entries) == len(bundle.context_files) > 0, (
|
||||
"a listing that omits documents is a base the navigator cannot fully see"
|
||||
|
|
@ -172,7 +188,7 @@ def test_the_verdict_layer_is_still_excluded(tmp_path: Path) -> None:
|
|||
"""
|
||||
base = _write_base(tmp_path, "med-dommer", body_chars=200, concepts=2, verdicts=2)
|
||||
|
||||
entries = _listing(base)
|
||||
entries = _documents(base)
|
||||
|
||||
assert len(entries) == 2, f"the verdict layer must not be listed as context: {entries!r}"
|
||||
assert not [e for e in entries if e["type"] == "verdict"]
|
||||
|
|
@ -184,7 +200,7 @@ def test_the_verdict_layer_is_still_excluded(tmp_path: Path) -> None:
|
|||
|
||||
def test_the_whole_document_is_still_one_call_away() -> None:
|
||||
"""(e) The bound is a disclosure LEVEL, not data loss."""
|
||||
entries = _listing(_TUNNEL)
|
||||
entries = _documents(_TUNNEL)
|
||||
biggest = max(entries, key=lambda e: int(e["chars"]))
|
||||
|
||||
whole = _tools(_TUNNEL)["read_file"].func(bundle_id=_TUNNEL.name, path=str(biggest["name"]))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue