feat(p21): a declaration that must have LOOKED, and a refusal that names the neighbours
C1. Round 4 produced 13 declarations over six runs and NOT ONE named a fasit concept. The distinct documents opened before each were 1,1,1,1,1,1,1,2,5,5,6,13,13: seven declared the base's FIRST requirement after opening exactly ONE document. The order offered two rules and asked which discriminates. Replayed against the real listings: "the declared document must have come back from a read_dir filtered on a word from the approach's label" refuses 13 of 13 -- including Soraasen's 12.11, the closest any run came -- because ZERO of the 13 were reached through a filtered listing at all. A gate that refuses every measured case, right and wrong alike, cannot discriminate. "fewer than k distinct documents opened" at k=3 refuses 8 of 13 and keeps the five that navigated. k=3, 4 and 5 refuse the SAME eight -- the distribution has a gap between 2 and 5 -- so the threshold is not on a cliff, and 3 is the lowest of that plateau. DISTINCT paths, not calls, and capped by the base's own size so a small base stays declarable. C2. Over the same traces 18 of 143 path-bearing calls named a path the base does not hold, ELEVEN of them one run walking R761/4-3, 4.3, 4-2, 4-1, 4-0, 4-5, 4-6 while the real names are R761/4, R761/41, R761/42. The refusal already named the nearest listable ancestor; now it also names up to five of that rung's own subdirectories, ranked by longest common prefix with the segment that failed. ONE copy shared by both refusal sites, built from context_files through in_dimension, so every name handed back resolves and the verdict layer can never be advertised in an apology. MEASURED after: 16 of 18. Load-bearing MEASURED, four mutations all red against the WHOLE suite, green control 1863/5, golden byte-unchanged: C3(i) the declaration gate detached (2 red) . C3(ii) the neighbour list empty (6) . C3(iii) built from files (1, the verdict arm alone) . C3(iv) count CALLS instead of distinct documents (1, the repetition arm alone). Three existing arms REWRITTEN, not weakened: all three read one document and declared, which is the measured failure class exactly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
7b4f85d77c
commit
ac0bfdba27
8 changed files with 534 additions and 28 deletions
|
|
@ -1025,6 +1025,13 @@ def _declared_document(index: Mapping[str, str], bundle_id: str, path: str) -> t
|
|||
return "", ""
|
||||
|
||||
|
||||
#: P21/C1 — how many DISTINCT documents a run must have opened before a declaration of the binding
|
||||
#: requirement is worth recording. MEASURED, never chosen: see ``_declare_requirement`` for the
|
||||
#: full distribution and for why the alternative rule the order offered was rejected. Capped by the
|
||||
#: base's own document count at the call site, so a small base stays declarable.
|
||||
_MIN_DOCUMENTS_READ: Final = 3
|
||||
|
||||
|
||||
#: Characters of the root index body one catalogue entry may carry. The catalogue's job is to let a
|
||||
#: manager pick a base, not to read one, so the excerpt is a fixed-size window rather than a share
|
||||
#: of the base: cost then scales with how many bases are configured, which the operator chose, and
|
||||
|
|
@ -1079,26 +1086,19 @@ def _refusal_kind(exc: Exception) -> str:
|
|||
return type(exc).__name__
|
||||
|
||||
|
||||
def _nearest_listable(bundle_dir: str, path: str, dimension: str | None) -> str:
|
||||
"""The deepest ANCESTOR of ``path`` that ``read_dir`` will actually answer for.
|
||||
def _neighbours(bundle_dir: str, path: str, dimension: str | None) -> tuple[str, tuple[str, ...]]:
|
||||
"""``(nearest listable ancestor, up to five of its subdirectories)`` for a path that is absent.
|
||||
|
||||
Chosen off the NAVIGATED ``context_files`` rather than off the filesystem, for two reasons that
|
||||
are the same reason: a directory can exist on disk and hold no navigated concept (nothing links
|
||||
it), in which case ``read_dir`` refuses it and the refusal would have handed the caller a path
|
||||
that does not resolve — ``_index_excerpt``'s rule one rung up, a path that never was is worse
|
||||
than no path. And ``context_files`` is the property that drops the ``type: verdict`` layer, so a
|
||||
refusal can never advertise by name the one layer no listing mentions.
|
||||
|
||||
Falls back to ``""``, the base's own top level, which ``directory_listing`` always answers.
|
||||
ONE navigation for both halves, and BOTH read off ``okf`` rather than reimplemented here: this
|
||||
module and ``directory_listing``'s own refusal answer the same question about the same base, and
|
||||
two copies of "which directory did they mean" would be free to give a caller two answers about
|
||||
one level (kø-(p)).
|
||||
"""
|
||||
bundle = okf.navigate_bundle(bundle_dir)
|
||||
reachable = [f for f in bundle.context_files if okf.in_dimension(f, dimension)]
|
||||
segments = path.strip("/").split("/")
|
||||
for depth in range(len(segments) - 1, 0, -1):
|
||||
candidate = "/".join(segments[:depth])
|
||||
if any(f.name.startswith(candidate + "/") for f in reachable):
|
||||
return candidate
|
||||
return ""
|
||||
return (
|
||||
okf.nearest_listable_directory(bundle, path, dimension=dimension),
|
||||
okf.nearest_subdirectories(bundle, path, dimension=dimension),
|
||||
)
|
||||
|
||||
|
||||
def _refused_mapping(exc: Exception) -> dict[str, Any]:
|
||||
|
|
@ -1332,9 +1332,17 @@ 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)
|
||||
# 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 ""
|
||||
raise okf.BundlePathNotFound(
|
||||
f"knowledge base {bundle_id!r} has no document {path!r}; nearest directory that "
|
||||
f"holds documents: {_nearest_listable(bundle_dir, path, dimension)!r} — list it "
|
||||
f"holds documents: {ancestor!r}{nearby} — list it "
|
||||
"with read_dir (it takes a filter) and read_file one of the names it gives"
|
||||
)
|
||||
# The verdict layer, refused HOWEVER the path was found (order 20260904T172353Z). No
|
||||
|
|
@ -1405,7 +1413,7 @@ def navigator_tools(
|
|||
assert opened is not None and requirements is not None # the constructor guard above
|
||||
# The base is resolved by the SAME index every read rung uses, so an unknown base is
|
||||
# refused here exactly as it is there rather than being accepted into the record.
|
||||
_resolve_bundle(index, bundle_id)
|
||||
resolved_dir = _resolve_bundle(index, bundle_id)
|
||||
read_paths = [call.path for call in opened if call.name == "read_file" and call.path]
|
||||
if path not in read_paths:
|
||||
raise RequirementNotRead(
|
||||
|
|
@ -1413,6 +1421,41 @@ def navigator_tools(
|
|||
"this is not one of them. Read it first — a requirement nobody read cannot bind a "
|
||||
"direction"
|
||||
)
|
||||
# P21/C1: the declaration must have LOOKED. MEASURED over round 4's six debate traces --
|
||||
# 13 declarations, and the distinct documents opened before each were
|
||||
# 1,1,1,1,1,1,1,2,5,5,6,13,13. Seven of them opened exactly ONE document, the base's front
|
||||
# matter, and declared its FIRST requirement (``Krav 1.2-1``, ``Krav 1.1-1``,
|
||||
# ``Krav 1.1.1-1``); none of the 13 named a fasit concept. Three of five paid runs read a
|
||||
# single document all run.
|
||||
#
|
||||
# THE ORDER OFFERED A SECOND RULE AND THE MEASUREMENT CHOSE BETWEEN THEM. The alternative
|
||||
# -- "the declared document must have been returned by a ``read_dir`` filtered on a word
|
||||
# from the approach's label" -- was replayed against the real listings and refuses 13 of
|
||||
# 13, including Soraasen's ``12.11``, which the order names as the closest any run came.
|
||||
# A gate that refuses every measured case, right and wrong alike, cannot discriminate: it
|
||||
# is the vacuous gate's mirror image. This one refuses 8 of 13 and keeps the five that
|
||||
# navigated, ``12.11`` among them.
|
||||
#
|
||||
# THE THRESHOLD IS NOT ON A CLIFF: k = 3, 4 and 5 all refuse the same eight, because the
|
||||
# measured distribution has a gap between 2 and 5. Three is the lowest of that plateau,
|
||||
# which is the least this can refuse while still separating the two measured classes.
|
||||
#
|
||||
# CAPPED BY THE BASE ITSELF. A base with two concept documents can be read whole in two,
|
||||
# and a floor above its size would make declaration impossible there -- a gate that can
|
||||
# only refuse. The cap is read off ``context_files``, the same property every listing rung
|
||||
# is built from, so the verdict layer is outside the denominator exactly as it is outside
|
||||
# the listings.
|
||||
distinct = len(set(read_paths))
|
||||
in_base = len(okf.navigate_bundle(resolved_dir).context_files)
|
||||
floor = min(_MIN_DOCUMENTS_READ, in_base)
|
||||
if distinct < floor:
|
||||
raise RequirementNotRead(
|
||||
f"{path!r}; this run has opened {distinct} distinct document(s) of the "
|
||||
f"{in_base} in {bundle_id!r}, and a binding requirement declared after {distinct} "
|
||||
f"is a guess rather than a finding — read at least {floor} before declaring one. "
|
||||
"Use read_dir with a 'filter' word from the approach's own label to find the "
|
||||
"candidates, then read_file the ones that could bind it"
|
||||
)
|
||||
requirements.append(DeclaredRequirement(bundle_id=bundle_id, path=path, ref=ref))
|
||||
# P20/A1: give back the DOCUMENT's own title and number, read off the base rather than
|
||||
# echoed from the arguments. MEASURED (P19 round 3, P17b): 13 declarations over 5 runs and
|
||||
|
|
|
|||
|
|
@ -1289,6 +1289,89 @@ def _matches_filter(file: BundleFile, needle: str) -> bool:
|
|||
return any(needle in value.casefold() for value in hay)
|
||||
|
||||
|
||||
#: P21/C2 — how many neighbouring directories a "no such path" refusal may name. A refusal's job is
|
||||
#: to hand back the one thing the caller can act on, not to re-list the level: five is enough to
|
||||
#: show the SHAPE of the names this base uses (``4``/``41``/``42`` rather than ``4-3``), and the
|
||||
#: full level is one ``read_dir`` away.
|
||||
_NEIGHBOUR_LIMIT: Final = 5
|
||||
|
||||
|
||||
def nearest_listable_directory(bundle: Bundle, path: str, *, dimension: str | None = None) -> str:
|
||||
"""The deepest ANCESTOR of ``path`` that ``directory_listing`` will actually answer for.
|
||||
|
||||
Chosen off the NAVIGATED ``context_files`` rather than off the filesystem, for two reasons that
|
||||
are the same reason: a directory can exist on disk and hold no navigated concept (nothing links
|
||||
it), in which case ``read_dir`` refuses it and the refusal would have handed the caller a path
|
||||
that does not resolve — ``_index_excerpt``'s rule one rung up, a path that never was is worse
|
||||
than no path. And ``context_files`` is the property that drops the ``type: verdict`` layer, so a
|
||||
refusal can never advertise by name the one layer no listing mentions.
|
||||
|
||||
Falls back to ``""``, the base's own top level, which ``directory_listing`` always answers.
|
||||
"""
|
||||
reachable = [f for f in bundle.context_files if in_dimension(f, dimension)]
|
||||
segments = path.strip("/").split("/")
|
||||
for depth in range(len(segments) - 1, 0, -1):
|
||||
candidate = "/".join(segments[:depth])
|
||||
if any(f.name.startswith(candidate + "/") for f in reachable):
|
||||
return candidate
|
||||
return ""
|
||||
|
||||
|
||||
def nearest_subdirectories(
|
||||
bundle: Bundle,
|
||||
path: str,
|
||||
*,
|
||||
dimension: str | None = None,
|
||||
limit: int = _NEIGHBOUR_LIMIT,
|
||||
) -> tuple[str, ...]:
|
||||
"""The directories a caller who named a path this base does not hold could have meant (P21/C2).
|
||||
|
||||
**The measured defect.** Over round 4's six traces, 16 of 105 ``read_dir`` calls and 2 of 38
|
||||
``read_file`` calls named a path the base does not hold, and eleven of those were one run
|
||||
walking ``R761/4-3``, ``R761/4.3``, ``R761/4-2``, ``R761/4-1``, ``R761/4-0``, ``R761/4-5``,
|
||||
``R761/4-6`` — guessing at a chapter-number spelling the corpus does not use, while the real
|
||||
neighbours are ``R761/4``, ``R761/41``, ``R761/42``. The refusal already named the nearest
|
||||
LISTABLE ancestor, which is the right rung; what it could not say is which of that rung's names
|
||||
the caller was reaching for.
|
||||
|
||||
**Every name it returns RESOLVES.** Built from ``context_files`` and through the SAME
|
||||
``in_dimension`` predicate the listing uses: a suggestion read off the filesystem could name a
|
||||
directory ``read_dir`` then refuses, and one built from ``files`` could name the ``type:
|
||||
verdict`` layer by path — advertising in a refusal the one layer no listing mentions.
|
||||
|
||||
**Ranked by longest common prefix with the segment that failed, then shortest, then name.** A
|
||||
ranking cannot refuse anything — this is help text on a refusal — so its failure direction is
|
||||
benign: at worst it names five real directories that are not the one meant. Prefix ranking is
|
||||
what puts ``4`` ahead of ``41`` for ``4-3``; with no common prefix at all every candidate ties
|
||||
and the order degrades to "the shortest names at this level", which is an honest "here is what
|
||||
IS here".
|
||||
"""
|
||||
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 ""
|
||||
|
||||
children: set[str] = set()
|
||||
for file in bundle.context_files:
|
||||
if not in_dimension(file, dimension) or not file.name.startswith(prefix):
|
||||
continue
|
||||
rest = file.name[len(prefix) :].split("/")
|
||||
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))
|
||||
return tuple(f"{prefix}{name}" for name in ranked[:limit])
|
||||
|
||||
|
||||
def directory_listing(
|
||||
bundle: Bundle,
|
||||
path: str = "",
|
||||
|
|
@ -1404,9 +1487,16 @@ def directory_listing(
|
|||
f"{path!r} in knowledge base {bundle.dir!r} is a document, not a directory; "
|
||||
f"use read_file to read {named.name!r} whole"
|
||||
)
|
||||
# P21/C2: name the rung the caller could have meant. The SAME two helpers the read_file
|
||||
# refusal uses (kø-(p)) — one question about one base must not have two answers — and both
|
||||
# are built from ``context_files`` through ``in_dimension``, so every name handed back is
|
||||
# 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 ""
|
||||
raise BundlePathNotFound(
|
||||
f"knowledge base {bundle.dir!r} has no directory {path!r}; it holds no concept "
|
||||
"document under that path"
|
||||
f"document under that path. Nearest directory that does: {ancestor!r}{nearby}"
|
||||
)
|
||||
# A1: the WINDOW. Clamped, never refused — a caller asking for more than the maximum asked for
|
||||
# a listing, and the bound is this rung's job to keep, not the caller's to remember. A negative
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue