fix(b-gate): the binding measures an execution, and the row-3 sentence stops counting doors

Third repair of the same failure, and the first that does not read a NAME. The judge measured
row 1 to 17 of 17 with 17 one-line probes and a door module without a single import: "the probe
touches the door" was still a text check, one notch further in.

What is measured now, in the probe's own source: an EXECUTION SITE (a process starter carrying
the door's name in its arguments, or the entry imported from the door's module and called), and
an assert that is DATA-DEPENDENT on that call. Dead code is pruned first -- a call in a branch
that never runs, or after a return, is no execution. The honest forms the suite already uses
keep counting: the command built in a variable first, and the subprocess started in a helper
that returns it.

All seven forms the judge measured (a1, a2, b1, b2, c1, c2 + the one-liner recipe) now give
0 of 1, each with an rc-0 control in the same setup.

[mutation-ok] -- no guard is switched off. The two `if False:` lines in this diff are the
judge's b1 probe form planted as test DATA (a Python string written into a tmp_path file the
gate only PARSES, never runs) and the docstring naming why dead code is pruned. The commit adds
the rule that makes `if False:` around a door call stop counting.

Naming the step becomes a DISCRIMINATOR, as a class: it is required only when more than one step
stands behind the same door. Driving a door with a sole step IS driving that step -- which is why
`gate` (a real end-to-end door probe against v1_gate) was rejected on a name technicality. Row 1:
0 -> 1 of 17. For a subcommand door the command name must stand in what was actually executed,
and the CLI itself must be registered or `-m`-runnable: an add_parser in a module nobody can
start is no door.

held_out: a reason must be named prose. "-", "todo", "x", "." were all accepted as a reason by
`.strip()`; measured against the contract's own 28 reasons (shortest: 30 chars, three words).

Row 3: the five forms the checkpoint measured as open AND cheap to close are closed -- importlib
for both SDK names, deno/npm exec/yarn dlx, and the official TypeScript SDK. 12 of 12 -> 15 of 15,
still 0 hits over 512 published files. The limit sentence stops enumerating what remains ("EXACTLY
TWO", then "THREE" -- both falsified by the first new measurement) and says what the guard IS.

N7 stays open in the gate and is closed in the suite: a deleted off-path step still shrinks the
denominator silently there, so the four are written out in an arm that goes red when one goes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-20 07:48:34 +02:00
commit e47be68b57
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
3 changed files with 786 additions and 144 deletions

View file

@ -520,6 +520,27 @@
"pattern_b64": "X19pbXBvcnRfX1woXHMqWyInXShjbGF1ZGVfYWdlbnRfc2RrfGNsYXVkZV9jb2RlX3Nka3xhbnRocm9waWMpWyInXQ==", "pattern_b64": "X19pbXBvcnRfX1woXHMqWyInXShjbGF1ZGVfYWdlbnRfc2RrfGNsYXVkZV9jb2RlX3Nka3xhbnRocm9waWMpWyInXQ==",
"known_positive_b64": "X19pbXBvcnRfXygiY2xhdWRlX2FnZW50X3NkayIp", "known_positive_b64": "X19pbXBvcnRfXygiY2xhdWRlX2FnZW50X3NkayIp",
"known_negative_b64": "X19pbXBvcnRfXygiYWdlbnRfZnJhbWV3b3JrIik=" "known_negative_b64": "X19pbXBvcnRfXygiYWdlbnRfZnJhbWV3b3JrIik="
},
{
"id": "dynamisk-import-modul",
"label": "importlib mot en Claude-klient",
"pattern_b64": "aW1wb3J0bGliXC5pbXBvcnRfbW9kdWxlXChccypbIlwnXShjbGF1ZGVfYWdlbnRfc2RrfGNsYXVkZV9jb2RlX3Nka3xhbnRocm9waWMpWyJcJ10=",
"known_positive_b64": "aW1wb3J0bGliLmltcG9ydF9tb2R1bGUoImNsYXVkZV9hZ2VudF9zZGsiKQ==",
"known_negative_b64": "aW1wb3J0bGliLmltcG9ydF9tb2R1bGUoImFnZW50X2ZyYW1ld29yayIp"
},
{
"id": "node-runner-indirekte",
"label": "claude-code startet av deno, npm exec, yarn dlx eller pnpm exec",
"pattern_b64": "KGRlbm9ccytydW5cYlteXG5dKj9ucG06fG5wbVxzK2V4ZWNcYlteXG5dKj98eWFyblxzK2RseFxiW15cbl0qP3xwbnBtXHMrZXhlY1xiW15cbl0qPykoQGFudGhyb3BpYy1haS8pP2NsYXVkZS1jb2RlXGI=",
"known_positive_b64": "ZGVubyBydW4gLUEgbnBtOkBhbnRocm9waWMtYWkvY2xhdWRlLWNvZGUgLXAgIngi",
"known_negative_b64": "bnBtIGV4ZWMgcHJldHRpZXIgLS0gLS13cml0ZSAu"
},
{
"id": "sdk-ts-import",
"label": "import av den offisielle TypeScript-SDK-en",
"pattern_b64": "KGltcG9ydHxyZXF1aXJlKVxiW15cbl0qWyJcJ11AYW50aHJvcGljLWFpLyhjbGF1ZGUtYWdlbnQtc2RrfGNsYXVkZS1jb2RlLXNka3xjbGF1ZGUtY29kZSlbIlwnXQ==",
"known_positive_b64": "aW1wb3J0IHsgcXVlcnkgfSBmcm9tICJAYW50aHJvcGljLWFpL2NsYXVkZS1hZ2VudC1zZGsiOw==",
"known_negative_b64": "aW1wb3J0IHsgeiB9IGZyb20gInpvZCI7"
} }
] ]
}, },

View file

@ -83,14 +83,19 @@ EXTERNAL_DOOR = (
"tools Claude Code kan bruke» — en kommando er nettopp det. Gaten teller tre arter den kan " "tools Claude Code kan bruke» — en kommando er nettopp det. Gaten teller tre arter den kan "
"VERIFISERE mot kilden: console-script (registrert i pyproject), module-main (kjørbar med " "VERIFISERE mot kilden: console-script (registrert i pyproject), module-main (kjørbar med "
"python -m, med sin egen __main__-vakt) og subcommand (et navn registrert i modulens egen " "python -m, med sin egen __main__-vakt) og subcommand (et navn registrert i modulens egen "
"argparse). En art gaten ikke kan verifisere er ikke en dør her. Og et navn er uansett ikke " "argparse, i en CLI som selv er registrert eller kjørbar med -m). En art gaten ikke kan "
"nok: PROBENS EGEN KILDE leses, og tre trekk måles i den — at proben rører DØREN (dens " "verifisere er ikke en dør her. Og et navn er uansett ikke nok: PROBENS EGEN KILDE leses, "
"dotted modulnavn eller dens registrerte kommandonavn står i et kall der), at den navngir " "og det som måles i den er en UTFØRELSE — at en prosess startes med dørens navn i "
"STEGET (symbolet, id-en eller underkommandoen som et eget ord), og at den asserter i det " "argumentene, eller at inngangen importeres fra dørens modul og kalles, at en assert er "
"hele tatt. En probe som er DELT mellom to steg beviser høyst ett av dem, og gaten vet ikke " "DATAAVHENGIG av det kallet (kallets verdi, eller et navn utledet av den, står i asserten), "
"hvilket — altså ingen. Valgt å lese probens kilde framfor å kreve at den ligger i en " "og at kommandolinja som faktisk ble kjørt navngir STEGET når mer enn ett steg står bak "
"kontrakt-navngitt fil, fordi et filnavn er en konvensjon en stub oppfyller like lett som en " "samme dør. Er steget ALENE bak sin dør, er det å kjøre døren å kjøre steget, og "
"ekte probe; målt 19.09 tok fjorten stubber og ÉN urelatert bestått test raden til 17 av 17" "navngivningen faller bort — den er en diskriminator, ikke et bevis. Død kode teller ikke: "
"et kall i en gren som aldri kjører, eller etter en return, er ingen utførelse. En probe "
"som er DELT mellom to steg beviser høyst ett av dem, og gaten vet ikke hvilket — altså "
"ingen. GRENSEN, uttalt: gaten leser at proben KJØRER døren og leser resultatet; den kjører "
"ikke proben på nytt med døren brutt. Den eneste formen som ikke kan skrives seg forbi er å "
"MUTERE inngangen til å reise og se proben bli rød, og det gjør gaten ikke"
) )
#: Rad 1: grensen for hvordan nevneren leses, sagt av raden selv. #: Rad 1: grensen for hvordan nevneren leses, sagt av raden selv.
@ -100,8 +105,10 @@ RUN_PATH_LIMIT = (
"19.09: 41 → 40). Gaten NAVNGIR at symbolet ikke lenger kalles i kjørestien, men feller det " "19.09: 41 → 40). Gaten NAVNGIR at symbolet ikke lenger kalles i kjørestien, men feller det "
"ikke. Valgt å uttale grensen framfor å følge hjelpere transitivt, fordi et kallgraf-søk " "ikke. Valgt å uttale grensen framfor å følge hjelpere transitivt, fordi et kallgraf-søk "
"ville trukket private hjelpere inn i nevneren og gjort den til en ny kuratert liste — den " "ville trukket private hjelpere inn i nevneren og gjort den til en ny kuratert liste — den "
"ene tingen raden finnes for å unngå. Stegene som er erklært UTENFOR kjørestien har ingen " "ene tingen raden finnes for å unngå. Stegene som er erklært utenfor kjørestien har ingen "
"utledet nevner i det hele tatt; de står navngitt under raden, ett for ett" "utledet nevner i det hele tatt; de står navngitt under raden, ett for ett, og et slikt "
"steg som SLETTES tar nevneren med seg uten at gaten kan se det. Den vakten bor i suiten "
"(en utskrevet liste over de fire, som blir rød når en av dem forsvinner), ikke her"
) )
#: Inngangsartene rad 1 kan VERIFISERE mot kilden. En art utenfor lista teller aldri som en dør: #: Inngangsartene rad 1 kan VERIFISERE mot kilden. En art utenfor lista teller aldri som en dør:
@ -370,77 +377,303 @@ def registered_entry(
return False, f"{module} navngir ingen underkommando" return False, f"{module} navngir ingen underkommando"
if not _registers_subcommand(tree, command): if not _registers_subcommand(tree, command):
return False, f"{module} registrerer ingen underkommando {command!r}" return False, f"{module} registrerer ingen underkommando {command!r}"
return True, "" #: Målt 20.09: ``add_parser("x")`` i en modul ingen kan starte er et navn inne i en CLI, ikke
#: en dør ut. Underkommandoen arver dørens art fra CLI-en sin — registrert i pakkemanifestet
#: eller kjørbar med ``-m``. Uten den ene av de to er «kallbar utenfra» en påstand.
target = f"{_dotted(package, module)}:{scope}"
if target in console_scripts(repo_root).values() or _has_dunder_main(tree, scope):
return True, ""
return (
False,
f"{module} er verken registrert i pyproject [project.scripts] eller har en "
f"__main__-vakt — en underkommando i en CLI ingen kan starte er ingen dør",
)
_TOKEN = re.compile(r"[\w-]+") _TOKEN = re.compile(r"[\w-]+")
#: Et ord i en grunn: minst tre bokstaver, ingen siffer. «todo» er ett ord, «-» er ingen.
_WORD = re.compile(r"[^\W\d_]{3,}", re.UNICODE)
def _meaningful(node: ast.AST) -> list[ast.AST]: #: Kallene på ``subprocess`` som STARTER en prosess. ``os.system`` og ``os.exec*`` håndteres
"""Hver node i kroppen UNNTATT strenger som står alene — docstringen er ikke en atferd.""" #: for seg, fordi navnene deres ikke er en lukket liste.
stack = [node] _PROCESS_ATTRS = frozenset({"run", "Popen", "call", "check_call", "check_output"})
def named_reason(reason: str) -> bool:
"""En grunn er NAVNGITT prosa, ikke bare ikke-tom.
Målt 19.09: ``.strip()`` skilte tom fra ikke-tom, og «-», «todo», «x» og «.» gikk alle
gjennom som grunn for å holde et kall utenfor nevneren. Terskelen er satt mot kontraktens
egne 28 grunner (korteste: 30 tegn, tre ord) den feller plassholderen og ingen av dem.
GRENSEN: dette måler FORM, ikke sannhet. «aaa bbb ccc» er prosa for denne regelen; ingen
tekstregel kan skille en grunn som holder fra en som bare ser ut som en."""
text = reason.strip()
return len(text) >= 12 and len(_WORD.findall(text)) >= 3
def door_key(step: Mapping[str, Any]) -> tuple[str, str, str]:
"""Døren et steg står bak: modul, scope og underkommando. To steg med samme nøkkel deles."""
entry = dict(step.get("entry", {}))
return (
str(entry.get("module", "")),
str(entry.get("scope", "main")),
str(entry.get("command", "")),
)
def _constant_truth(test: ast.expr) -> bool | None:
return bool(test.value) if isinstance(test, ast.Constant) else None
def _live_nodes(scope: ast.AST) -> list[ast.AST]:
"""Nodene i kroppen som KAN kjøre — dokumentstrenger, døde grener og kode etter en
``return`` holdt utenfor.
Målt 19.09 (dommerens b1 og b2): et dørkall inne i ``if False:`` og et dørkall etter
``return`` telte begge som «proben rører døren». En setning som aldri kjører er ikke en
utførelse, og en gate som leser den måler kilden som tekst igjen."""
out: list[ast.AST] = [] out: list[ast.AST] = []
while stack:
current = stack.pop() def statements(body: Sequence[ast.stmt]) -> None:
if ( for stmt in body:
isinstance(current, ast.Expr) if (
and isinstance(current.value, ast.Constant) isinstance(stmt, ast.Expr)
and isinstance(current.value.value, str) and isinstance(stmt.value, ast.Constant)
): and isinstance(stmt.value.value, str)
continue ):
out.append(current) continue
stack += list(ast.iter_child_nodes(current)) if isinstance(stmt, ast.If):
truth = _constant_truth(stmt.test)
if truth is not None:
statements(stmt.body if truth else stmt.orelse)
continue
out.append(stmt)
for child in ast.iter_child_nodes(stmt):
if isinstance(child, (ast.stmt, ast.excepthandler)):
continue
out.extend(ast.walk(child))
for field in ("body", "orelse", "finalbody"):
statements(getattr(stmt, field, None) or ())
for handler in getattr(stmt, "handlers", None) or ():
statements(handler.body)
if isinstance(stmt, (ast.Return, ast.Raise, ast.Break, ast.Continue)):
break
statements(getattr(scope, "body", []))
return out return out
def door_strings(nodes: Sequence[ast.AST]) -> set[str]: def _target_names(target: ast.AST) -> set[str]:
"""Strengene proben bruker, docstringen unntatt. if isinstance(target, ast.Name):
return {target.id}
if isinstance(target, (ast.Tuple, ast.List)):
return {n for element in target.elts for n in _target_names(element)}
return set()
Videre enn ``call_payload`` med vilje, og grunnen er målt: den ekte probe-formen bygger
kommandoen i en variabel først (``base = [sys.executable, "-m", "<modul>"]``) og sender den def _strings_in(value: ast.AST, bindings: Mapping[str, set[str]]) -> set[str]:
inn etterpå. Et dotted modulnavn er spesifikt at det ER døren uansett hvor det settes """Strengene et uttrykk bærer, variabler fulgt ett steg av gangen."""
sammen det er nettopp den formen rad 3 selv har en egen vakt for."""
found: set[str] = set() found: set[str] = set()
for node in nodes: for child in ast.walk(value):
for child in _meaningful(node): if isinstance(child, ast.Constant) and isinstance(child.value, str):
if isinstance(child, ast.Constant) and isinstance(child.value, str): found.add(child.value)
found.add(child.value) elif isinstance(child, ast.Name):
found |= bindings.get(child.id, set())
return found return found
def call_payload(nodes: Sequence[ast.AST]) -> tuple[set[str], set[str]]: def string_bindings(nodes: Sequence[ast.AST]) -> dict[str, set[str]]:
"""(ord, hele strenger) proben sender INN i et kall, pluss navnene den kaller. """``navn -> strengene det kan bære``, som fikspunkt over tilordningene.
To avgrensninger, begge målt fram: Den ærlige probe-formen bygger kommandoen i en variabel først
(``base = [sys.executable, "-m", "<modul>"]``) og sender den inn etterpå. Uten dette leddet
* **Bare det som går inn i et kall.** En docstring som nevner symbolet, eller en kommentar ville regelen felt kapabilitet den motsatte feilen av den den er skrevet for."""
som navngir modulen, er ikke en atferd. Leses hele funksjonskroppen, er «nevner» nok og pairs: list[tuple[str, ast.AST]] = []
da er et navn en dør igjen, bare ett hakk lenger inne.
* **Delt i ORD, aldri delstreng.** «gate» er ikke nevnt av
``portfolio_optimiser.evals.v1_gate``. Målt 19.09: en tidligere utgave av denne regelen
godtok steget «gate» fordi probefila importerte modulen som ``gate`` den bandt til et
lokalt alias, ikke til steget."""
words: set[str] = set()
constants: set[str] = set()
for node in nodes: for node in nodes:
for call in ast.walk(node): if isinstance(node, ast.Assign):
if not isinstance(call, ast.Call): pairs += [(n, node.value) for t in node.targets for n in _target_names(t)]
continue elif isinstance(node, (ast.AnnAssign, ast.AugAssign)) and node.value is not None:
func = call.func pairs += [(n, node.value) for n in _target_names(node.target)]
if isinstance(func, ast.Name): elif isinstance(node, ast.NamedExpr):
words.add(func.id) pairs += [(n, node.value) for n in _target_names(node.target)]
elif isinstance(func, ast.Attribute): bindings: dict[str, set[str]] = {}
words.add(func.attr) for _ in range(4):
for argument in [*call.args, *[k.value for k in call.keywords]]: changed = False
for child in ast.walk(argument): for name, value in pairs:
if isinstance(child, ast.Constant) and isinstance(child.value, str): found = _strings_in(value, bindings)
constants.add(child.value) if not found <= bindings.get(name, set()):
words |= set(_TOKEN.findall(child.value)) bindings.setdefault(name, set()).update(found)
elif isinstance(child, ast.Name): changed = True
words.add(child.id) if not changed:
elif isinstance(child, ast.Attribute): break
words.add(child.attr) return bindings
return words, constants
def _starter_aliases(tree: ast.AST) -> set[str]:
"""Prosess-startere importert bart (``from subprocess import run``)."""
names: set[str] = set()
for node in ast.walk(tree):
if not isinstance(node, ast.ImportFrom) or node.module not in ("subprocess", "os"):
continue
for alias in node.names:
if (
alias.name in _PROCESS_ATTRS
or alias.name == "system"
or alias.name.startswith("exec")
):
names.add(alias.asname or alias.name)
return names
def _is_process_start(call: ast.Call, aliases: set[str]) -> bool:
func = call.func
if isinstance(func, ast.Attribute) and isinstance(func.value, ast.Name):
if func.value.id == "subprocess" and func.attr in _PROCESS_ATTRS:
return True
if func.value.id == "os" and (func.attr == "system" or func.attr.startswith("exec")):
return True
return isinstance(func, ast.Name) and func.id in aliases
@dataclass(frozen=True)
class DoorUse:
"""Hva proben FAKTISK gjorde med døren, målt i probens egen kilde.
``executed``: en prosess ble startet med dørens navn i argumentene, eller inngangssymbolet
ble importert fra dørens modul og kalt. ``proven``: en ``assert`` er DATAAVHENGIG av det
kallet. ``payload``: strengene kallet faktisk bar kommandolinja, ikke kildeteksten."""
executed: bool
proven: bool
payload: frozenset[str]
def door_executions(
tree: ast.AST,
live: Mapping[str, Sequence[ast.AST]],
entry: Mapping[str, Any],
dotted: str,
repo_root: Path,
) -> DoorUse:
"""Proben KJØRER døren, og asserter på det kjøringen ga — målt, ikke lest som tekst.
Dette er tredje utgave av regelen, og de to første falt samme måte: de leste et NAVN.
Utgave 1 slo opp om en nodeid var ``passed``; utgave 2 krevde at dørens navn sto som en
streng et kall brukte, og dommerens seks former (navnet i en ubrukt variabel, modulen
importert uten kall, kallet i en død gren, kallet etter ``return``, resultatet forkastet,
navnet skrevet ut med ``print``) gikk alle gjennom.
To krav, begge målt fram: et UTFØRELSESSTED (en prosess-starter, eller inngangen importert
OG kalt) og en assert som leser det utførelsen ga. En hjelper som returnerer kallet regnes
som døren selv det er formen suitens egne prober har."""
scope = str(entry.get("scope", "main"))
names = {dotted}
target = f"{dotted}:{scope}"
names |= {name for name, value in console_scripts(repo_root).items() if value == target}
starters = _starter_aliases(tree)
parent, _, last = dotted.rpartition(".")
module_aliases: set[str] = set()
imported = False
for node in ast.walk(tree):
if isinstance(node, ast.ImportFrom):
module = node.module or ""
if module == parent:
module_aliases |= {a.asname or a.name for a in node.names if a.name == last}
elif module == dotted:
imported = imported or any(a.name == scope for a in node.names)
elif isinstance(node, ast.Import):
module_aliases |= {
a.asname or a.name.rpartition(".")[2] for a in node.names if a.name == dotted
}
bindings = string_bindings([*_live_nodes(tree), *[n for nodes in live.values() for n in nodes]])
payload: set[str] = set()
door_calls: set[int] = set()
door_functions: set[str] = set()
def arguments(call: ast.Call) -> set[str]:
found: set[str] = set()
for argument in [*call.args, *[k.value for k in call.keywords]]:
found |= _strings_in(argument, bindings)
return found
def is_door(call: ast.Call) -> bool:
if _is_process_start(call, starters):
found = arguments(call)
if found & names:
payload.update(found)
return True
return False
func = call.func
in_process = (
isinstance(func, ast.Attribute)
and func.attr == scope
and isinstance(func.value, ast.Name)
and func.value.id in module_aliases
) or (isinstance(func, ast.Name) and func.id == scope and imported)
through_helper = isinstance(func, ast.Name) and func.id in door_functions
if in_process or through_helper:
payload.update(arguments(call))
return True
return False
for _ in range(4):
before = (len(door_calls), len(door_functions))
for name, nodes in live.items():
for node in nodes:
if isinstance(node, ast.Call) and is_door(node):
door_calls.add(id(node))
if any(
isinstance(stmt, ast.Return)
and stmt.value is not None
and any(child is node for child in ast.walk(stmt.value))
for stmt in nodes
if isinstance(stmt, ast.Return)
):
door_functions.add(name)
if (len(door_calls), len(door_functions)) == before:
break
def reads_door(node: ast.AST, tainted: set[str]) -> bool:
return any(
id(child) in door_calls or (isinstance(child, ast.Name) and child.id in tainted)
for child in ast.walk(node)
)
tainted: set[str] = set()
for _ in range(4):
changed = False
for nodes in live.values():
for node in nodes:
targets: list[ast.AST] = []
if isinstance(node, ast.Assign) and reads_door(node.value, tainted):
targets = list(node.targets)
elif (
isinstance(node, (ast.AnnAssign, ast.NamedExpr))
and node.value is not None
and reads_door(node.value, tainted)
):
targets = [node.target]
elif isinstance(node, ast.With):
targets = [
item.optional_vars
for item in node.items
if item.optional_vars is not None and reads_door(item.context_expr, tainted)
]
for bound in targets:
for label in _target_names(bound):
if label not in tainted:
tainted.add(label)
changed = True
if not changed:
break
proven = any(
isinstance(node, ast.Assert) and reads_door(node, tainted)
for nodes in live.values()
for node in nodes
)
return DoorUse(bool(door_calls), proven, frozenset(payload))
def step_needles(step: Mapping[str, Any]) -> set[str]: def step_needles(step: Mapping[str, Any]) -> set[str]:
@ -452,68 +685,23 @@ def step_needles(step: Mapping[str, Any]) -> set[str]:
return needles return needles
def drives_door(
tree: ast.AST,
nodes: Sequence[ast.AST],
entry: Mapping[str, Any],
dotted: str,
repo_root: Path,
) -> bool:
"""Proben rører døren — MÅLT i probens egen kilde, på én av to måter.
Som subprosess: dørens dotted modulnavn (``python -m <modul>``) eller dens registrerte
kommandonavn står som en streng et kall bruker. Eller i prosess: inngangen er importert fra
dørens egen modul og kalt. Alt annet er en test som består et annet sted."""
scope = str(entry.get("scope", "main"))
names = {dotted}
if str(entry.get("kind", "")) == "console-script":
target = f"{dotted}:{scope}"
names |= {name for name, value in console_scripts(repo_root).items() if value == target}
if names & door_strings(nodes):
return True
parent, _, last = dotted.rpartition(".")
aliases: set[str] = set()
imported = False
for node in ast.walk(tree):
if isinstance(node, ast.ImportFrom):
module = node.module or ""
if module == parent:
aliases |= {a.asname or a.name for a in node.names if a.name == last}
elif module == dotted:
imported = imported or any(a.name == scope for a in node.names)
elif isinstance(node, ast.Import):
aliases |= {
a.asname or a.name.rpartition(".")[2] for a in node.names if a.name == dotted
}
for node in nodes:
for call in ast.walk(node):
if not isinstance(call, ast.Call):
continue
func = call.func
if (
isinstance(func, ast.Attribute)
and func.attr == scope
and isinstance(func.value, ast.Name)
and func.value.id in aliases
):
return True
if isinstance(func, ast.Name) and func.id == scope and imported:
return True
return False
def probe_binds( def probe_binds(
repo_root: Path, repo_root: Path,
step: Mapping[str, Any], step: Mapping[str, Any],
nodeid: str, nodeid: str,
package: str = "portfolio_optimiser", package: str = "portfolio_optimiser",
alone: bool = False,
) -> tuple[bool, str]: ) -> tuple[bool, str]:
"""(proben beviser DETTE steget, grunnen når den ikke gjør det). """(proben beviser DETTE steget, grunnen når den ikke gjør det).
Målt 19.09.2026 (N2/N2b): gaten slo opp om nodeid-en var ``passed`` og godtok den. En Målt 19.09.2026 (N2/N2b): gaten slo opp om nodeid-en var ``passed`` og godtok den. En
registrert stub-dør med en probe pekt en vilkårlig grønn test tok rad 1 fra 3 til 4 av 17 registrert stub-dør med en probe pekt en vilkårlig grønn test tok rad 1 fra 3 til 4 av 17
og gjort for alle fjorten røde steg: 17 av 17 GRØNN, uten én linje kapabilitet. Bindingen og gjort for alle fjorten røde steg: 17 av 17 GRØNN, uten én linje kapabilitet. Bindingen
er derfor en MÅLING i probens kilde, ikke en erklæring i kontrakten.""" er derfor en MÅLING i probens kilde, ikke en erklæring i kontrakten.
``alone`` sier at steget er ALENE bak sin dør. Da er det å kjøre døren å kjøre steget, og
navngivningen faller bort den er en diskriminator mellom steg som DELER en dør, og målt
19.09 avviste den en ekte ende-til-ende dørprobe (``gate``) en navneteknikalitet."""
file_part, separator, rest = nodeid.partition("::") file_part, separator, rest = nodeid.partition("::")
if not separator or not rest: if not separator or not rest:
return False, f"proben {nodeid} navngir ingen test" return False, f"proben {nodeid} navngir ingen test"
@ -528,16 +716,27 @@ def probe_binds(
functions = _functions(tree) functions = _functions(tree)
if name not in functions: if name not in functions:
return False, f"proben {name} finnes ikke i {file_part}" return False, f"proben {name} finnes ikke i {file_part}"
nodes = [functions[n] for n in sorted(_reachable(functions, name))] live = {n: _live_nodes(functions[n]) for n in sorted(_reachable(functions, name))}
if not any(isinstance(x, ast.Assert) for node in nodes for x in ast.walk(node)): if not any(isinstance(x, ast.Assert) for nodes in live.values() for x in nodes):
return False, f"proben {name} asserter ingenting — da måler den ikke" return False, f"proben {name} asserter ingenting — da måler den ikke"
dotted = _dotted(package, str(step["entry"]["module"])) dotted = _dotted(package, str(step["entry"]["module"]))
if not drives_door(tree, nodes, step["entry"], dotted, repo_root): use = door_executions(tree, live, step["entry"], dotted, repo_root)
return False, f"proben {name} rører ikke døren ({dotted})" if not use.executed:
return False, f"proben {name} rører ikke døren ({dotted}) — ingen utførelse i kilden"
if not use.proven:
return False, f"proben {name} asserter ikke på det døren gjorde"
words = {word for text in use.payload for word in _TOKEN.findall(text)}
command = str(dict(step["entry"]).get("command", ""))
if command and command not in words:
return False, f"proben {name} kjører ikke underkommandoen {command!r}"
if alone:
return True, ""
needles = step_needles(step) needles = step_needles(step)
words, _constants = call_payload(nodes)
if not needles & words: if not needles & words:
return False, f"proben {name} navngir ikke steget ({', '.join(sorted(needles))})" return (
False,
f"proben {name} navngir ikke steget ({', '.join(sorted(needles))}) i det den kjørte",
)
return True, "" return True, ""
@ -556,12 +755,13 @@ def _binding_why(
repo_root: Path, repo_root: Path,
package: str, package: str,
shared: Mapping[str, Sequence[str]], shared: Mapping[str, Sequence[str]],
alone: bool = False,
) -> str: ) -> str:
for nodeid in probes: for nodeid in probes:
if nodeid in shared: if nodeid in shared:
others = ", ".join(i for i in shared[nodeid] if i != str(step["id"])) others = ", ".join(i for i in shared[nodeid] if i != str(step["id"]))
return f"atferdsprobe: delt med {others} — en probe beviser ett steg, eller ingen" return f"atferdsprobe: delt med {others} — en probe beviser ett steg, eller ingen"
ok, why = probe_binds(repo_root, step, nodeid, package) ok, why = probe_binds(repo_root, step, nodeid, package, alone)
if not ok: if not ok:
return f"atferdsprobe: {why}" return f"atferdsprobe: {why}"
return "" return ""
@ -618,6 +818,11 @@ def measure_steps(
) -> list[Step]: ) -> list[Step]:
measured: list[Step] = [] measured: list[Step] = []
shared = shared_probes(steps) shared = shared_probes(steps)
#: Hvor mange steg som står bak HVER dør. Ett steg bak en dør trenger ingen navngivning —
#: to gjør, og da er det navnet som sier hvilket av dem proben beviste.
behind: dict[tuple[str, str, str], int] = {}
for step in steps:
behind[door_key(step)] = behind.get(door_key(step), 0) + 1
for step in steps: for step in steps:
tree = _tree(src, str(step["module"])) tree = _tree(src, str(step["module"]))
resolved = tree is not None and _defined(tree, str(step["symbol"])) resolved = tree is not None and _defined(tree, str(step["symbol"]))
@ -640,7 +845,9 @@ def measure_steps(
elif verdict: elif verdict:
why = f"atferdsprobe: {verdict}" why = f"atferdsprobe: {verdict}"
else: else:
why = _binding_why(step, probes, repo_root, package, shared) why = _binding_why(
step, probes, repo_root, package, shared, behind[door_key(step)] == 1
)
external = resolved and called and not why external = resolved and called and not why
measured.append(Step(str(step["id"]), str(step["label"]), resolved, called, external, why)) measured.append(Step(str(step["id"]), str(step["label"]), resolved, called, external, why))
return measured return measured
@ -663,7 +870,7 @@ def score_toolbox(
#: Målt 19.09 (N4/N5): et steg flyttet til «held_out» tok nevneren 17 → 16 MED ELLER UTEN #: Målt 19.09 (N4/N5): et steg flyttet til «held_out» tok nevneren 17 → 16 MED ELLER UTEN
#: grunn, og sammendraget sa «med grunn» uansett. En tom grunn er ingen grunn: symbolet blir #: grunn, og sammendraget sa «med grunn» uansett. En tom grunn er ingen grunn: symbolet blir
#: stående i nevneren som et kall uten dør, nøyaktig som et udeklarert kall. #: stående i nevneren som et kall uten dør, nøyaktig som et udeklarert kall.
held = {symbol: reason for symbol, reason in claimed.items() if reason.strip()} held = {symbol: reason for symbol, reason in claimed.items() if named_reason(reason)}
calls = run_path_calls(src, run_path) if run_path else {} calls = run_path_calls(src, run_path) if run_path else {}
undeclared = sorted(name for name in calls if name not in declared and name not in held) undeclared = sorted(name for name in calls if name not in declared and name not in held)
n = len(in_path) + len(undeclared) n = len(in_path) + len(undeclared)
@ -678,7 +885,7 @@ def score_toolbox(
for name in undeclared for name in undeclared
) )
diagnostics = tuple( diagnostics = tuple(
f"holdt utenfor: {symbol}{reason or 'UTEN GRUNN'}" f"holdt utenfor: {symbol}{reason if named_reason(reason) else 'UTEN GRUNN'}"
for symbol, reason in sorted(claimed.items()) for symbol, reason in sorted(claimed.items())
) + tuple( ) + tuple(
f"holdt utenfor: {symbol} kalles ikke lenger i kjørestien (foreldet unntak)" f"holdt utenfor: {symbol} kalles ikke lenger i kjørestien (foreldet unntak)"
@ -768,21 +975,25 @@ def score_roles(roles: Mapping[str, Any], outcomes: Mapping[str, str], src: Path
_FENCE = re.compile(r"^\s*(```|~~~)") _FENCE = re.compile(r"^\s*(```|~~~)")
#: Rad 3: hva vakten IKKE rekker, sagt av raden selv — og ETTERPRØVD. Erklæringen oppga to #: Rad 3: hva vakten ER, og hva den ikke kan se — sagt av raden selv.
#: gjenstående veier; PM plantet 21 kallformer 19.09 og MÅLTE at seks slapp. De fire billigste #:
#: (den offisielle SDK-en i to skriveformer, node- og uv-kjørerne, det dynamiske import-kallet) #: Tre runder på rad sa erklæringen hvor mange veier som STO IGJEN («NØYAKTIG TO», så «TRE»), og
#: er lukket med hver sin vakt; de tre som står igjen står her, navngitt. #: begge gangene veltet den første nye målingen den: 19.09 slapp tolv plantede former forbi, ni
#: av dem utenfor de navngitte klassene. Feilen var ikke at vakten er ufullstendig — det sier
#: raden selv — men at setningen var en OPPREGNING. Den sier nå hva vakten er.
NO_CLAUDE_LIMIT = ( NO_CLAUDE_LIMIT = (
"rad 3 er en TEKSTVAKT over hele det publiserte manifestet, ikke en dataflyt-analyse: den " "rad 3 er TEKSTMØNSTRE over hvert spor i git-manifestet, ikke en dataflyt-analyse: den "
"feller et programnavn skrevet ut, en absolutt sti, en liste lagt i en variabel, en " "feller et navn som STÅR SKREVET — programnavnet, en absolutt sti, en liste lagt i en "
"konstant, en shell-streng, den offisielle Python-SDK-en, en node- eller uv-kjører som " "variabel, en konstant, en shell-streng, de offisielle SDK-ene i Python og TypeScript, en "
"starter kommandoen, og et dynamisk import-kall. TRE veier står igjen og kan ikke felles " "node-, deno- eller uv-kjører som starter kommandoen, og et dynamisk import-kall i begge "
"linje for linje: et kommandonavn satt sammen av deler ved kjøretid, et navn lest ut av en " "skriveformer. Den ser IKKE et navn som blir til først når koden kjører: satt sammen av "
"miljøvariabel, og et navn som er base64-kodet eller på annen måte avkodet først. Raden " "deler, lest ut av en miljøvariabel, base64-kodet eller avkodet på annen måte. Den ser "
"påstår derfor ikke at ingen vei finnes; den påstår at ingen SKREVET vei finnes, over en " "heller ikke noe utenfor manifestet — en avhengighet, et container-bilde, en tjeneste i den "
"nevner den oppgir i filer. k av n er ikke et dekningsmål: det teller vakter som MÅLER " "andre enden av et nettkall. Lista over kjørere og SDK-er er ikke uttømmende, og raden "
"(hver med kjent-positiv og kjent-negativ), aldri hvor stor del av angrepsflaten de dekker " "påstår ikke at ingen vei finnes; den påstår at ingen SKREVET vei finnes, over en nevner "
"— en vakt lagt til uten en målt omgåelse bak seg hever både k og n og beviser ingenting" "den oppgir i filer. k av n er ikke et dekningsmål: det teller vakter som MÅLER (hver med "
"kjent-positiv og kjent-negativ), aldri hvor stor del av angrepsflaten de dekker — en vakt "
"lagt til uten en målt omgåelse bak seg hever både k og n og beviser ingenting"
) )

View file

@ -44,9 +44,9 @@ _NOW = datetime(2026, 9, 19, 12, 0, tzinfo=timezone.utc)
#: Repoets egne tall ved skriving (19.09.2026), talt av armene under mot KILDEN. De står her for #: Repoets egne tall ved skriving (19.09.2026), talt av armene under mot KILDEN. De står her for
#: at en stille endring i kjørestien skal vise seg som en rød test og ikke som et nytt tall. #: at en stille endring i kjørestien skal vise seg som en rød test og ikke som et nytt tall.
_STEPS_TODAY = 17 _STEPS_TODAY = 17
_EXTERNAL_TODAY = 0 _EXTERNAL_TODAY = 1
_ROLES_TODAY = ("proposer", "checker") _ROLES_TODAY = ("proposer", "checker")
_PATTERNS_TODAY = 12 _PATTERNS_TODAY = 15
def _write(path: Path, text: str) -> None: def _write(path: Path, text: str) -> None:
@ -106,10 +106,10 @@ def test_the_step_denominator_equals_an_independent_count_of_the_source() -> Non
assert row.n == _STEPS_TODAY assert row.n == _STEPS_TODAY
def test_row1_today_is_red_with_no_step_callable_from_outside() -> None: def test_row1_today_is_red_with_the_steps_that_have_no_door_named() -> None:
"""Var «3 av 17» til 19.09. De tre falt da proben ble BUNDET til steget: to av dem driver """Var «3 av 17» til 19.09, så 0 av 17 da proben ble BUNDET til steget. Fra 20.09 teller
riktig dør uten å navngi hvilket steg de beviser, den tredje går ikke gjennom døren i det ``gate`` igjen: den proben kjører v1-gaten som subprosess og leser JSON-en ut av den, og
hele tatt. Ingen av dem var en kodefeil det var kontrakten som godtok dem.""" steget er alene bak den døren den ble avvist en navneteknikalitet, ikke atferd."""
passing = {n: "passed" for step in _CONFIG["steps"] for n in step.get("probe", ())} passing = {n: "passed" for step in _CONFIG["steps"] for n in step.get("probe", ())}
row = gate.score_toolbox(_CONFIG["steps"], _SRC, _CONFIG["run_path"], passing, _REPO) row = gate.score_toolbox(_CONFIG["steps"], _SRC, _CONFIG["run_path"], passing, _REPO)
assert (row.k, row.n, row.status) == (_EXTERNAL_TODAY, _STEPS_TODAY, gate.RED) assert (row.k, row.n, row.status) == (_EXTERNAL_TODAY, _STEPS_TODAY, gate.RED)
@ -117,7 +117,7 @@ def test_row1_today_is_red_with_no_step_callable_from_outside() -> None:
assert "validering" in named and "utboks" in named assert "validering" in named and "utboks" in named
assert "prepass-artefakt" in named and "parse-feil" in named assert "prepass-artefakt" in named and "parse-feil" in named
bound = {x.split(":")[0] for x in row.exceptions if "atferdsprobe" in x} bound = {x.split(":")[0] for x in row.exceptions if "atferdsprobe" in x}
assert bound == {"rundebinding", "rapport", "gate"}, row.exceptions assert bound == {"rundebinding", "rapport"}, row.exceptions
def _fake_src(root: Path, *, chat_client: bool, symbol: str = "gjoer_noe") -> Path: def _fake_src(root: Path, *, chat_client: bool, symbol: str = "gjoer_noe") -> Path:
@ -1136,14 +1136,20 @@ def test_a_probe_that_drives_the_door_without_naming_the_step_proves_the_door_on
tmp_path: Path, tmp_path: Path,
) -> None: ) -> None:
"""To steg bak SAMME dør er det vanlige tilfellet (``round_builder`` bærer to). En probe som """To steg bak SAMME dør er det vanlige tilfellet (``round_builder`` bærer to). En probe som
bare kjører kommandoen skiller dem ikke den navngi steget den beviser.""" bare kjører kommandoen skiller dem ikke den navngi steget den beviser.
src = _fake_src(tmp_path, chat_client=False)
_write_probe(tmp_path)
assert gate.score_toolbox(_one_step(), src, _fake_run_path(), _FAKE_PASSED, tmp_path).k == 1
_write_probe(tmp_path, needle="et_helt_annet_steg") Nevneren er TO med vilje (endret 20.09): navngivningen er en diskriminator, og et steg som
row = gate.score_toolbox(_one_step(), src, _fake_run_path(), _FAKE_PASSED, tmp_path) er alene bak sin dør har ingenting å skilles fra se
assert (row.k, row.n) == (0, 1) ``test_the_sole_step_behind_a_door_is_proved_by_driving_that_door``."""
src = _two_step_src(tmp_path)
a = _write_probe(tmp_path, needle="gjoer_noe", nodeid="tests/test_a.py::test_a")
b = _write_probe(tmp_path, needle="gjoer_noe_annet", nodeid="tests/test_b.py::test_b")
outcomes = {a: "passed", b: "passed"}
assert gate.score_toolbox(_two_steps(a, b), src, _fake_run_path(), outcomes, tmp_path).k == 2
b = _write_probe(tmp_path, needle="et_helt_annet_steg", nodeid="tests/test_b.py::test_b")
row = gate.score_toolbox(_two_steps(a, b), src, _fake_run_path(), outcomes, tmp_path)
assert (row.k, row.n) == (1, 2)
assert any("navngir ikke steget" in x for x in row.exceptions), row.exceptions assert any("navngir ikke steget" in x for x in row.exceptions), row.exceptions
@ -1165,9 +1171,18 @@ def test_a_probe_that_only_mentions_the_door_or_the_step_proves_neither(tmp_path
assert (row.k, row.n) == (0, 1), row.exceptions assert (row.k, row.n) == (0, 1), row.exceptions
assert any("rører ikke døren" in x for x in row.exceptions), row.exceptions assert any("rører ikke døren" in x for x in row.exceptions), row.exceptions
_write_probe(tmp_path, needle="et_annet_navn", docstring="gjoer_noe") to_steg = _two_step_src(tmp_path)
row = gate.score_toolbox(_one_step(), src, _fake_run_path(), _FAKE_PASSED, tmp_path) a = _write_probe(tmp_path, needle="gjoer_noe", nodeid="tests/test_a.py::test_a")
assert (row.k, row.n) == (0, 1), row.exceptions b = _write_probe(
tmp_path,
needle="et_annet_navn",
docstring="gjoer_noe_annet",
nodeid="tests/test_b.py::test_b",
)
row = gate.score_toolbox(
_two_steps(a, b), to_steg, _fake_run_path(), {a: "passed", b: "passed"}, tmp_path
)
assert (row.k, row.n) == (1, 2), row.exceptions
assert any("navngir ikke steget" in x for x in row.exceptions), row.exceptions assert any("navngir ikke steget" in x for x in row.exceptions), row.exceptions
@ -1452,3 +1467,398 @@ def test_a_runbook_without_the_sections_the_contract_names_is_not_green(tmp_path
row = gate.score_runbook(cfg, tmp_path, now=_NOW) row = gate.score_runbook(cfg, tmp_path, now=_NOW)
assert row.status == gate.NOT_MEASURED, row.exceptions assert row.status == gate.NOT_MEASURED, row.exceptions
assert any("seksjon" in x for x in row.exceptions), row.exceptions assert any("seksjon" in x for x in row.exceptions), row.exceptions
# --- rad 1, TREDJE reparasjon: bindingen måler UTFØRELSE, ikke tekst --------------------------
#
# Sjekkpunktet 19.09 (`a37e429`) MÅLTE at rad 1 kunne bli 17 av 17 med 17 énlinjes-prober og en
# dørmodul uten én eneste import: «proben rører døren» var en tekstsjekk ett hakk lenger inne.
# Dommerens seks probeformer + oppskriften står her, hver som sin egen arm, og hver skal gi 0.
def _write_raw_probe(root: Path, body: str, nodeid: str = _FAKE_PROBE) -> str:
"""En probe skrevet ORDRETT — de uærlige formene kan ikke uttrykkes gjennom ``_write_probe``."""
file_part, _, name = nodeid.partition("::")
_write(root / file_part, body.replace("<navn>", name))
return nodeid
#: Dommerens sju former, verbatim slik de ble målt: seks som BRAST mot forrige utgave, pluss
#: oppskriften «17 énlinjes-prober + dørmodul uten imports» i sin minste form. Døren er
#: ``fakesrc.driver``, steget ``gjoer_noe`` — nøyaktig dem ``_one_step`` erklærer.
_JUKSEFORMER: tuple[tuple[str, str], ...] = (
(
"a1-navn-i-ubrukt-variabel",
"def <navn>(tmp_path):\n"
' door = "fakesrc.driver"\n'
' steg = "gjoer_noe"\n'
" assert isinstance(door, str) and isinstance(steg, str)\n",
),
(
"a2-importert-aldri-kalt",
"from fakesrc import driver\n\n\n"
"def <navn>(tmp_path):\n"
" assert driver is not None\n"
' assert "gjoer_noe"\n',
),
(
"b1-kallet-i-doed-gren",
"import subprocess\nimport sys\n\n\n"
"def <navn>(tmp_path):\n"
" if False:\n"
' subprocess.run([sys.executable, "-m", "fakesrc.driver", "--steg", "gjoer_noe"])\n'
" assert True\n",
),
(
"b2-kallet-etter-return",
"import subprocess\nimport sys\n\n\n"
"def <navn>(tmp_path):\n"
' assert "gjoer_noe"\n'
" return None\n"
' subprocess.run([sys.executable, "-m", "fakesrc.driver", "--steg", "gjoer_noe"])\n',
),
(
"c1-resultatet-forkastet",
"import subprocess\nimport sys\n\n\n"
"def <navn>(tmp_path):\n"
' subprocess.run([sys.executable, "-m", "fakesrc.driver", "--steg", "gjoer_noe"])\n'
" assert 2 + 2 == 4\n",
),
(
"c2-bare-skrevet-ut",
'def <navn>(tmp_path):\n print("fakesrc.driver", "gjoer_noe")\n assert True\n',
),
(
"oppskriften-enlinjes",
'def <navn>(tmp_path):\n assert len("fakesrc.driver") and len("gjoer_noe")\n',
),
)
@pytest.mark.parametrize(("form", "body"), _JUKSEFORMER, ids=[n for n, _ in _JUKSEFORMER])
def test_none_of_the_judges_seven_forms_is_a_behaviour_probe(
tmp_path: Path, form: str, body: str
) -> None:
"""Hver form plantes ALENE, med rc-0-kontrollen først: den ærlige proben teller, formen ikke.
Uten kontrollen ville armen bestått mot en gate som avviste ALT «0 av 1» er et
måleresultat bare når 1 av 1 er oppnåelig i samme oppsett."""
src = _fake_src(tmp_path, chat_client=False)
_write_probe(tmp_path)
kontroll = gate.score_toolbox(_one_step(), src, _fake_run_path(), _FAKE_PASSED, tmp_path)
assert (kontroll.k, kontroll.n) == (1, 1), kontroll.exceptions
_write_raw_probe(tmp_path, body)
row = gate.score_toolbox(_one_step(), src, _fake_run_path(), _FAKE_PASSED, tmp_path)
assert (row.k, row.n) == (0, 1), (form, row.exceptions)
assert any("atferdsprobe" in x for x in row.exceptions), (form, row.exceptions)
def test_a_probe_that_runs_the_door_and_throws_the_result_away_proves_nothing(
tmp_path: Path,
) -> None:
"""Dommerens c1, med sin EGEN grunn: kallet skjedde, men ingen assert leser det det ga.
Skilles fra «rører ikke døren», for det er to ulike feil og bare den ene er en løgn."""
src = _fake_src(tmp_path, chat_client=False)
_write_raw_probe(tmp_path, dict(_JUKSEFORMER)["c1-resultatet-forkastet"])
row = gate.score_toolbox(_one_step(), src, _fake_run_path(), _FAKE_PASSED, tmp_path)
assert row.k == 0
assert any("asserter ikke på det døren" in x for x in row.exceptions), row.exceptions
def test_a_probe_that_only_names_the_door_never_reaches_the_execution_rule(
tmp_path: Path,
) -> None:
"""Dommerens a1: navnet står i kilden, men ingen prosess starter. Grunnen skal være at døren
ikke RØRES ikke at asserten er feil, for da ville en assert-fiks «løst» det."""
src = _fake_src(tmp_path, chat_client=False)
_write_raw_probe(tmp_path, dict(_JUKSEFORMER)["a1-navn-i-ubrukt-variabel"])
row = gate.score_toolbox(_one_step(), src, _fake_run_path(), _FAKE_PASSED, tmp_path)
assert any("rører ikke døren" in x for x in row.exceptions), row.exceptions
def test_an_honest_probe_that_starts_the_door_through_a_helper_still_counts(
tmp_path: Path,
) -> None:
"""Den ÆRLIGE formen suiten faktisk bruker (``test_v1_gate._cli``): subprosessen startes i en
hjelper, og testen asserter det hjelperen ga tilbake. En regel som feller den, feller
kapabilitet som er den motsatte feilen av den vi retter."""
src = _fake_src(tmp_path, chat_client=False)
_write_raw_probe(
tmp_path,
"import subprocess\nimport sys\n\n\n"
"def _cli(*args):\n"
" return subprocess.run(\n"
' [sys.executable, "-m", "fakesrc.driver", *args], capture_output=True, text=True\n'
" )\n\n\n"
"def <navn>(tmp_path):\n"
' proc = _cli("--steg", "gjoer_noe")\n'
" assert proc.returncode == 0\n",
)
row = gate.score_toolbox(_one_step(), src, _fake_run_path(), _FAKE_PASSED, tmp_path)
assert (row.k, row.n) == (1, 1), row.exceptions
def test_a_probe_that_builds_the_command_in_a_variable_first_still_counts(
tmp_path: Path,
) -> None:
"""``round_builder``-probens form: ``base = [sys.executable, "-m", "<modul>"]`` og
``subprocess.run([*base, ])``. Strengen når kallet gjennom en variabel; det er en
utførelse."""
src = _fake_src(tmp_path, chat_client=False)
_write_raw_probe(
tmp_path,
"import subprocess\nimport sys\n\n\n"
"def <navn>(tmp_path):\n"
' base = [sys.executable, "-m", "fakesrc.driver"]\n'
' ok = subprocess.run([*base, "--steg", "gjoer_noe"], capture_output=True, text=True)\n'
" assert ok.returncode == 0, ok.stderr\n",
)
row = gate.score_toolbox(_one_step(), src, _fake_run_path(), _FAKE_PASSED, tmp_path)
assert (row.k, row.n) == (1, 1), row.exceptions
def test_the_step_name_must_stand_in_what_was_actually_executed(tmp_path: Path) -> None:
"""Navngivningen flyttes fra «et hvilket som helst kall-argument» til DEN kommandolinja som
ble kjørt. To steg bak samme dør, samme kommando ordet som skiller dem være i argv."""
src = _two_step_src(tmp_path)
a = _write_raw_probe(
tmp_path,
"import subprocess\nimport sys\n\n\n"
"def <navn>(tmp_path):\n"
" proc = subprocess.run(\n"
' [sys.executable, "-m", "fakesrc.driver", "--steg", "gjoer_noe"],\n'
" capture_output=True,\n"
" )\n"
" assert proc.returncode == 0\n",
nodeid="tests/test_a.py::test_a",
)
b = _write_raw_probe(
tmp_path,
"import subprocess\nimport sys\n\n\n"
"def <navn>(tmp_path):\n"
" proc = subprocess.run(\n"
' [sys.executable, "-m", "fakesrc.driver"], capture_output=True\n'
" )\n"
' hjelp("gjoer_noe_annet")\n'
" assert proc.returncode == 0\n",
nodeid="tests/test_b.py::test_b",
)
row = gate.score_toolbox(
_two_steps(a, b), src, _fake_run_path(), {a: "passed", b: "passed"}, tmp_path
)
assert (row.k, row.n) == (1, 2), row.exceptions
assert any("navngir ikke steget" in x for x in row.exceptions), row.exceptions
def test_the_sole_step_behind_a_door_is_proved_by_driving_that_door(tmp_path: Path) -> None:
"""KLASSE-rettingen av 3 → 0: navngivningen er en DISKRIMINATOR mellom steg som deler dør.
Er steget alene bak sin dør, er det å kjøre døren å kjøre steget og en ekte ende-til-ende
dørprobe skal ikke avvises en navneteknikalitet (målt 19.09 ``gate``-proben)."""
src = _fake_src(tmp_path, chat_client=False)
_write_probe(tmp_path, needle="et_helt_annet_ord")
row = gate.score_toolbox(_one_step(), src, _fake_run_path(), _FAKE_PASSED, tmp_path)
assert (row.k, row.n) == (1, 1), row.exceptions
# ... og den samme proben beviser INGEN av to steg som deler døren.
delt = _write_probe(tmp_path, needle="et_helt_annet_ord", nodeid="tests/test_x.py::test_x")
annen = _write_probe(tmp_path, needle="et_helt_annet_ord", nodeid="tests/test_y.py::test_y")
row = gate.score_toolbox(
_two_steps(delt, annen),
_two_step_src(tmp_path),
_fake_run_path(),
{delt: "passed", annen: "passed"},
tmp_path,
)
assert row.k == 0, row.exceptions
assert all("navngir ikke steget" in x for x in row.exceptions), row.exceptions
def test_the_gates_own_gate_probe_binds_after_the_class_fix() -> None:
"""Det ene steget som SKULLE telle før og ikke gjorde det: ``gate`` kjører v1-gaten som
subprosess og leser JSON-en ut av den, og er ALENE bak sin dør talt her, ikke antatt.
Målt gjennom raden, ikke gjennom en hjelper: det er raden som er påstanden."""
doer = [
(s["entry"]["module"], s["entry"].get("scope", "main"), s["entry"].get("command", ""))
for s in _CONFIG["steps"]
]
gate_step = next(s for s in _CONFIG["steps"] if s["id"] == "gate")
noekkel = (
gate_step["entry"]["module"],
gate_step["entry"].get("scope", "main"),
gate_step["entry"].get("command", ""),
)
assert doer.count(noekkel) == 1, "armen forutsetter at gate er alene bak sin dør"
row = gate.score_toolbox(
_CONFIG["steps"], _SRC, _CONFIG["run_path"], _all_probes_passed(), _REPO
)
assert not any(x.startswith("gate:") for x in row.exceptions), row.exceptions
def _all_probes_passed() -> dict[str, str]:
"""Hver nodeid kontrakten navngir, som «passed» — utfallet er IKKE det som måles her."""
return {nodeid: "passed" for nodeid in gate.probe_nodeids(_CONFIG)}
def test_the_door_rule_says_that_it_measures_an_execution_and_states_its_limit() -> None:
"""A8-klassen: regelen skal si nøyaktig hva koden MÅLER, og hva den ikke kan se."""
rule = gate.EXTERNAL_DOOR.casefold()
assert "utførelse" in rule
assert "dataavhengig" in rule
assert "alene bak" in rule
# Grensen dommeren navnga som den eneste formen som ikke kan skrives forbi.
assert "muter" in rule or "mutant" in rule
# --- rad 1: en grunn skal være NAVNGITT prosa, ikke bare ikke-tom -----------------------------
@pytest.mark.parametrize("plassholder", ["-", "todo", "x", ".", "??", "n/a"])
def test_a_placeholder_is_not_a_named_reason(tmp_path: Path, plassholder: str) -> None:
"""Sjekkpunktet målte at «-», «todo», «x» og «.» alle ble godtatt som grunn: ``.strip()``
skilte tom fra ikke-tom, ikke grunn fra ikke-grunn."""
src, spec = _mini_run_path(tmp_path, extra_call=True)
spec["held_out"] = [{"symbol": "beta", "reason": "hjelper inne i alfa, ikke et eget steg"}]
steps = [
{
"id": "alfa",
"label": "alfa",
"module": "steg.py",
"symbol": "alfa",
"driver": {"module": "kjor.py", "scope": "run_project"},
"entry": {"kind": "module-main", "module": "kjor.py", "scope": "main"},
}
]
assert gate.score_toolbox(steps, src, spec, {}, tmp_path).n == 1, "kontroll: ekte prosa holder"
spec["held_out"] = [{"symbol": "beta", "reason": plassholder}]
row = gate.score_toolbox(steps, src, spec, {}, tmp_path)
assert row.n == 2, f"{plassholder!r} er ingen grunn — symbolet blir i nevneren"
assert any("UTEN grunn" in x for x in row.exceptions), row.exceptions
def test_every_held_out_reason_in_the_contract_is_named_prose() -> None:
"""Terskelen måles mot kontraktens egne 28 grunner, gjennom RADEN: strammes den så den
feller ekte prosa, vokser nevneren og denne armen blir rød. Tallet 28 er talt her."""
grunner = [str(h["reason"]) for h in _CONFIG["run_path"]["held_out"]]
assert len(grunner) == 28
assert min(len(g) for g in grunner) >= 30, "margin mot terskelen, målt"
row = gate.score_toolbox(_CONFIG["steps"], _SRC, _CONFIG["run_path"], {}, _REPO)
assert row.n == _STEPS_TODAY, "en ekte grunn skal ikke falle ut som plassholder"
assert sum(1 for d in row.diagnostics if d.startswith("holdt utenfor: ")) >= len(grunner)
assert not any("UTEN GRUNN" in d for d in row.diagnostics), row.diagnostics
def test_the_row_limit_says_the_off_path_steps_have_no_derived_denominator() -> None:
"""N7, uttalt: de fire stegene utenfor kjørestien har ingen utledet kilde, og et slettet
off-path-steg tar derfor nevneren 17 16. Gaten kan ikke felle det; suiten kan."""
assert "utenfor kjørestien" in gate.RUN_PATH_LIMIT
assert "suiten" in gate.RUN_PATH_LIMIT
#: De fire stegene som er erklært UTENFOR kjørestien, talt for hånd i kontrakten 20.09.2026.
#: Dette er N7s eneste vakt: sletter noen et av dem, krymper nevneren stille i GATEN — her blir
#: det rødt, og navnet står igjen i suiten.
_OFF_PATH_TODAY: tuple[tuple[str, str, str], ...] = (
("gate", "evals/v1_gate.py", "main"),
("rapport", "evals/round_builder.py", "build_round"),
("rundebinding", "evals/round_builder.py", "main"),
("validering", "run.py", "evaluate_mandate_candidates"),
)
def test_no_off_path_step_can_be_deleted_without_this_arm_going_red() -> None:
driver = (str(_CONFIG["run_path"]["module"]), str(_CONFIG["run_path"]["scope"]))
funnet = tuple(
sorted(
(str(s["id"]), str(s["driver"]["module"]), str(s["driver"]["scope"]))
for s in _CONFIG["steps"]
if (str(s["driver"]["module"]), str(s["driver"]["scope"])) != driver
)
)
assert funnet == _OFF_PATH_TODAY
# --- rad 1: en underkommando er bare en dør når modulen selv er kallbar utenfra ---------------
def test_a_subcommand_is_only_a_door_when_its_module_is_reachable_from_outside(
tmp_path: Path,
) -> None:
"""En ``add_parser("x")`` i en modul ingen kan starte er ingen dør: underkommandoen er et
navn inne i en CLI, og CLI-en selv være registrert (konsoll-skript eller ``-m``)."""
src = tmp_path / "fakesrc"
_write(src / "steg.py", "def gjoer_noe():\n return 42\n")
_write(
src / "verktoey.py",
"import argparse\n\nfrom fakesrc.steg import gjoer_noe\n\n\n"
"def main(argv=None):\n"
" parser = argparse.ArgumentParser()\n"
' sub = parser.add_subparsers(dest="kommando")\n'
' sub.add_parser("naviger-pakke")\n'
" args = parser.parse_args(argv)\n"
' if args.kommando == "naviger-pakke":\n'
" return gjoer_noe()\n"
" return 2\n",
)
entry = {
"kind": "subcommand",
"module": "verktoey.py",
"scope": "main",
"command": "naviger-pakke",
}
_fake_pyproject(tmp_path, {})
ok, why = gate.registered_entry(src, tmp_path, "fakesrc", entry)
assert ok is False and ("pyproject" in why or "__main__" in why), why
_fake_pyproject(tmp_path, {"fake-verktoey": "fakesrc.verktoey:main"})
assert gate.registered_entry(src, tmp_path, "fakesrc", entry) == (True, "")
# --- rad 3: de fem veiene sjekkpunktet målte som åpne OG billige å lukke ----------------------
#: Hver form er base64 av nøyaktig det som ble plantet. Klartekst her ville vært radens eget funn.
_MAALTE_AAPNE_VEIER = (
("dynamisk-import-modul", "aW1wb3J0bGliLmltcG9ydF9tb2R1bGUoImNsYXVkZV9hZ2VudF9zZGsiKQ=="),
("dynamisk-import-modul-anthropic", "aW1wb3J0bGliLmltcG9ydF9tb2R1bGUoImFudGhyb3BpYyIp"),
("node-runner-deno", "ZGVubyBydW4gLUEgbnBtOkBhbnRocm9waWMtYWkvY2xhdWRlLWNvZGUgLXAgIngi"),
("node-runner-npm-exec", "bnBtIGV4ZWMgQGFudGhyb3BpYy1haS9jbGF1ZGUtY29kZSAtLSAtcCAieCI="),
("node-runner-yarn-dlx", "eWFybiBkbHggQGFudGhyb3BpYy1haS9jbGF1ZGUtY29kZSAtcCAieCI="),
(
"sdk-ts-import",
"aW1wb3J0IHsgcXVlcnkgfSBmcm9tICJAYW50aHJvcGljLWFpL2NsYXVkZS1hZ2VudC1zZGsiOw==",
),
)
@pytest.mark.parametrize(
("form", "snippet"), _MAALTE_AAPNE_VEIER, ids=[n for n, _ in _MAALTE_AAPNE_VEIER]
)
def test_every_form_the_checkpoint_measured_as_slipping_through_is_now_caught(
tmp_path: Path, form: str, snippet: str
) -> None:
"""Tolv indirekte former slapp 19.09; fem av dem var verken eksotiske eller uoppregnelige.
Hver plantes alene, med rc-0-kontroll før og etter."""
cfg = json.loads(json.dumps(_CONFIG["no_claude_path"]))
cfg["manifest"] = {"sentinel": "src/run.py", "sentinels": ["src/run.py"]}
_write(tmp_path / "src" / "run.py", "x = 1\n")
assert gate.score_no_claude_path(cfg, tmp_path).status == gate.GREEN, "kontroll: ren flate"
planted = tmp_path / "src" / "plantet.ts"
_write(planted, gate.decode(snippet) + "\n")
row = gate.score_no_claude_path(cfg, tmp_path)
assert row.status == gate.RED, (form, row.exceptions)
planted.unlink()
assert gate.score_no_claude_path(cfg, tmp_path).status == gate.GREEN
def test_the_row_limit_is_a_class_description_and_no_longer_an_enumeration() -> None:
"""Tredje runde med samme mønster: «NØYAKTIG TO veier» → «TRE veier», begge falsifisert av
den første målingen som ble gjort. Setningen skal si hva vakten ER og hva den ikke ser
aldri hvor mange veier som står igjen."""
limit = gate.NO_CLAUDE_LIMIT.casefold()
assert "to veier står igjen" not in limit
assert "tre veier står igjen" not in limit
assert "tekstmønstre" in limit or "tekstvakt" in limit
assert "uttømmende" in limit
assert "git-manifestet" in limit