feat(verdict-gate): read_file nekter verdict-laget, uansett hvordan stien ble funnet

S2c lot dette staa som en uttalt aerlighetsgrense: ingen listing navngir
type:verdict-laget - context_files dropper det paa hvert nivaa, saa verken
read_bundle, read_dir eller bundle_context nevner en dom - men en GJETTET sti
naadde en, og aa naa den slik gaar utenom den gatede ExpeL-folden som er eneste
sanksjonerte vei fra en tidligere dom inn i en hypotese. Egenskapen var arvet
fra S7a-3 og ble naabar fra debatten ogsaa da S2c ga den navigatoerens fire
verktoey. Operatoerbeslutning 04.09: regelen skal bo ETT sted, i verktoeyet.

Gaten er i read_file, altsaa i én kopi for begge kallere. Ikke i renderingen,
som S2c selv maalte til "et filter i navnet alene" mens denne sprossen fortsatt
serverer bytene; ikke i prompt-tekst, som er raad til en utrodd velger og ikke
en gate.

Predikatet leser DOKUMENTET, ikke gangen. Bundle.verdicts svarer paa "hvilke
dommer naadde navigasjonen", som er riktig spoersmaal for ExpeL-froeene; gatens
spoersmaal er det andre - "faar disse bytene forlate" - og det stilles om en sti
en modell valgte, som ingen listing ga den. En dom ingen index lenker ville seilt
rett gjennom en navigasjons-noeklet sjekk. _VERDICT_TYPE er ÉN kopi av hva et
verdict ER (koe-(p)); tre flater svarer nå paa det.

Gaten staar FOER dimensjonssjekken: laget nektes ubetinget, og dimension=None er
utforskningens egen kall - en gate etter den grenen ville vaert fravaerende fra
nøyaktig den kalleren den ble skrevet for.

VERIFISERT: 1314 passed / 5 skipped (fra 1306/5, +8 = de aatte nye armene, ingen
eksisterende testfil roert). Golden demo-transcript BYTEUENDRET, shasum -a 1 av
INNHOLDET = ea8c534773acdbe41ae68f2c55724d69aaf8be4f. ruff + mypy rene.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-04 20:16:18 +02:00
commit 0a83bb8bc9
2 changed files with 58 additions and 3 deletions

View file

@ -215,7 +215,20 @@ class ExplorationError(RuntimeError):
class VerdictLayerRefused(ValueError):
"""PLACEHOLDER for the red observation — declared, not yet enforced anywhere."""
"""A navigator asked ``read_file`` for a document belonging to the ``type: verdict`` layer.
Prior expert judgements reach a hypothesis through ONE door the gated ExpeL fold inside
``run_project``, keyed on the candidate (S3.2) and folded before generation. Everything else in
the bundle is context an agent may navigate; a verdict is not, because a run that reads its own
corpus of past judgements as ordinary knowledge has routed around the gate that decides which
of them are relevant, and self-contamination is exactly what målbilde §4 excludes the layer to
prevent.
A ``ValueError``, the ``DimensionScopeRefused``/``BundlePathNotFound`` precedent: the caller is
a model choosing a path, so the refusal lands on the CLI's refusal tuple and hosting's 400 arm
rather than the crash channel. Like the dimension scope it is a refused read inside a run that
is otherwise fine, never an exploration that cannot be honoured as configured.
"""
class DimensionScopeRefused(ValueError):
@ -1017,6 +1030,25 @@ def navigator_tools(
# model-chosen path is untrusted input by definition, so it goes through the same gate the
# navigation walk uses rather than a second, laxer check.
resolved = Path(safe_resolve(bundle_dir, path))
# The verdict layer, refused HOWEVER the path was found (order 20260904T172353Z). No
# listing names it — ``context_files`` drops it at every level, so ``read_bundle`` and
# ``read_dir`` never mention one — but a GUESSED path reached it, and reaching it that way
# walks around the gated ExpeL fold that is the only sanctioned route from a past judgement
# into a hypothesis. Measured on the fixture base before the gate: all 2 883 characters.
#
# The rule lives HERE, in the tool, and therefore in exactly one place for both callers —
# the exploration (``--explore``) and, since S2c, the debate. Not in the rendering, which
# S2c measured to be "a filter in name only" while this rung still serves the bytes; not in
# prompt text, which is advice to an untrusted chooser rather than a gate. It is BEFORE the
# dimension check because the layer is refused unconditionally: ``dimension=None`` is the
# exploration's own call and admits every dimension, so a gate placed after that branch
# would be absent from precisely the caller it was written for.
if okf.declares_verdict_type(resolved):
raise VerdictLayerRefused(
f"document {path!r} in knowledge base {bundle_id!r} is a prior expert verdict; "
"judgements reach a hypothesis only through the gated ExpeL fold, never by being "
"read as bundle knowledge"
)
if dimension is not None:
# The SECOND half of the scope (§4.1a). A listing that hides a document while this rung
# still serves it by path is a filter in name only, and the caller here is a model that