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
|
|
@ -1015,6 +1015,84 @@ def assert_declared_ids_agree(bundle: Bundle) -> None:
|
|||
)
|
||||
|
||||
|
||||
class BundlePathNotFound(ValueError):
|
||||
"""A listing was asked for a directory the navigated bundle does not have.
|
||||
|
||||
A ``ValueError``, the ``BundleIdMismatch`` precedent: it must land on the CLI's refusal tuple
|
||||
and hosting's 400 arm rather than the crash channel. Refusing is the point — an unknown path
|
||||
rendered as an empty listing is indistinguishable from a directory that exists and holds
|
||||
nothing, and the caller is a model choosing a path out of a previous listing.
|
||||
"""
|
||||
|
||||
|
||||
def directory_listing(bundle: Bundle, path: str = "") -> dict[str, Any]:
|
||||
"""One LEVEL of a navigated bundle: the subdirectories under ``path`` with what each holds, and
|
||||
the concept documents that sit directly in it.
|
||||
|
||||
The rung between ``list_bundles`` (which bases exist) and ``read_file`` (what one document
|
||||
says). Before it existed, ``read_bundle`` answered with EVERY concept document in the base —
|
||||
measured on K2 at 42 761 o200k tokens riding in 7 of 12 prompts, 90 % of the run — because the
|
||||
price of finding out what a base contains was set by how much it contains.
|
||||
|
||||
**The tree is derived from ``context_files`` NAMES, never from ``index.md`` files.** Every name
|
||||
is already the full bundle-relative posix path, so the shape was never lost — only the
|
||||
rendering flattened it, which is why ``bundle_context`` and both nav-goldens are byte-identical
|
||||
after this was added. Deriving from names also means a directory whose index was never linked is
|
||||
still visible, and that the two renderings of one bundle cannot disagree about what a concept is.
|
||||
|
||||
``context_files``, NEVER ``files``: it is the property that drops the ``type: verdict`` layer
|
||||
AND nested ``index.md`` at every level. A listing built from ``files`` would put prior verdicts
|
||||
in front of the navigator around the gated ExpeL fold (målbilde §4) — and a directory COUNT
|
||||
built from ``files`` would advertise documents the navigator is not allowed to be shown.
|
||||
|
||||
**Every path is BUNDLE-RELATIVE, i.e. usable verbatim as the next call's argument.** A level-
|
||||
relative name would have to be composed by the caller, and the caller is a model: a path that
|
||||
never existed is worse than no path (``_index_excerpt``'s rule, one rung up).
|
||||
|
||||
``documents`` on a directory entry is the count of concept documents in its whole SUBTREE — what
|
||||
the subtree holds, not what one ``read_dir`` on it returns. It is the price signal a navigator
|
||||
chooses against, and the tool description says which of the two it is rather than leaving the
|
||||
reader to guess.
|
||||
|
||||
:raises BundlePathNotFound: no navigated concept document lives under ``path``.
|
||||
"""
|
||||
prefix = "" if path in ("", ".") else path.strip("/") + "/"
|
||||
directories: dict[str, int] = {}
|
||||
documents: list[dict[str, Any]] = []
|
||||
for f in bundle.context_files:
|
||||
if not f.name.startswith(prefix):
|
||||
continue
|
||||
rest = f.name[len(prefix) :]
|
||||
head, sep, _ = rest.partition("/")
|
||||
if sep:
|
||||
directories[prefix + head] = directories.get(prefix + head, 0) + 1
|
||||
else:
|
||||
documents.append(
|
||||
{
|
||||
"name": f.name,
|
||||
# ``or "document"`` mirrors ``bundle_context``'s own fallback for a file with no
|
||||
# declared type, so the two renderings cannot disagree about it.
|
||||
"type": f.type or "document",
|
||||
"title": unquote_scalar(f.frontmatter.get("title", f.name)),
|
||||
# What the next rung COSTS, in the unit the ceiling is measured in. A navigator
|
||||
# that cannot see the price cannot choose against a budget.
|
||||
"chars": len(f.body),
|
||||
}
|
||||
)
|
||||
if prefix and not directories and not documents:
|
||||
raise BundlePathNotFound(
|
||||
f"knowledge base {bundle.dir!r} has no directory {path!r}; it holds no concept "
|
||||
"document under that path"
|
||||
)
|
||||
return {
|
||||
"path": path,
|
||||
"directories": [
|
||||
{"path": name, "documents": count} for name, count in sorted(directories.items())
|
||||
],
|
||||
"documents": documents,
|
||||
}
|
||||
|
||||
|
||||
def bundle_context(bundle: Bundle, *, dimension: str | None = None) -> str:
|
||||
"""Render a navigated bundle as agent read-context via progressive disclosure: the ``index.md``
|
||||
summary, then each concept file as ``## {type}: {title}\\n{body}``. ``type: verdict`` files are
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue