`_EXTERNAL_TODAY` and `_WITH_A_DOOR_TODAY` are the arm's own count, not the gate's, so the seven new doors have to be written down here before the suite agrees they exist. The two assertions that pinned `kjørekonfig`/`utboks`/`prepass-artefakt`/`parse-feil` as door-LESS are inverted rather than deleted: they were the arm's discriminator, and a door that silently went away must still turn this red. The manifest count moves 514 -> 515 for the one new probe file. Also a probe for the shape guard: the writers take `Mapping`/`Sequence` and would serialize whatever they were handed, so a list where an object belongs would become a valid file with the wrong shape and the run that reads it later would be the one to fail. The door refuses it as a 3 (the call parsed, so 2 is the wrong code) and leaves no directory behind. Carries its rc-0 control. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2010 lines
91 KiB
Python
2010 lines
91 KiB
Python
"""B-gatens egne tester: hver rad KAN bli grønn og KAN bli rød — og kan ikke FAKES grønn.
|
|
|
|
Rammen er operatørbeslutningen 19.09.2026: i utvikling og test LEDER Claude Code, og
|
|
portfolio-optimiser er verktøykassen. po kaller ALDRI Claude. Gaten måler avstanden dit, rad for
|
|
rad, uten et eneste modellkall.
|
|
|
|
Tre ting gjør testene load-bearing i stedet for grønne-men-døde:
|
|
|
|
* **Nevneren telles uavhengig HER.** Rad 1 og rad 2 får ikke sine tall fra gatens egne funksjoner:
|
|
testen parser kilden selv (``ast``) eller importerer modulen og teller, og sammenligner. En gate
|
|
som hardkoder nevneren felles av arm ``M-1``, som kjører den mot en KONSTRUERT kilde der svaret
|
|
er et annet enn repoets.
|
|
* **Hver vakt har en kjent-positiv.** Et mønster som ikke treffer sin egen kjent-positive er ikke
|
|
en måling, og rad 3 nekter å telle det (``M-3``). Et fravær («0 treff») uttales med nevner.
|
|
* **Rad 6 kan ikke bli grønn av en fil gaten selv skriver** (``M-5``): armen kjører hele
|
|
``evaluate`` og krever at attesteringsfila fortsatt ikke finnes etterpå.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import ast
|
|
import base64
|
|
import hashlib
|
|
import json
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
import pytest
|
|
|
|
from portfolio_optimiser import workflow
|
|
from portfolio_optimiser.evals import b_gate as gate
|
|
from portfolio_optimiser.evals import v1_gate
|
|
|
|
_REPO = Path(__file__).resolve().parents[1]
|
|
_SRC = _REPO / "src" / "portfolio_optimiser"
|
|
_CONFIG = gate.load_config()
|
|
#: Klokka rad 6 leses mot, injisert: den ENE sjekken som ser på tiden skal ikke flytte seg.
|
|
_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
|
|
#: at en stille endring i kjørestien skal vise seg som en rød test og ikke som et nytt tall.
|
|
_STEPS_TODAY = 17
|
|
_EXTERNAL_TODAY = 15
|
|
_ROLES_TODAY = ("proposer", "checker")
|
|
_PATTERNS_TODAY = 15
|
|
|
|
|
|
def _write(path: Path, text: str) -> None:
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(text, encoding="utf-8")
|
|
|
|
|
|
def _probes(outcome: str = "missing") -> Any:
|
|
return lambda nodeids: {n: outcome for n in nodeids}
|
|
|
|
|
|
def _row(rows: list[v1_gate.Row], key: str) -> v1_gate.Row:
|
|
return next(r for r in rows if r.key == key)
|
|
|
|
|
|
# ---------------------------------------------------------------------------------------------
|
|
# Rad 1 — verktøykassen er komplett
|
|
# ---------------------------------------------------------------------------------------------
|
|
|
|
|
|
def _independently_counted_steps() -> tuple[int, int]:
|
|
"""(steg som oppløses i kilden, steg med et kallsted) — talt HER, av testens egen parser.
|
|
|
|
Kilden er ``src/portfolio_optimiser/``; ingen av gatens funksjoner røres."""
|
|
resolved = called = 0
|
|
for step in _CONFIG["steps"]:
|
|
tree = ast.parse((_SRC / step["module"]).read_text(encoding="utf-8"))
|
|
defs = {
|
|
n.name
|
|
for n in tree.body
|
|
if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef))
|
|
}
|
|
if step["symbol"] in defs:
|
|
resolved += 1
|
|
driver = ast.parse((_SRC / step["driver"]["module"]).read_text(encoding="utf-8"))
|
|
scopes = [
|
|
n
|
|
for n in ast.walk(driver)
|
|
if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef))
|
|
and n.name == step["driver"]["scope"]
|
|
]
|
|
names = {
|
|
c.func.id if isinstance(c.func, ast.Name) else c.func.attr
|
|
for s in scopes
|
|
for c in ast.walk(s)
|
|
if isinstance(c, ast.Call) and isinstance(c.func, (ast.Name, ast.Attribute))
|
|
}
|
|
if step["symbol"] in names:
|
|
called += 1
|
|
return resolved, called
|
|
|
|
|
|
def test_the_step_denominator_equals_an_independent_count_of_the_source() -> None:
|
|
resolved, called = _independently_counted_steps()
|
|
assert resolved == called == _STEPS_TODAY, "kjørestien har endret seg — oppdater nevneren"
|
|
row = gate.score_toolbox(_CONFIG["steps"], _SRC, _CONFIG["run_path"], {}, _REPO)
|
|
assert row.n == _STEPS_TODAY
|
|
|
|
|
|
#: Stegene som HAR en dør ut 20.09.2026, skrevet ut. Sju av dem er verktøykassens under-
|
|
#: kommandoer — fire fra steg 2 i byggerekkefølgen og de tre dommen fikk i steg 3; ``gate`` er
|
|
#: v1-gatens egen kommando, som alltid var en ekte dørprobe og ble avvist på en navne-
|
|
#: teknikalitet til klasse-rettingen samme dag.
|
|
_WITH_A_DOOR_TODAY = (
|
|
"coverage",
|
|
"debatt-verktøy",
|
|
"dom",
|
|
"forslagsvurderinger",
|
|
"gate",
|
|
"kjørekonfig",
|
|
"kontekst",
|
|
"kostnadsgrunnlag",
|
|
"pakke",
|
|
"parse-feil",
|
|
"prepass",
|
|
"prepass-artefakt",
|
|
"utboks",
|
|
"validering",
|
|
"verdict-id",
|
|
)
|
|
|
|
|
|
def test_row1_today_is_red_and_names_exactly_the_steps_that_have_a_door() -> None:
|
|
"""Var «3 av 17» til 19.09, så 0 av 17 da proben ble BUNDET til steget, 5 av 17 da verktøy-
|
|
kassens fire første dører sto, og 8 av 17 da dommens tre kom gjennom den samme døren.
|
|
Armen teller ikke bare k — den navngir HVILKE, så en dør som flytter seg uten å bli bygd
|
|
(eller en probe som slutter å drive sin dør) blir rød her og ikke bare et annet tall.
|
|
|
|
20.09: 15 av 17 da de sju utboks-skriverne fikk hver sin underkommando. De TO som står igjen
|
|
er de to som deler `round_builder`-døren uten at kommandolinja kan skille dem — gaten krever
|
|
at argv NAVNGIR steget når mer enn ett steg står bak samme dør, og den CLI-en har ingen
|
|
underkommando å navngi dem med."""
|
|
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)
|
|
assert (row.k, row.n, row.status) == (_EXTERNAL_TODAY, _STEPS_TODAY, gate.RED)
|
|
assert len(_WITH_A_DOOR_TODAY) == _EXTERNAL_TODAY
|
|
uten_doer = {x.split(":")[0] for x in row.exceptions}
|
|
alle = {str(s["id"]) for s in _CONFIG["steps"]}
|
|
assert sorted(alle - uten_doer) == sorted(_WITH_A_DOOR_TODAY), row.exceptions
|
|
assert "kjørekonfig" not in uten_doer and "utboks" not in uten_doer
|
|
assert "prepass-artefakt" not in uten_doer and "parse-feil" not in uten_doer
|
|
bound = {x.split(":")[0] for x in row.exceptions if "atferdsprobe" in x}
|
|
assert bound == {"rundebinding", "rapport"}, row.exceptions
|
|
|
|
|
|
def _fake_src(root: Path, *, chat_client: bool, symbol: str = "gjoer_noe") -> Path:
|
|
"""En konstruert kilde med ETT steg: en inngang ``main`` som når steget.
|
|
|
|
``chat_client=True`` legger en chatklient på veien dit — steget er da fortsatt kallbart, men
|
|
ikke uten en modell, og skal ikke telle."""
|
|
src = root / "fakesrc"
|
|
body = " return create_chat_client()\n" if chat_client else " return 1\n"
|
|
_write(src / "steg.py", f"def {symbol}():\n return 42\n")
|
|
_write(
|
|
src / "driver.py",
|
|
"from fakesrc.steg import " + symbol + "\n\n"
|
|
"def _hjelper():\n" + body + "\n\n"
|
|
"def kjor():\n"
|
|
f" _hjelper()\n return {symbol}()\n\n\n"
|
|
"def main(argv=None):\n return kjor()\n\n\n"
|
|
'if __name__ == "__main__":\n raise SystemExit(main())\n',
|
|
)
|
|
return src
|
|
|
|
|
|
#: En dør er registrert OG bevist med atferd. De konstruerte kildene under bærer begge deler.
|
|
#: Probefila SKRIVES (``_write_probe``) i stedet for bare å navngis: fra 19.09 leser gaten probens
|
|
#: EGEN kilde, og en nodeid som ikke finnes er ikke en atferd.
|
|
_FAKE_PROBE = "tests/test_konstruerte_prober.py::test_doeren_skriver_artefaktet"
|
|
_FAKE_PASSED = {_FAKE_PROBE: "passed"}
|
|
|
|
|
|
def _fake_run_path() -> dict[str, Any]:
|
|
"""Kjørestien i den konstruerte kilden — bærer pakkenavnet døren måles mot."""
|
|
return {"module": "driver.py", "scope": "kjor", "package": "fakesrc"}
|
|
|
|
|
|
def _write_probe(
|
|
root: Path,
|
|
*,
|
|
door: str = "fakesrc.driver",
|
|
needle: str = "gjoer_noe",
|
|
asserts: bool = True,
|
|
docstring: str = "",
|
|
nodeid: str = _FAKE_PROBE,
|
|
) -> str:
|
|
"""En probe som DRIVER døren: kaller kommandoen og asserter på det den gjorde.
|
|
|
|
Hvert av de fire trekkene gaten måler kan skrus av her, ett om gangen: hvilken dør den rører
|
|
(``door``), hvilket steg den navngir (``needle``), om den asserter i det hele tatt, og hva
|
|
den bare OMTALER uten å røre (``docstring``)."""
|
|
file_part, _, name = nodeid.partition("::")
|
|
said = f' """{docstring}"""\n' if docstring else ""
|
|
body = (
|
|
"import subprocess\n"
|
|
"import sys\n\n\n"
|
|
f"def {name}(tmp_path):\n"
|
|
+ said
|
|
+ f' proc = subprocess.run([sys.executable, "-m", "{door}", "--steg", "{needle}"])\n'
|
|
+ (" assert proc.returncode == 0\n" if asserts else " return proc\n")
|
|
)
|
|
_write(root / file_part, body)
|
|
return nodeid
|
|
|
|
|
|
def _one_step(symbol: str = "gjoer_noe") -> list[dict[str, Any]]:
|
|
return [
|
|
{
|
|
"id": "steg",
|
|
"label": "ett steg",
|
|
"module": "steg.py",
|
|
"symbol": symbol,
|
|
"driver": {"module": "driver.py", "scope": "kjor"},
|
|
"entry": {"kind": "module-main", "module": "driver.py", "scope": "main"},
|
|
"probe": [_FAKE_PROBE],
|
|
}
|
|
]
|
|
|
|
|
|
def test_a_step_reachable_from_a_cli_entry_without_a_chat_client_counts(tmp_path: Path) -> None:
|
|
src = _fake_src(tmp_path, chat_client=False)
|
|
_write_probe(tmp_path)
|
|
row = gate.score_toolbox(_one_step(), src, _fake_run_path(), _FAKE_PASSED, tmp_path)
|
|
assert (row.k, row.n, row.status) == (1, 1, gate.GREEN), row.exceptions
|
|
|
|
|
|
def test_m2_a_step_that_needs_a_chat_client_is_not_callable_from_outside(tmp_path: Path) -> None:
|
|
"""M-2: fjernes chatklient-sjekken, hopper k fra 0 til 1 og denne armen feller mutanten."""
|
|
src = _fake_src(tmp_path, chat_client=True)
|
|
_write_probe(tmp_path)
|
|
row = gate.score_toolbox(_one_step(), src, _fake_run_path(), _FAKE_PASSED, tmp_path)
|
|
assert (row.k, row.n, row.status) == (0, 1, gate.RED)
|
|
assert any("chatklient" in x for x in row.exceptions)
|
|
|
|
|
|
def test_m1_the_denominator_follows_the_source_and_is_not_the_config_length(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
"""M-1: en hardkodet nevner overlever repoet, men ikke en kilde med ett steg mindre."""
|
|
src = _fake_src(tmp_path, chat_client=False)
|
|
steps = _one_step() + [
|
|
{
|
|
"id": "borte",
|
|
"label": "et steg som ikke finnes i kilden",
|
|
"module": "steg.py",
|
|
"symbol": "finnes_ikke",
|
|
"driver": {"module": "driver.py", "scope": "kjor"},
|
|
"entry": {"kind": "module-main", "module": "driver.py", "scope": "main"},
|
|
}
|
|
]
|
|
_write_probe(tmp_path)
|
|
row = gate.score_toolbox(steps, src, _fake_run_path(), _FAKE_PASSED, tmp_path)
|
|
assert row.n == 1, "et steg uten symbol i kilden er ikke et steg"
|
|
assert any("borte" in x for x in row.exceptions)
|
|
|
|
|
|
def test_a_step_the_driver_no_longer_calls_leaves_the_denominator(tmp_path: Path) -> None:
|
|
src = _fake_src(tmp_path, chat_client=False)
|
|
_write(src / "steg.py", "def gjoer_noe():\n return 42\n\n\ndef ubrukt():\n return 0\n")
|
|
steps = _one_step() + [
|
|
{
|
|
"id": "ubrukt",
|
|
"label": "definert, men ikke i kjørestien",
|
|
"module": "steg.py",
|
|
"symbol": "ubrukt",
|
|
"driver": {"module": "driver.py", "scope": "kjor"},
|
|
"entry": {"kind": "module-main", "module": "driver.py", "scope": "main"},
|
|
}
|
|
]
|
|
_write_probe(tmp_path)
|
|
row = gate.score_toolbox(steps, src, _fake_run_path(), _FAKE_PASSED, tmp_path)
|
|
assert row.n == 1
|
|
|
|
|
|
def test_an_entry_without_a_main_is_no_entry(tmp_path: Path) -> None:
|
|
src = _fake_src(tmp_path, chat_client=False)
|
|
text = (src / "driver.py").read_text(encoding="utf-8").replace("def main(", "def _main(")
|
|
_write(src / "driver.py", text)
|
|
_write_probe(tmp_path)
|
|
row = gate.score_toolbox(_one_step(), src, _fake_run_path(), _FAKE_PASSED, tmp_path)
|
|
assert (row.k, row.n) == (0, 1)
|
|
assert any("main" in x for x in row.exceptions)
|
|
|
|
|
|
# ---------------------------------------------------------------------------------------------
|
|
# Rad 2 — det modellen leverte, kan leveres utenfra
|
|
# ---------------------------------------------------------------------------------------------
|
|
|
|
|
|
def test_the_role_denominator_is_read_from_workflow_and_matches_the_imported_module() -> None:
|
|
assert workflow._MAKER_CHECKER_ROLES == _ROLES_TODAY # uavhengig telling: modulen selv
|
|
assert gate.run_roles(_SRC, _CONFIG["roles"]["source"]) == _ROLES_TODAY
|
|
|
|
|
|
def test_the_role_denominator_follows_a_changed_source(tmp_path: Path) -> None:
|
|
src = tmp_path / "fakesrc"
|
|
_write(src / "workflow.py", '_MAKER_CHECKER_ROLES = ("a", "b", "c")\n')
|
|
assert gate.run_roles(src, _CONFIG["roles"]["source"]) == ("a", "b", "c")
|
|
|
|
|
|
def test_a_role_counts_only_when_every_named_probe_passes() -> None:
|
|
evidence = _CONFIG["roles"]["evidence"]
|
|
outcomes = {n: "passed" for ns in evidence.values() for n in ns}
|
|
row = gate.score_roles(_CONFIG["roles"], outcomes, _SRC)
|
|
assert (row.k, row.n, row.status) == (2, 2, gate.GREEN)
|
|
outcomes[evidence["checker"][0]] = "failed"
|
|
row = gate.score_roles(_CONFIG["roles"], outcomes, _SRC)
|
|
assert (row.k, row.n, row.status) == (1, 2, gate.RED)
|
|
|
|
|
|
def test_row2_today_is_red_because_the_named_probes_do_not_exist_yet() -> None:
|
|
row = gate.score_roles(_CONFIG["roles"], {}, _SRC)
|
|
assert (row.k, row.n, row.status) == (0, 2, gate.RED)
|
|
assert all("missing" in x for x in row.exceptions)
|
|
|
|
|
|
def test_m4_row2_demands_byte_identical_artefacts_not_merely_present_ones() -> None:
|
|
"""M-4: byttes kravet til «finnes», står kontrakten igjen uten den ene setningen som skiller
|
|
en ekte likhet fra to filer med samme navn."""
|
|
assert "byte-identisk" in gate.BYTE_IDENTICAL_RULE
|
|
row = gate.score_roles(_CONFIG["roles"], {}, _SRC)
|
|
assert any("byte-identisk" in a for a in row.attests)
|
|
|
|
|
|
# ---------------------------------------------------------------------------------------------
|
|
# Rad 3 — po har ingen vei til Claude
|
|
# ---------------------------------------------------------------------------------------------
|
|
|
|
|
|
def _patterns() -> list[dict[str, str]]:
|
|
return list(_CONFIG["no_claude_path"]["patterns"])
|
|
|
|
|
|
def test_every_pattern_hits_its_own_known_positive_and_misses_its_known_negative() -> None:
|
|
"""Fravær er et måleresultat: en vakt som ikke kan treffe, måler ikke null — den måler
|
|
ingenting. Nevner: alle mønstrene i kontrakten."""
|
|
assert len(_patterns()) == _PATTERNS_TODAY
|
|
for spec in _patterns():
|
|
rx = re.compile(gate.decode(spec["pattern_b64"]))
|
|
assert rx.search(gate.decode(spec["known_positive_b64"])), spec["id"]
|
|
assert not rx.search(gate.decode(spec["known_negative_b64"])), spec["id"]
|
|
|
|
|
|
def _clean_surface(tmp_path: Path) -> dict[str, Any]:
|
|
"""En ren, konstruert flate med sin egen sentinel — nevneren er aldri 0 her."""
|
|
cfg = json.loads(json.dumps(_CONFIG["no_claude_path"]))
|
|
cfg["manifest"] = {"sentinel": "src/rent.py", "skip_dirs": [".git", "__pycache__"]}
|
|
_write(tmp_path / "src" / "rent.py", "print('ingenting her')\n")
|
|
return cfg
|
|
|
|
|
|
def test_m3_the_denominator_is_the_number_of_patterns_that_can_measure(tmp_path: Path) -> None:
|
|
"""M-3: fjernes et mønster, faller nevneren — og et mønster som ikke treffer sin egen
|
|
kjent-positive telles ikke som en vakt."""
|
|
cfg = _clean_surface(tmp_path)
|
|
row = gate.score_no_claude_path(cfg, tmp_path)
|
|
assert (row.k, row.n, row.status) == (_PATTERNS_TODAY, _PATTERNS_TODAY, gate.GREEN)
|
|
|
|
cfg["patterns"][0]["pattern_b64"] = base64.b64encode(b"finnes-aldri-xyzzy").decode()
|
|
row = gate.score_no_claude_path(cfg, tmp_path)
|
|
assert (row.k, row.n, row.status) == (_PATTERNS_TODAY - 1, _PATTERNS_TODAY, gate.RED)
|
|
assert any("kjent-positiv" in x for x in row.exceptions)
|
|
|
|
cfg["patterns"] = cfg["patterns"][1:]
|
|
row = gate.score_no_claude_path(cfg, tmp_path)
|
|
assert row.n == _PATTERNS_TODAY - 1
|
|
|
|
|
|
def test_a_planted_path_to_claude_turns_the_row_red_for_that_pattern(tmp_path: Path) -> None:
|
|
cfg = _clean_surface(tmp_path)
|
|
assert gate.score_no_claude_path(cfg, tmp_path).status == gate.GREEN, "rc-0-kontrollen først"
|
|
for spec in cfg["patterns"]:
|
|
_write(
|
|
tmp_path / "src" / f"{spec['id']}.py", gate.decode(spec["known_positive_b64"]) + "\n"
|
|
)
|
|
row = gate.score_no_claude_path(cfg, tmp_path)
|
|
assert (row.k, row.status) == (0, gate.RED)
|
|
assert len(row.exceptions) == _PATTERNS_TODAY
|
|
|
|
|
|
def test_markdown_counts_only_inside_fenced_blocks(tmp_path: Path) -> None:
|
|
"""«docs med kjørbare kommandoer»: en pakkenavn-omtale i brødtekst er ikke en vei til Claude;
|
|
den samme linja i en kodeblokk er en kommando noen kan kjøre."""
|
|
cfg = _clean_surface(tmp_path)
|
|
spec = next(s for s in cfg["patterns"] if s["id"] == "maf-anthropic")
|
|
line = gate.decode(spec["known_positive_b64"])
|
|
_write(tmp_path / "docs" / "prosa.md", f"- vurdert og forkastet: `{line}` (aldri tatt inn)\n")
|
|
assert gate.score_no_claude_path(cfg, tmp_path).status == gate.GREEN
|
|
_write(tmp_path / "docs" / "kjorbar.md", f"```bash\n{line}\n```\n")
|
|
row = gate.score_no_claude_path(cfg, tmp_path)
|
|
assert row.status == gate.RED
|
|
assert any("kjorbar.md" in x for x in row.exceptions)
|
|
|
|
|
|
def test_row3_on_the_real_published_surface_is_green_today() -> None:
|
|
row = gate.score_no_claude_path(_CONFIG["no_claude_path"], _REPO)
|
|
assert (row.k, row.n, row.status) == (_PATTERNS_TODAY, _PATTERNS_TODAY, gate.GREEN), (
|
|
row.exceptions
|
|
)
|
|
|
|
|
|
def test_the_surface_names_its_denominator_in_files_not_in_prose() -> None:
|
|
files, _ = gate.published_files(_REPO, _manifest())
|
|
assert len(files) > 100, "en tom flate ville gitt 0 treff uten å måle noe"
|
|
assert any(f.name == "run.py" for f in files)
|
|
|
|
|
|
# ---------------------------------------------------------------------------------------------
|
|
# Rad 4 — ingen modellkall i verktøykasse-modus
|
|
# ---------------------------------------------------------------------------------------------
|
|
|
|
|
|
def test_row4_is_red_today_and_names_all_three_checks() -> None:
|
|
row = gate.score_no_model_calls(_CONFIG["no_model_calls"], {}, _SRC)
|
|
assert (row.k, row.n, row.status) == (0, 3, gate.RED)
|
|
assert len(row.exceptions) == 3
|
|
|
|
|
|
def test_row4_counts_only_checks_whose_probes_pass() -> None:
|
|
checks = _CONFIG["no_model_calls"]["checks"]
|
|
outcomes = {n: "passed" for c in checks.values() for n in c["probe"]}
|
|
row = gate.score_no_model_calls(_CONFIG["no_model_calls"], outcomes, _SRC)
|
|
assert row.status == gate.GREEN
|
|
outcomes[checks["fullfører"]["probe"][0]] = "skipped"
|
|
row = gate.score_no_model_calls(_CONFIG["no_model_calls"], outcomes, _SRC)
|
|
assert (row.k, row.status) == (2, gate.RED)
|
|
|
|
|
|
def test_row4_states_what_the_budget_guard_is_in_b_instead_of_pretending() -> None:
|
|
row = gate.score_no_model_calls(_CONFIG["no_model_calls"], {}, _SRC)
|
|
assert any("budsjett" in a.casefold() for a in row.attests)
|
|
assert "valgt" in gate.BUDGET_IN_B.casefold()
|
|
|
|
|
|
# ---------------------------------------------------------------------------------------------
|
|
# Rad 5 — Foundry-veien urørt
|
|
# ---------------------------------------------------------------------------------------------
|
|
|
|
|
|
def test_the_foundry_path_is_intact_in_the_source_today() -> None:
|
|
ok, missing = gate.foundry_intact(_CONFIG["foundry"], _SRC)
|
|
assert (ok, missing) == (True, ())
|
|
|
|
|
|
def test_the_foundry_check_fails_when_the_azure_profile_is_gone(tmp_path: Path) -> None:
|
|
src = tmp_path / "fakesrc"
|
|
_write(src / "backends.py", "class Profile:\n LOCAL = 'local'\n")
|
|
_write(src / "run.py", "def run_project(client_factory=None):\n return 1\n")
|
|
ok, missing = gate.foundry_intact(_CONFIG["foundry"], src)
|
|
assert ok is False
|
|
assert any("AZURE" in m for m in missing)
|
|
|
|
|
|
def test_row5_is_red_until_the_schema_comparison_probe_passes() -> None:
|
|
structural = len(_CONFIG["foundry"]["profile"]["members"]) + 2
|
|
row = gate.score_foundry(_CONFIG["foundry"], {}, _SRC)
|
|
assert (row.k, row.n, row.status) == (structural, structural + 1, gate.RED)
|
|
nodeid = _CONFIG["foundry"]["evidence"]["samme-skjema"][0]
|
|
row = gate.score_foundry(_CONFIG["foundry"], {nodeid: "passed"}, _SRC)
|
|
assert (row.k, row.n, row.status) == (structural + 1, structural + 1, gate.GREEN)
|
|
|
|
|
|
# ---------------------------------------------------------------------------------------------
|
|
# Rad 6 — kjøreboka finnes
|
|
# ---------------------------------------------------------------------------------------------
|
|
|
|
|
|
def test_row6_is_not_measured_while_the_runbook_is_missing(tmp_path: Path) -> None:
|
|
row = gate.score_runbook(_CONFIG["runbook"], tmp_path)
|
|
assert (row.k, row.n, row.status) == (0, 2, gate.NOT_MEASURED)
|
|
assert row.failing is True
|
|
|
|
|
|
def test_row6_is_still_not_measured_without_the_operators_attestation(tmp_path: Path) -> None:
|
|
_runbook(tmp_path)
|
|
row = gate.score_runbook(_CONFIG["runbook"], tmp_path)
|
|
assert (row.k, row.n, row.status) == (1, 2, gate.NOT_MEASURED)
|
|
|
|
|
|
def test_row6_goes_green_only_with_every_required_key(tmp_path: Path) -> None:
|
|
cfg = _CONFIG["runbook"]
|
|
digest = _runbook(tmp_path)
|
|
attest = tmp_path / cfg["attestation"]
|
|
_write(attest, f"kjørebok: {cfg['path']}\nsjekksum: {digest}\nkjørt: operatøren\n")
|
|
row = gate.score_runbook(cfg, tmp_path, now=_NOW)
|
|
assert row.status == gate.NOT_MEASURED
|
|
assert any("dato" in x for x in row.exceptions)
|
|
_attestation_lines(tmp_path, digest)
|
|
row = gate.score_runbook(cfg, tmp_path, now=_NOW)
|
|
assert (row.k, row.n, row.status) == (2, 2, gate.GREEN)
|
|
|
|
|
|
def test_a_duplicated_key_in_the_attestation_is_red(tmp_path: Path) -> None:
|
|
cfg = _CONFIG["runbook"]
|
|
digest = _runbook(tmp_path)
|
|
_attestation_lines(tmp_path, digest)
|
|
path = tmp_path / cfg["attestation"]
|
|
path.write_text(path.read_text(encoding="utf-8") + "kjørt: en annen\n", encoding="utf-8")
|
|
row = gate.score_runbook(cfg, tmp_path, now=_NOW)
|
|
assert row.status == gate.NOT_MEASURED
|
|
assert any("to ganger" in x for x in row.exceptions)
|
|
|
|
|
|
def test_m5_the_gate_never_writes_the_attestation_itself(tmp_path: Path) -> None:
|
|
"""M-5: kan gaten skrive fila den leser, er rad 6 en sløyfe som bekrefter seg selv."""
|
|
_write(tmp_path / _CONFIG["runbook"]["path"], "# kjørebok\n")
|
|
attest = tmp_path / _CONFIG["runbook"]["attestation"]
|
|
rows = gate.evaluate(config=_CONFIG, repo_root=tmp_path, src=_SRC, probe_runner=_probes())
|
|
assert not attest.exists(), "gaten skrev operatørens attestering — da beviser rad 6 ingenting"
|
|
assert _row(rows, "kjørebok").status == gate.NOT_MEASURED
|
|
assert any("aldri" in a for a in _row(rows, "kjørebok").attests)
|
|
|
|
|
|
# ---------------------------------------------------------------------------------------------
|
|
# Hele gaten
|
|
# ---------------------------------------------------------------------------------------------
|
|
|
|
|
|
def test_the_gate_is_red_today_and_every_row_is_red_for_a_measured_reason() -> None:
|
|
rows = gate.evaluate(config=_CONFIG, repo_root=_REPO, src=_SRC, probe_runner=_probes())
|
|
assert gate.exit_code(rows) == 1
|
|
assert [r.key for r in rows] == [
|
|
"verktøykasse",
|
|
"roller",
|
|
"ingen-claude-vei",
|
|
"ingen-modellkall",
|
|
"foundry",
|
|
"kjørebok",
|
|
]
|
|
for row in rows:
|
|
assert row.status in {gate.GREEN, gate.RED, gate.NOT_MEASURED}
|
|
if row.status != gate.GREEN:
|
|
assert row.k is not None and row.n is not None, f"{row.key} mangler k av N"
|
|
assert row.exceptions, f"{row.key} er ikke grønn uten å si hvorfor"
|
|
|
|
|
|
def test_the_probe_runner_separates_a_test_that_exists_from_one_that_does_not() -> None:
|
|
"""Kjent-positiv for selve probe-mekanismen: «missing» over hele lista ville vært et
|
|
måleresultat uten nevner. Nevneren er to nodeid-er — én som finnes i suiten og én som ikke
|
|
gjør det — og mekanismen må skille dem. Proben peker med vilje UT av denne fila: en nodeid
|
|
herfra ville fått barne-pytest til å kjøre denne testen igjen, i det uendelige."""
|
|
lever = "tests/test_ledger.py::test_totals_per_project_and_portfolio"
|
|
dod = "tests/test_ledger.py::test_finnes_ikke_xyzzy"
|
|
outcomes = v1_gate.run_probes([lever, dod], _REPO)
|
|
assert outcomes[lever] == "passed"
|
|
assert outcomes[dod] == "missing"
|
|
|
|
|
|
def test_render_names_every_row_the_budget_choice_and_the_runbook_rule() -> None:
|
|
rows = gate.evaluate(config=_CONFIG, repo_root=_REPO, src=_SRC, probe_runner=_probes())
|
|
text = gate.render(rows)
|
|
for row in rows:
|
|
assert row.title in text
|
|
assert gate.BUDGET_IN_B in text
|
|
assert gate.RUNBOOK_RULE in text
|
|
|
|
|
|
def test_the_command_runs_offline_and_exits_one() -> None:
|
|
proc = subprocess.run(
|
|
[sys.executable, "-m", "portfolio_optimiser.evals.b_gate", "--json"],
|
|
cwd=_REPO,
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
assert proc.returncode == 1, proc.stderr
|
|
payload = json.loads(proc.stdout)
|
|
assert payload["exit"] == 1
|
|
assert len(payload["rows"]) == 6
|
|
|
|
|
|
def test_a_bad_attestation_path_is_usage_error_not_a_silent_green(tmp_path: Path) -> None:
|
|
proc = subprocess.run(
|
|
[sys.executable, "-m", "portfolio_optimiser.evals.b_gate", "--attest", str(tmp_path / "x")],
|
|
cwd=_REPO,
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
assert proc.returncode == 2
|
|
|
|
|
|
# =============================================================================================
|
|
# REPARASJONEN 19.09.2026 (PM-sjekkpunktet på 207337c): nevnerne utledet av KILDEN, en dør som
|
|
# må være REGISTRERT og bevist med atferd, hele den publiserte flaten, og en attestering som kan
|
|
# felles. Hver arm under feller på en ASSERT om atferd, aldri ved innsamling.
|
|
# =============================================================================================
|
|
|
|
#: po-funksjoner ``run.py::run_project`` faktisk kaller — talt av armens EGEN parser under.
|
|
#: 41 = PMs 39 deterministiske + de to som krever en chatklient og holdes utenfor med grunn.
|
|
_RUN_PATH_CALLS_TODAY = 41
|
|
#: utboks-skrivere: definert i outbox.py / kalt i kjørestien. PMs tall, talt om igjen her.
|
|
_WRITERS_DEFINED = 10
|
|
_WRITERS_IN_RUN_PATH = 7
|
|
#: publiserte filer i git-manifestet (uttrekk og arbeidstre gir SAMME tall — det var hele poenget
|
|
#: med å slutte å telle filtreet: 435 i arbeidstreet var to gitignorerte .local.md-filer).
|
|
_PUBLISHED_TODAY = 515
|
|
_UNDECODABLE_TODAY = 1
|
|
|
|
|
|
def _manifest() -> dict[str, Any]:
|
|
manifest = _CONFIG["no_claude_path"].get("manifest")
|
|
assert manifest is not None, "kontrakten utleder ikke flaten av repo-manifestet"
|
|
return dict(manifest)
|
|
|
|
|
|
def _run_path_spec() -> dict[str, Any]:
|
|
spec = _CONFIG.get("run_path")
|
|
assert spec is not None, "kontrakten navngir ingen kjørested — nevneren har da ingen kilde"
|
|
return dict(spec)
|
|
|
|
|
|
def _independently_counted_run_path() -> dict[str, str]:
|
|
"""po-funksjonene ``run_project`` kaller, talt av TESTENS egen parser — ingen gate-kode rørt.
|
|
|
|
Regelen: et navn importert fra en ``portfolio_optimiser``-undermodul og kalt i scopet, eller
|
|
et attributt på en modul importert som ``from portfolio_optimiser import X``. Klasser og
|
|
unntak (stor forbokstav) er ikke steg; private hjelpere i run.py heller ikke."""
|
|
tree = ast.parse((_SRC / "run.py").read_text(encoding="utf-8"))
|
|
symbols: dict[str, str] = {}
|
|
aliases: set[str] = set()
|
|
for node in ast.walk(tree):
|
|
if not isinstance(node, ast.ImportFrom):
|
|
continue
|
|
module = node.module or ""
|
|
if module == "portfolio_optimiser":
|
|
aliases |= {a.asname or a.name for a in node.names}
|
|
elif module.startswith("portfolio_optimiser."):
|
|
for alias in node.names:
|
|
symbols[alias.asname or alias.name] = module.split(".")[-1] + ".py"
|
|
scope = next(
|
|
n
|
|
for n in ast.walk(tree)
|
|
if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef)) and n.name == "run_project"
|
|
)
|
|
found: dict[str, str] = {}
|
|
for call in ast.walk(scope):
|
|
if not isinstance(call, ast.Call):
|
|
continue
|
|
func = call.func
|
|
if isinstance(func, ast.Name) and func.id in symbols and func.id[:1].islower():
|
|
found[func.id] = symbols[func.id]
|
|
elif (
|
|
isinstance(func, ast.Attribute)
|
|
and isinstance(func.value, ast.Name)
|
|
and func.value.id in aliases
|
|
and func.attr[:1].islower()
|
|
):
|
|
found[func.attr] = f"{func.value.id}.py"
|
|
return found
|
|
|
|
|
|
def test_the_run_path_denominator_is_read_from_the_source_not_from_the_contract() -> None:
|
|
"""Rad 1s nevner kom fra gatens EGEN b_gate.json. Nå leses kjørestien av kilden, og testen
|
|
teller den om igjen selv."""
|
|
mine = _independently_counted_run_path()
|
|
assert len(mine) == _RUN_PATH_CALLS_TODAY, sorted(mine)
|
|
theirs = gate.run_path_calls(_SRC, _run_path_spec())
|
|
assert set(theirs) == set(mine), sorted(set(mine) ^ set(theirs))
|
|
|
|
|
|
def test_every_call_in_the_run_path_is_either_a_declared_step_or_held_out_with_a_reason() -> None:
|
|
"""Den ene egenskapen en kuratert liste ikke har: ingenting kan utelates STILLE."""
|
|
calls = set(_independently_counted_run_path())
|
|
declared = {str(s["symbol"]) for s in _CONFIG["steps"]}
|
|
held = {
|
|
str(h["symbol"]): str(h.get("reason", "")) for h in _run_path_spec().get("held_out", ())
|
|
}
|
|
assert not (calls - declared - set(held)), sorted(calls - declared - set(held))
|
|
assert all(held.values()), [s for s, why in held.items() if not why]
|
|
assert len(calls & declared) + len(held) == _RUN_PATH_CALLS_TODAY
|
|
|
|
|
|
def test_the_outbox_writers_the_run_path_uses_are_all_declared_steps() -> None:
|
|
"""PMs 7 av 10, talt om igjen her: artefaktfamilien er ikke tre filer, den er sju."""
|
|
outbox_tree = ast.parse((_SRC / "outbox.py").read_text(encoding="utf-8"))
|
|
writers = {
|
|
n.name
|
|
for n in outbox_tree.body
|
|
if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef)) and n.name.startswith("write_")
|
|
}
|
|
assert len(writers) == _WRITERS_DEFINED
|
|
in_path = writers & set(_independently_counted_run_path())
|
|
assert len(in_path) == _WRITERS_IN_RUN_PATH, sorted(in_path)
|
|
declared = {str(s["symbol"]) for s in _CONFIG["steps"]}
|
|
assert not (in_path - declared), sorted(in_path - declared)
|
|
|
|
|
|
def _mini_run_path(root: Path, *, extra_call: bool) -> tuple[Path, dict[str, Any]]:
|
|
"""En konstruert kilde med en kjøresti som kaller ett eller to po-steg."""
|
|
src = root / "minisrc"
|
|
_write(src / "steg.py", "def alfa():\n return 1\n\n\ndef beta():\n return 2\n")
|
|
body = " alfa()\n" + (" beta()\n" if extra_call else "")
|
|
_write(
|
|
src / "kjor.py",
|
|
f"from minisrc.steg import alfa, beta\n\n\nasync def run_project():\n{body} return 0\n",
|
|
)
|
|
return src, {"module": "kjor.py", "scope": "run_project", "package": "minisrc"}
|
|
|
|
|
|
def test_an_undeclared_call_in_the_run_path_raises_the_denominator_and_is_named(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
"""M-6: nevneren kan ikke krympe stille — et kall ingen erklærte teller som et steg UTEN dør."""
|
|
src, spec = _mini_run_path(tmp_path, extra_call=True)
|
|
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"},
|
|
}
|
|
]
|
|
row = gate.score_toolbox(steps, src, spec, {}, tmp_path)
|
|
assert row.n == 2, "beta kalles i kjørestien og er verken erklært eller holdt utenfor"
|
|
assert any("beta" in x for x in row.exceptions)
|
|
|
|
|
|
def test_a_held_out_call_leaves_the_denominator_but_is_named_with_its_reason(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
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"},
|
|
}
|
|
]
|
|
row = gate.score_toolbox(steps, src, spec, {}, tmp_path)
|
|
assert row.n == 1
|
|
assert any("beta" in d and "hjelper inne i alfa" in d for d in row.diagnostics), row.diagnostics
|
|
|
|
|
|
def test_removing_a_declared_step_does_not_shrink_the_denominator(tmp_path: Path) -> None:
|
|
"""A2 fra sjekkpunktet: «utboks» fjernet fra steps ga «3 av 12» uten en klage."""
|
|
src, spec = _mini_run_path(tmp_path, extra_call=True)
|
|
row = gate.score_toolbox([], src, spec, {}, tmp_path)
|
|
assert row.n == 2, "begge kallene står igjen i kjørestien selv om kontrakten glemte dem"
|
|
assert len(row.exceptions) == 2
|
|
|
|
|
|
# --- døren: registrert, av en art gaten kan verifisere, og bevist med atferd -------------------
|
|
|
|
|
|
def _fake_pyproject(root: Path, scripts: dict[str, str]) -> None:
|
|
lines = "\n".join(f'{name} = "{target}"' for name, target in scripts.items())
|
|
_write(
|
|
root / "pyproject.toml",
|
|
f'[project]\nname = "fake"\nversion = "0"\n\n[project.scripts]\n{lines}\n\n[tool.x]\ny = 1\n',
|
|
)
|
|
|
|
|
|
def test_an_entry_kind_the_gate_cannot_verify_is_never_a_door(tmp_path: Path) -> None:
|
|
src = _fake_src(tmp_path, chat_client=False)
|
|
ok, why = gate.registered_entry(
|
|
src, tmp_path, "fakesrc", {"kind": "magi", "module": "driver.py", "scope": "main"}
|
|
)
|
|
assert ok is False and "magi" in why
|
|
assert "magi" not in gate.ENTRY_KINDS
|
|
|
|
|
|
def test_a_console_script_is_a_door_only_when_the_manifest_registers_it(tmp_path: Path) -> None:
|
|
src = _fake_src(tmp_path, chat_client=False)
|
|
entry = {"kind": "console-script", "module": "driver.py", "scope": "main"}
|
|
_fake_pyproject(tmp_path, {})
|
|
ok, why = gate.registered_entry(src, tmp_path, "fakesrc", entry)
|
|
assert ok is False and "pyproject" in why
|
|
_fake_pyproject(tmp_path, {"fake-kommando": "fakesrc.driver:main"})
|
|
assert gate.registered_entry(src, tmp_path, "fakesrc", entry) == (True, "")
|
|
|
|
|
|
def test_a_module_main_is_a_door_only_with_the_dunder_guard(tmp_path: Path) -> None:
|
|
src = _fake_src(tmp_path, chat_client=False)
|
|
entry = {"kind": "module-main", "module": "driver.py", "scope": "main"}
|
|
assert gate.registered_entry(src, tmp_path, "fakesrc", entry) == (True, "")
|
|
stripped = (src / "driver.py").read_text(encoding="utf-8")
|
|
_write(src / "driver.py", stripped[: stripped.index("if __name__")])
|
|
ok, why = gate.registered_entry(src, tmp_path, "fakesrc", entry)
|
|
assert ok is False and "__main__" in why
|
|
|
|
|
|
def test_a_subcommand_is_a_door_only_when_the_parser_registers_that_name(tmp_path: Path) -> None:
|
|
src = _fake_src(tmp_path, chat_client=False)
|
|
entry = {
|
|
"kind": "subcommand",
|
|
"module": "driver.py",
|
|
"scope": "main",
|
|
"command": "naviger-pakke",
|
|
}
|
|
assert gate.registered_entry(src, tmp_path, "fakesrc", entry)[0] is False
|
|
_write(
|
|
src / "driver.py",
|
|
(src / "driver.py").read_text(encoding="utf-8")
|
|
+ '\n\ndef _parser(sub):\n sub.add_parser("naviger-pakke")\n',
|
|
)
|
|
assert gate.registered_entry(src, tmp_path, "fakesrc", entry) == (True, "")
|
|
|
|
|
|
def test_a_stub_main_is_not_a_door_without_a_registered_entry(tmp_path: Path) -> None:
|
|
"""A1 fra sjekkpunktet, felt: en modul med bare `def main(): return <symbol>()` tok rad 1 fra
|
|
3 til 4 av 13. Navn alene er aldri grønt."""
|
|
src = _fake_src(tmp_path, chat_client=False)
|
|
_fake_pyproject(tmp_path, {})
|
|
steps = _one_step()
|
|
steps[0]["entry"] = {"kind": "console-script", "module": "driver.py", "scope": "main"}
|
|
_write_probe(tmp_path)
|
|
row = gate.score_toolbox(steps, src, _fake_run_path(), _FAKE_PASSED, tmp_path)
|
|
assert (row.k, row.n) == (0, 1)
|
|
assert any("pyproject" in x for x in row.exceptions), row.exceptions
|
|
|
|
|
|
def test_a_registered_door_without_a_passing_behaviour_probe_does_not_count(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
"""«Kallet med en fixture gir det dokumenterte artefaktet» — et navn er ikke en atferd."""
|
|
src = _fake_src(tmp_path, chat_client=False)
|
|
_write_probe(tmp_path)
|
|
steps = _one_step()
|
|
row = gate.score_toolbox(steps, src, _fake_run_path(), {}, tmp_path)
|
|
assert (row.k, row.n) == (0, 1)
|
|
assert any("atferdsprobe" in x for x in row.exceptions), row.exceptions
|
|
row = gate.score_toolbox(steps, src, _fake_run_path(), {_FAKE_PROBE: "skipped"}, tmp_path)
|
|
assert row.k == 0, "hoppet over er ikke bestått"
|
|
row = gate.score_toolbox(steps, src, _fake_run_path(), _FAKE_PASSED, tmp_path)
|
|
assert (row.k, row.n, row.status) == (1, 1, gate.GREEN), row.exceptions
|
|
|
|
|
|
def test_a_step_without_any_probe_is_never_a_door(tmp_path: Path) -> None:
|
|
src = _fake_src(tmp_path, chat_client=False)
|
|
_write_probe(tmp_path)
|
|
steps = _one_step()
|
|
steps[0].pop("probe")
|
|
row = gate.score_toolbox(steps, src, _fake_run_path(), _FAKE_PASSED, tmp_path)
|
|
assert (row.k, row.n) == (0, 1)
|
|
assert any("ingen atferdsprobe" in x for x in row.exceptions), row.exceptions
|
|
|
|
|
|
def test_the_door_rule_claims_only_kinds_the_gate_can_verify() -> None:
|
|
"""A8: EXTERNAL_DOOR påsto at en MCP-registrert dør telles; «kind» ble aldri lest."""
|
|
assert gate.ENTRY_KINDS
|
|
assert "mcp" not in gate.EXTERNAL_DOOR.casefold().replace("mcp ville", "")
|
|
assert all(kind in gate.EXTERNAL_DOOR for kind in gate.ENTRY_KINDS)
|
|
assert '"kind"' in Path(gate.__file__).read_text(encoding="utf-8")
|
|
|
|
|
|
# --- rad 3: hele den publiserte flaten, og de indirekte veiene ---------------------------------
|
|
|
|
|
|
def test_the_published_surface_is_the_repo_manifest_and_not_a_handlist() -> None:
|
|
"""A10: main.py, examples/, spikes/ og contexts/ er publisert, men lå utenfor roots.
|
|
|
|
Armen holder i BEGGE verdener — arbeidstreet (git-manifestet) og et rent uttrekk (filtreet),
|
|
som er nøyaktig det publiserte. Den nøyaktige nevneren pinnes av armen under, som er den ene
|
|
som trenger repoets egen git-metadata."""
|
|
files, _source = gate.published_files(_REPO, _manifest())
|
|
names = {str(p.relative_to(_REPO)) for p in files}
|
|
assert "main.py" in names and "CLAUDE.md" in names and "llms.txt" in names
|
|
assert any(n.startswith("examples/") for n in names)
|
|
assert any(n.startswith("spikes/") for n in names)
|
|
assert any(n.startswith("contexts/") for n in names)
|
|
assert len(files) > 400, len(files)
|
|
|
|
|
|
def test_the_surface_count_matches_an_independent_count_of_the_manifest() -> None:
|
|
"""Den ENE armen som trenger repoets egen git-metadata. I et rent uttrekk («not a git
|
|
repository») finnes ikke manifestet å telle mot, og armen HOPPER OVER seg selv i stedet for å
|
|
felle: ellers vokser «røde i uttrekket» med hver ny git-avhengig arm, og tallet slutter å
|
|
være et funn. Nevneren pinnes HER, ikke i armen over."""
|
|
listing = subprocess.run(["git", "ls-files", "-z"], cwd=_REPO, capture_output=True, text=True)
|
|
if listing.returncode != 0:
|
|
pytest.skip("uttrekk uten git-metadata: manifestet finnes ikke å telle mot")
|
|
mine = {t for t in listing.stdout.split("\0") if t}
|
|
assert len(mine) == _PUBLISHED_TODAY, len(mine)
|
|
files, source = gate.published_files(_REPO, _manifest())
|
|
assert {str(p.relative_to(_REPO)) for p in files} == mine
|
|
assert "git-manifestet" == source
|
|
|
|
|
|
def test_a_surface_without_its_sentinel_is_not_measured_instead_of_green(tmp_path: Path) -> None:
|
|
"""A4: «6 av 6 GRØNN over 0 publiserte filer». Et fravær uten nevner er ikke et bevis."""
|
|
cfg = json.loads(json.dumps(_CONFIG["no_claude_path"]))
|
|
cfg.pop("roots", None)
|
|
cfg["manifest"] = {"sentinel": "src/portfolio_optimiser/run.py"}
|
|
row = gate.score_no_claude_path(cfg, tmp_path)
|
|
assert row.status == gate.NOT_MEASURED, row.reason
|
|
assert any("sentinel" in x for x in row.exceptions), row.exceptions
|
|
assert "0 publiserte filer" in row.reason
|
|
|
|
|
|
def test_the_row_names_which_manifest_it_read() -> None:
|
|
"""Nevneren står i radens EGEN tekst, sammen med hvor lista kom fra og hvor mange filer som
|
|
ikke lot seg avkode — «0 treff» uten nevner er ikke et bevis."""
|
|
row = gate.score_no_claude_path(_CONFIG["no_claude_path"], _REPO)
|
|
files, source = gate.published_files(_REPO, _manifest())
|
|
assert f"over {len(files)} publiserte filer" in row.reason
|
|
assert source in row.reason
|
|
assert f"{_UNDECODABLE_TODAY} ikke-UTF-8, lest som byte-tekst" in row.reason
|
|
|
|
|
|
def test_the_surface_falls_back_to_the_tree_when_there_is_no_git_metadata(tmp_path: Path) -> None:
|
|
"""Et rent uttrekk (`git archive | tar -x`) har ingen .git — og ER nøyaktig det publiserte."""
|
|
_write(tmp_path / "src" / "portfolio_optimiser" / "run.py", "x = 1\n")
|
|
_write(tmp_path / "main.py", "y = 2\n")
|
|
_write(tmp_path / "__pycache__" / "skrot.pyc", "nei\n")
|
|
files, source = gate.published_files(tmp_path, {"sentinel": "src/portfolio_optimiser/run.py"})
|
|
names = {str(p.relative_to(tmp_path)) for p in files}
|
|
assert names == {"src/portfolio_optimiser/run.py", "main.py"}
|
|
assert "uttrekk" in source
|
|
|
|
|
|
#: PMs fem realistiske kall-varianter, base64-kodet av samme grunn som mønstrene i kontrakten:
|
|
#: tests/ er selv en del av den publiserte flaten raden skanner, og en klartekst-variant her
|
|
#: ville registrert seg som sitt eget funn.
|
|
_INDIRECT_VARIANTS = (
|
|
("direkte", "c3VicHJvY2Vzcy5ydW4oWyJjbGF1ZGUiLCAiLXAiLCAieCJdKQ=="),
|
|
("absolutt-sti", "c3VicHJvY2Vzcy5ydW4oWyIvdXNyL2xvY2FsL2Jpbi9jbGF1ZGUiLCAiLXAiLCAieCJdKQ=="),
|
|
("liste-i-variabel", "Y21kID0gWyJjbGF1ZGUiLCAiLXAiLCAieCJdCnN1YnByb2Nlc3MucnVuKGNtZCk="),
|
|
(
|
|
"konstant",
|
|
"Q0xBVURFX0JJTiA9ICJjbGF1ZGUiCnN1YnByb2Nlc3MucnVuKFtDTEFVREVfQklOLCAiLXAiLCAieCJdKQ==",
|
|
),
|
|
("shell-streng", "c3VicHJvY2Vzcy5ydW4oc2hsZXguc3BsaXQoImNsYXVkZSAtcCB4Iikp"),
|
|
)
|
|
|
|
|
|
def test_every_indirect_way_of_starting_claude_is_caught(tmp_path: Path) -> None:
|
|
"""A9: 4 av 5 realistiske varianter slapp forbi. Hver plantes som kjent-positiv, én om
|
|
gangen, og hver skal felle raden."""
|
|
cfg = json.loads(json.dumps(_CONFIG["no_claude_path"]))
|
|
cfg.pop("roots", None)
|
|
cfg["manifest"] = {"sentinel": "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"
|
|
for name, snippet in _INDIRECT_VARIANTS:
|
|
planted = tmp_path / "src" / f"{name}.py"
|
|
_write(planted, gate.decode(snippet) + "\n")
|
|
row = gate.score_no_claude_path(cfg, tmp_path)
|
|
assert row.status == gate.RED, name
|
|
assert any(name in x for x in row.exceptions), (name, row.exceptions)
|
|
planted.unlink()
|
|
assert gate.score_no_claude_path(cfg, tmp_path).status == gate.GREEN
|
|
|
|
|
|
def test_the_row_states_the_limit_its_patterns_cannot_reach() -> None:
|
|
row = gate.score_no_claude_path(_CONFIG["no_claude_path"], _REPO)
|
|
assert any("satt sammen" in a or "miljøvariabel" in a for a in row.attests), row.attests
|
|
|
|
|
|
# --- rad 4, 5 og 6: hver nevner med en navngitt kilde -----------------------------------------
|
|
|
|
|
|
def test_row4_counts_only_checks_whose_named_source_resolves(tmp_path: Path) -> None:
|
|
"""M=3 var en konstant i gatens egen konfig. Nå er hver sjekk bundet til et symbol i kilden."""
|
|
cfg = json.loads(json.dumps(_CONFIG["no_model_calls"]))
|
|
row = gate.score_no_model_calls(cfg, {}, _SRC)
|
|
assert (row.k, row.n, row.status) == (0, 3, gate.RED)
|
|
for name, check in cfg["checks"].items():
|
|
assert isinstance(check, dict) and "source" in check, name
|
|
tree = ast.parse((_SRC / check["source"]["module"]).read_text(encoding="utf-8"))
|
|
names = {
|
|
n.name for n in tree.body if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef))
|
|
} | {
|
|
t.id
|
|
for n in tree.body
|
|
if isinstance(n, ast.Assign)
|
|
for t in n.targets
|
|
if isinstance(t, ast.Name)
|
|
}
|
|
assert check["source"]["symbol"] in names, name
|
|
src = tmp_path / "tom"
|
|
src.mkdir()
|
|
row = gate.score_no_model_calls(cfg, {}, src)
|
|
assert row.n == 0, "en sjekk uten kilde i koden teller ikke med i nevneren"
|
|
assert len(row.exceptions) == 3
|
|
|
|
|
|
def test_row5_counts_each_structural_requirement_on_its_own(tmp_path: Path) -> None:
|
|
"""M=2 blandet profil, fabrikk og injeksjonssøm til ÉN enhet. Nå telles hvert krav for seg,
|
|
og hvert av dem er et symbol i kilden."""
|
|
row = gate.score_foundry(_CONFIG["foundry"], {}, _SRC)
|
|
members = len(_CONFIG["foundry"]["profile"]["members"])
|
|
evidence = len(_CONFIG["foundry"]["evidence"])
|
|
assert row.n == members + 2 + evidence
|
|
assert (row.k, row.status) == (members + 2, gate.RED)
|
|
nodeid = _CONFIG["foundry"]["evidence"]["samme-skjema"][0]
|
|
row = gate.score_foundry(_CONFIG["foundry"], {nodeid: "passed"}, _SRC)
|
|
assert row.status == gate.GREEN
|
|
|
|
|
|
def test_row5_denominator_follows_the_named_members(tmp_path: Path) -> None:
|
|
cfg = json.loads(json.dumps(_CONFIG["foundry"]))
|
|
cfg["profile"]["members"].append("FINNES_IKKE")
|
|
row = gate.score_foundry(cfg, {}, _SRC)
|
|
assert row.n == len(cfg["profile"]["members"]) + 2 + len(cfg["evidence"])
|
|
assert any("FINNES_IKKE" in x for x in row.exceptions)
|
|
|
|
|
|
def test_row6_denominator_comes_from_the_artefacts_the_contract_names(tmp_path: Path) -> None:
|
|
artefacts = _CONFIG["runbook"].get("artefacts")
|
|
assert artefacts is not None, "M=2 var en konstant i koden, ikke noe kontrakten navngir"
|
|
row = gate.score_runbook(_CONFIG["runbook"], tmp_path)
|
|
assert row.n == len(artefacts)
|
|
|
|
|
|
# --- rad 6: attesteringen kan felles ----------------------------------------------------------
|
|
|
|
|
|
#: Én seksjons kropp i rc-0-kontrollen. Lengden er ikke pynt: kontrakten krever at det STÅR noe
|
|
#: under hver overskrift, og en fixtur med to ord ville gjort kontrollen rød sammen med juksen.
|
|
_SECTION_BODY = (
|
|
"steg 1: naviger pakken med toolbox-doeren og les filene den rapporterer.\n"
|
|
"steg 2: hent kostnadsgrunnlaget fra det prisede skjemaet i samme base.\n"
|
|
)
|
|
|
|
|
|
def _runbook(root: Path) -> str:
|
|
"""En kjørebok som bærer seksjonene kontrakten navngir, med tekst under hver — ellers er «x»
|
|
en kjørebok (N3), og de fem overskriftene alene er det også (resten av N3, målt 19.09)."""
|
|
sections = "\n".join(f"## {s}\n\n{_SECTION_BODY}" for s in _CONFIG["runbook"]["sections"])
|
|
text = f"# kjørebok\n\n{sections}"
|
|
_write(root / _CONFIG["runbook"]["path"], text)
|
|
return hashlib.sha256(text.encode("utf-8")).hexdigest()
|
|
|
|
|
|
def _attestation_lines(root: Path, digest: str, **over: str) -> None:
|
|
fields = {
|
|
"kjørebok": _CONFIG["runbook"]["path"],
|
|
"sjekksum": digest,
|
|
"kjørt": "operatøren",
|
|
"dato": "2026-09-19",
|
|
}
|
|
fields.update(over)
|
|
_write(
|
|
root / _CONFIG["runbook"]["attestation"],
|
|
"".join(f"{k}: {v}\n" for k, v in fields.items()),
|
|
)
|
|
|
|
|
|
def test_a_valid_attestation_is_the_only_thing_that_turns_row6_green(tmp_path: Path) -> None:
|
|
"""rc-0-kontrollen FØRST: en gate som nekter alt er like ubrukelig som en som nekter noe."""
|
|
digest = _runbook(tmp_path)
|
|
_attestation_lines(tmp_path, digest)
|
|
row = gate.score_runbook(_CONFIG["runbook"], tmp_path, now=_NOW)
|
|
assert (row.k, row.n, row.status) == (2, 2, gate.GREEN), row.exceptions
|
|
|
|
|
|
def test_an_attestation_that_names_another_runbook_is_refused(tmp_path: Path) -> None:
|
|
"""A7: «kjørebok: x» ga 2 av 2 GRØNN."""
|
|
digest = _runbook(tmp_path)
|
|
_attestation_lines(tmp_path, digest, **{"kjørebok": "x"})
|
|
row = gate.score_runbook(_CONFIG["runbook"], tmp_path, now=_NOW)
|
|
assert row.status == gate.NOT_MEASURED
|
|
assert any("kjørebok" in x for x in row.exceptions)
|
|
|
|
|
|
def test_an_attestation_whose_checksum_does_not_match_the_runbook_is_refused(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
"""En attestering av en ANNEN versjon av kjøreboka er ikke en attestering av denne."""
|
|
_runbook(tmp_path)
|
|
_attestation_lines(tmp_path, "0" * 64)
|
|
row = gate.score_runbook(_CONFIG["runbook"], tmp_path, now=_NOW)
|
|
assert row.status == gate.NOT_MEASURED
|
|
assert any("sjekksum" in x for x in row.exceptions)
|
|
|
|
|
|
def test_an_attestation_dated_in_the_future_is_refused(tmp_path: Path) -> None:
|
|
"""A7: framtidsdato 3026-01-01 ga GRØNN. Samme regel som v1-gaten, gjenbrukt."""
|
|
digest = _runbook(tmp_path)
|
|
_attestation_lines(tmp_path, digest, dato="3026-01-01")
|
|
row = gate.score_runbook(_CONFIG["runbook"], tmp_path, now=_NOW)
|
|
assert row.status == gate.NOT_MEASURED
|
|
assert any("framtiden" in x for x in row.exceptions)
|
|
|
|
|
|
def test_an_attestation_without_a_real_date_is_refused(tmp_path: Path) -> None:
|
|
digest = _runbook(tmp_path)
|
|
_attestation_lines(tmp_path, digest, dato="x")
|
|
row = gate.score_runbook(_CONFIG["runbook"], tmp_path, now=_NOW)
|
|
assert row.status == gate.NOT_MEASURED
|
|
assert any("ISO" in x or "dato" in x for x in row.exceptions)
|
|
|
|
|
|
def test_an_attestation_with_a_bom_is_still_read(tmp_path: Path) -> None:
|
|
"""v1-gaten TÅLER BOM — en editor som skriver den er ikke operatørens feil."""
|
|
digest = _runbook(tmp_path)
|
|
_attestation_lines(tmp_path, digest)
|
|
path = tmp_path / _CONFIG["runbook"]["attestation"]
|
|
path.write_bytes(b"\xef\xbb\xbf" + path.read_bytes())
|
|
assert gate.score_runbook(_CONFIG["runbook"], tmp_path, now=_NOW).status == gate.GREEN
|
|
|
|
|
|
# =============================================================================================
|
|
# REPARASJON 2, 19.09.2026 (PM-sjekkpunktet på c1bdb37): en probe som er bundet til SITT steg,
|
|
# en grenseerklæring som er sann, og en grunn som ikke kan være tom. Hver arm under feller på en
|
|
# ASSERT om atferd, og hver nekt-arm bærer sin egen rc-0-kontroll FØRST: en regel som nekter alt
|
|
# er like ubrukelig som en som nekter ingenting.
|
|
# =============================================================================================
|
|
|
|
#: Den konstruerte doblen brukes i to steg-varianter; symbolene står her fordi armene teller dem.
|
|
_TWO_STEP_SYMBOLS = ("gjoer_noe", "gjoer_noe_annet")
|
|
|
|
|
|
def _two_step_src(root: Path) -> Path:
|
|
"""En konstruert kilde med TO steg bak samme dør — der en delt probe blir et spørsmål."""
|
|
src = root / "fakesrc"
|
|
_write(
|
|
src / "steg.py",
|
|
"def gjoer_noe():\n return 42\n\n\ndef gjoer_noe_annet():\n return 43\n",
|
|
)
|
|
_write(
|
|
src / "driver.py",
|
|
"from fakesrc.steg import gjoer_noe, gjoer_noe_annet\n\n\n"
|
|
"def kjor():\n gjoer_noe()\n return gjoer_noe_annet()\n\n\n"
|
|
"def main(argv=None):\n return kjor()\n\n\n"
|
|
'if __name__ == "__main__":\n raise SystemExit(main())\n',
|
|
)
|
|
return src
|
|
|
|
|
|
def _two_steps(probe_a: str, probe_b: str) -> list[dict[str, Any]]:
|
|
entry = {"kind": "module-main", "module": "driver.py", "scope": "main"}
|
|
return [
|
|
{
|
|
"id": "steg-a",
|
|
"label": "steg a",
|
|
"module": "steg.py",
|
|
"symbol": "gjoer_noe",
|
|
"driver": {"module": "driver.py", "scope": "kjor"},
|
|
"entry": dict(entry),
|
|
"probe": [probe_a],
|
|
},
|
|
{
|
|
"id": "steg-b",
|
|
"label": "steg b",
|
|
"module": "steg.py",
|
|
"symbol": "gjoer_noe_annet",
|
|
"driver": {"module": "driver.py", "scope": "kjor"},
|
|
"entry": dict(entry),
|
|
"probe": [probe_b],
|
|
},
|
|
]
|
|
|
|
|
|
def test_a_probe_that_never_touches_the_door_is_not_a_behaviour_probe(tmp_path: Path) -> None:
|
|
"""N2 fra sjekkpunktet: en registrert stub-dør + en probe pekt på en URELATERT grønn test tok
|
|
rad 1 fra 3 til 4 av 17. Det som skilte dem var ingenting — gaten slo bare opp om nodeid-en
|
|
var ``passed``. Nå leses probens EGEN kilde, og «rører døren» er målt, ikke erklært."""
|
|
src = _fake_src(tmp_path, chat_client=False)
|
|
_write_probe(tmp_path)
|
|
row = gate.score_toolbox(_one_step(), src, _fake_run_path(), _FAKE_PASSED, tmp_path)
|
|
assert (row.k, row.n, row.status) == (1, 1, gate.GREEN), row.exceptions
|
|
|
|
_write_probe(tmp_path, door="fakesrc.enhelt_annen_modul")
|
|
row = gate.score_toolbox(_one_step(), src, _fake_run_path(), _FAKE_PASSED, tmp_path)
|
|
assert (row.k, row.n) == (0, 1)
|
|
assert any("rører ikke døren" in x for x in row.exceptions), row.exceptions
|
|
|
|
|
|
def test_a_probe_that_drives_the_door_without_naming_the_step_proves_the_door_only(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
"""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 må navngi steget den beviser.
|
|
|
|
Nevneren er TO med vilje (endret 20.09): navngivningen er en diskriminator, og et steg som
|
|
er alene bak sin dør har ingenting å skilles fra — se
|
|
``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
|
|
|
|
|
|
def test_a_probe_that_only_mentions_the_door_or_the_step_proves_neither(tmp_path: Path) -> None:
|
|
"""FUNN 19.09, mutant M12 overlevde FØRSTE forsøk på å felle den: leses HELE probens kropp,
|
|
holder det å skrive dørens navn som en streng som står alene. Da er «rører døren» en omtale
|
|
igjen, ikke en atferd — nøyaktig feilen ett hakk lenger inne.
|
|
|
|
Nålen er dørens navn NØYAKTIG, ikke som del av en setning: gaten sammenligner hele strenger,
|
|
så en docstring som OMTALER modulen i prosa faller uansett. Den billigste måten å jukse på er
|
|
derfor å skrive navnet bart, og det er den formen armen planter."""
|
|
src = _fake_src(tmp_path, chat_client=False)
|
|
_write_probe(tmp_path, docstring="en ekte probe: den kjører døren og sjekker exit-koden")
|
|
row = gate.score_toolbox(_one_step(), src, _fake_run_path(), _FAKE_PASSED, tmp_path)
|
|
assert (row.k, row.n) == (1, 1), row.exceptions
|
|
|
|
_write_probe(tmp_path, door="fakesrc.enhelt_annen_modul", docstring="fakesrc.driver")
|
|
row = gate.score_toolbox(_one_step(), src, _fake_run_path(), _FAKE_PASSED, tmp_path)
|
|
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
|
|
|
|
to_steg = _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="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
|
|
|
|
|
|
def test_a_probe_that_asserts_nothing_is_not_a_behaviour_probe(tmp_path: Path) -> None:
|
|
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, asserts=False)
|
|
row = gate.score_toolbox(_one_step(), src, _fake_run_path(), _FAKE_PASSED, tmp_path)
|
|
assert (row.k, row.n) == (0, 1)
|
|
assert any("asserter" in x for x in row.exceptions), row.exceptions
|
|
|
|
|
|
def test_a_probe_whose_test_does_not_exist_is_never_a_door(tmp_path: Path) -> None:
|
|
"""«passed» for en nodeid som ikke finnes er et måleresultat uten kilde."""
|
|
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
|
|
|
|
(tmp_path / "tests" / "test_konstruerte_prober.py").unlink()
|
|
row = gate.score_toolbox(_one_step(), src, _fake_run_path(), _FAKE_PASSED, tmp_path)
|
|
assert (row.k, row.n) == (0, 1)
|
|
assert any("finnes ikke" in x for x in row.exceptions), row.exceptions
|
|
|
|
_write_probe(tmp_path, nodeid="tests/test_konstruerte_prober.py::test_et_annet_navn")
|
|
row = gate.score_toolbox(_one_step(), src, _fake_run_path(), _FAKE_PASSED, tmp_path)
|
|
assert (row.k, row.n) == (0, 1), row.exceptions
|
|
|
|
|
|
def test_one_probe_cannot_prove_two_steps(tmp_path: Path) -> None:
|
|
"""N2b sin kjerne: ÉN test brukt som atferdsprobe for alle stegene samtidig. En probe som er
|
|
delt beviser høyst ett av dem, og gaten vet ikke hvilket — altså ingen."""
|
|
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"}
|
|
row = gate.score_toolbox(_two_steps(a, b), src, _fake_run_path(), outcomes, tmp_path)
|
|
assert (row.k, row.n, row.status) == (2, 2, gate.GREEN), row.exceptions
|
|
|
|
shared = _write_probe(
|
|
tmp_path,
|
|
needle="gjoer_noe",
|
|
nodeid="tests/test_delt.py::test_delt",
|
|
)
|
|
row = gate.score_toolbox(
|
|
_two_steps(shared, shared), src, _fake_run_path(), {shared: "passed"}, tmp_path
|
|
)
|
|
assert row.k == 0, row.exceptions
|
|
assert all("delt" in x for x in row.exceptions), row.exceptions
|
|
|
|
|
|
def test_the_same_unrelated_green_test_cannot_turn_the_real_row1_into_a_door(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
"""N2b mot KONTRAKTEN som står: hver av de 17 stegenes probe byttet ut med én urelatert
|
|
bestått test. Målt 19.09 ga det «17 av 17 GRØNN» med fjorten stubber; her er nevneren
|
|
repoets egen kontrakt, og ingen av stegene skal telle."""
|
|
steps = json.loads(json.dumps(_CONFIG["steps"]))
|
|
unrelated = "tests/test_ledger.py::test_totals_per_project_and_portfolio"
|
|
for step in steps:
|
|
step["probe"] = [unrelated]
|
|
row = gate.score_toolbox(steps, _SRC, _CONFIG["run_path"], {unrelated: "passed"}, _REPO)
|
|
assert row.n == _STEPS_TODAY
|
|
assert row.k == 0, row.exceptions
|
|
|
|
|
|
def test_the_door_rule_says_what_it_measures_about_the_probe() -> None:
|
|
"""A8-klassen igjen: `EXTERNAL_DOOR` lovet at proben «kaller døren og leser artefaktet den
|
|
skriver», og det fantes ingen kode. Regelen skal si nøyaktig det gaten MÅLER."""
|
|
rule = gate.EXTERNAL_DOOR.casefold()
|
|
assert "probens egen kilde" in rule
|
|
assert "delt" in rule
|
|
|
|
|
|
# --- rad 1: en grunn kan ikke være tom, og de kuraterte stegene navngis ------------------------
|
|
|
|
|
|
def test_a_held_out_call_without_a_reason_stays_in_the_denominator(tmp_path: Path) -> None:
|
|
"""N4/N5: et steg flyttet fra ``steps`` til ``held_out`` tok nevneren 17 → 16 MED ELLER UTEN
|
|
grunn. Unntakslista er den nye kurateringsflaten; en tom grunn er ingen grunn."""
|
|
src, spec = _mini_run_path(tmp_path, extra_call=True)
|
|
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"},
|
|
}
|
|
]
|
|
spec["held_out"] = [{"symbol": "beta", "reason": "hjelper inne i alfa, ikke et eget steg"}]
|
|
assert gate.score_toolbox(steps, src, spec, {}, tmp_path).n == 1, "kontroll: en ekte grunn"
|
|
|
|
spec["held_out"] = [{"symbol": "beta", "reason": " "}]
|
|
row = gate.score_toolbox(steps, src, spec, {}, tmp_path)
|
|
assert row.n == 2, "en tom grunn skal ikke kunne krympe nevneren"
|
|
assert any("beta" in x for x in row.exceptions), row.exceptions
|
|
|
|
|
|
def test_the_row_summary_counts_reasons_and_not_entries(tmp_path: Path) -> None:
|
|
"""Radens sammendrag sa «29 kall holdt utenfor med grunn» også når grunnen var tom."""
|
|
src, spec = _mini_run_path(tmp_path, extra_call=True)
|
|
spec["held_out"] = [{"symbol": "beta", "reason": ""}]
|
|
row = gate.score_toolbox([], src, spec, {}, tmp_path)
|
|
assert "0 kall holdt utenfor med grunn" in row.reason, row.reason
|
|
|
|
|
|
def test_every_held_out_entry_in_the_contract_carries_a_non_empty_reason() -> None:
|
|
held = list(_CONFIG["run_path"]["held_out"])
|
|
assert held
|
|
assert all(str(h.get("reason", "")).strip() for h in held), [
|
|
h["symbol"] for h in held if not str(h.get("reason", "")).strip()
|
|
]
|
|
|
|
|
|
def test_the_steps_outside_the_run_path_are_named_as_having_no_derived_source() -> None:
|
|
"""N7: et erklært steg UTENFOR kjørestien kan slettes stille — det har ingen kilde å lukke
|
|
mot. Fire av sytten er slike. De skal stå i radens egen tekst, ikke telles stille."""
|
|
row = gate.score_toolbox(_CONFIG["steps"], _SRC, _CONFIG["run_path"], {}, _REPO)
|
|
spec = _CONFIG["run_path"]
|
|
outside = [
|
|
str(s["id"])
|
|
for s in _CONFIG["steps"]
|
|
if (s["driver"]["module"], s["driver"]["scope"]) != (spec["module"], spec["scope"])
|
|
]
|
|
assert outside, "kontrakten har ingen steg utenfor kjørestien — da er denne armen foreldet"
|
|
for step_id in outside:
|
|
assert any(step_id in d and "uten utledet kilde" in d for d in row.diagnostics), (
|
|
step_id,
|
|
row.diagnostics,
|
|
)
|
|
|
|
|
|
def test_the_row_states_that_a_call_moved_into_a_helper_leaves_its_denominator() -> None:
|
|
"""N8: et po-kall flyttet én etasje ned, fra ``run_project`` til en hjelper den kaller, tar
|
|
nevneren 41 → 40. Gaten NAVNGIR at kallet er borte, men feller ikke — og grensen er radens
|
|
egen å uttale."""
|
|
row = gate.score_toolbox(_CONFIG["steps"], _SRC, _CONFIG["run_path"], {}, _REPO)
|
|
assert any("hjelper" in a for a in row.attests), row.attests
|
|
|
|
|
|
# --- rad 3: veiene som fortsatt sto åpne, og grenseerklæringen som var usann -------------------
|
|
|
|
#: PMs seks MÅLTE omgåelser av aldri-Claude-vakten, base64 av samme grunn som mønstrene: tests/
|
|
#: er selv en del av flaten raden skanner, og en klartekst-variant her ville vært sitt eget funn.
|
|
_MEASURED_ESCAPES = (
|
|
("sdk-anthropic", "aW1wb3J0IGFudGhyb3BpYw=="),
|
|
("sdk-anthropic-from", "ZnJvbSBhbnRocm9waWMgaW1wb3J0IEFudGhyb3BpYw=="),
|
|
("node-runner", "bnB4IEBhbnRocm9waWMtYWkvY2xhdWRlLWNvZGUgLXAgImFuYWx5c2VyIg=="),
|
|
(
|
|
"node-runner-argv",
|
|
"c3VicHJvY2Vzcy5ydW4oWyJucHgiLCAiQGFudGhyb3BpYy1haS9jbGF1ZGUtY29kZSIsICItcCIsICJ4Il0p",
|
|
),
|
|
("uv-runner", "dXZ4IGNsYXVkZS1jb2RlIC1wICJ4Ig=="),
|
|
("dynamisk-import", "X19pbXBvcnRfXygiY2xhdWRlX2FnZW50X3NkayIp"),
|
|
)
|
|
|
|
|
|
def test_every_way_the_checkpoint_measured_as_open_and_cheap_to_close_is_caught(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
"""De seks formene PM plantet og målte som USETTE 19.09: den offisielle Python-SDK-en (to
|
|
skriveformer), node-kjøreren (to former), uv-kjøreren og et dynamisk import-kall. Hver
|
|
plantes én om gangen, med rc-0-kontroll først og etter.
|
|
|
|
Formene STÅR IKKE skrevet her, bare base64-kodet — denne fila er selv en del av flaten raden
|
|
skanner, og en klartekst-variant ville vært sitt eget funn. Målt: den var det, i første
|
|
utkast av denne docstringen, og vakten fant den."""
|
|
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"
|
|
for name, snippet in _MEASURED_ESCAPES:
|
|
planted = tmp_path / "src" / f"{name}.py"
|
|
_write(planted, gate.decode(snippet) + "\n")
|
|
row = gate.score_no_claude_path(cfg, tmp_path)
|
|
assert row.status == gate.RED, name
|
|
assert any(name in x for x in row.exceptions), (name, row.exceptions)
|
|
planted.unlink()
|
|
assert gate.score_no_claude_path(cfg, tmp_path).status == gate.GREEN
|
|
|
|
|
|
def test_the_row_states_the_ways_that_measurably_remain_and_no_longer_claims_two() -> None:
|
|
"""Radens egen grenseerklæring oppga NØYAKTIG TO veier igjen; målt sto minst SEKS. De fire
|
|
billige er lukket over; de tre som står skal stå NAVNGITT — og «to veier» skal være borte."""
|
|
limit = gate.NO_CLAUDE_LIMIT.casefold()
|
|
assert "to veier står igjen" not in limit
|
|
assert "base64" in limit
|
|
assert "miljøvariabel" in limit
|
|
assert "satt sammen" in limit
|
|
|
|
|
|
def test_the_row_says_its_ratio_is_not_a_coverage_measure() -> None:
|
|
"""N11: k av n kan polstres med en vakt som bare treffer sin egen kjent-positive. Tallet er
|
|
et mål på at hver vakt MÅLER, aldri på hvor mye av angrepsflaten som er dekket."""
|
|
assert "dekningsmål" in gate.NO_CLAUDE_LIMIT
|
|
|
|
|
|
def test_a_file_that_is_not_utf8_is_still_scanned_for_a_command(tmp_path: Path) -> None:
|
|
"""N9: ``\\xff\\xfe`` + kommandoen ga GRØNN, og fila ble bare telt som «ulesbar». Et fravær
|
|
som er målt bort er ikke et fravær."""
|
|
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"
|
|
spec = next(s for s in cfg["patterns"] if s["id"] == "prosess")
|
|
payload = b"\xff\xfe" + gate.decode(spec["known_positive_b64"]).encode("utf-8") + b"\n"
|
|
(tmp_path / "src" / "skjult.bin").write_bytes(payload)
|
|
row = gate.score_no_claude_path(cfg, tmp_path)
|
|
assert row.status == gate.RED, row.reason
|
|
assert any("skjult.bin" in x for x in row.exceptions), row.exceptions
|
|
|
|
|
|
def test_a_symlink_pointing_out_of_the_tree_is_refused(tmp_path: Path) -> None:
|
|
"""N10b: symlenker filtreres bort. Peker lenka INN i treet skannes målet selv; peker den UT,
|
|
bar den en kommando usett."""
|
|
cfg = json.loads(json.dumps(_CONFIG["no_claude_path"]))
|
|
cfg["manifest"] = {"sentinel": "src/run.py", "sentinels": ["src/run.py"]}
|
|
repo = tmp_path / "repo"
|
|
_write(repo / "src" / "run.py", "x = 1\n")
|
|
assert gate.score_no_claude_path(cfg, repo).status == gate.GREEN, "kontroll: ren flate"
|
|
spec = next(s for s in cfg["patterns"] if s["id"] == "prosess")
|
|
utenfor = tmp_path / "utenfor.py"
|
|
_write(utenfor, gate.decode(spec["known_positive_b64"]) + "\n")
|
|
(repo / "src" / "lenke.py").symlink_to(utenfor)
|
|
row = gate.score_no_claude_path(cfg, repo)
|
|
assert row.status != gate.GREEN, row.reason
|
|
assert any("lenke.py" in x for x in row.exceptions), row.exceptions
|
|
|
|
|
|
def test_a_pruned_manifest_is_not_measured_instead_of_green(tmp_path: Path) -> None:
|
|
"""N6: manifestet filtrert fra 512 til 451 filer (hele ``examples/``, ``spikes/``,
|
|
``contexts/`` og ``main.py``) ga fortsatt «9 av 9 GRØNN». Sentinel-vakten stoppet en TOM
|
|
flate, ikke en beskåret — én sentinel per område den beskjæringen tok."""
|
|
cfg = json.loads(json.dumps(_CONFIG["no_claude_path"]))
|
|
cfg["manifest"] = {
|
|
"sentinels": ["src/run.py", "main.py", "eksempler/en.md"],
|
|
"skip_dirs": [".git", "__pycache__"],
|
|
}
|
|
for rel in cfg["manifest"]["sentinels"]:
|
|
_write(tmp_path / rel, "x = 1\n")
|
|
assert gate.score_no_claude_path(cfg, tmp_path).status == gate.GREEN, "kontroll: hel flate"
|
|
|
|
(tmp_path / "eksempler" / "en.md").unlink()
|
|
row = gate.score_no_claude_path(cfg, tmp_path)
|
|
assert row.status == gate.NOT_MEASURED, row.reason
|
|
assert any("eksempler/en.md" in x for x in row.exceptions), row.exceptions
|
|
|
|
|
|
def test_the_contract_plants_a_sentinel_in_every_area_the_handlist_missed() -> None:
|
|
"""De 69 sporede filene utenfor den gamle håndlista lå i fire områder. Hvert av dem bærer nå
|
|
sin egen sentinel, så en beskåret flate er IKKE MÅLT og ikke grønn."""
|
|
sentinels = list(_CONFIG["no_claude_path"]["manifest"].get("sentinels", ()))
|
|
assert sentinels, "kontrakten har ingen sentinel-liste — da måler en beskåret flate grønt"
|
|
areas = {s.split("/")[0] for s in sentinels}
|
|
assert {"main.py", "examples", "spikes", "contexts", "src"} <= areas, sorted(areas)
|
|
for rel in sentinels:
|
|
assert (_REPO / rel).is_file(), rel
|
|
|
|
|
|
# --- rad 6: en kjørebok må ha et innhold kontrakten navngir ------------------------------------
|
|
|
|
|
|
def test_a_runbook_without_the_sections_the_contract_names_is_not_green(tmp_path: Path) -> None:
|
|
"""N3: en kjørebok hvis hele innhold er «x», med en korrekt sjekksummet attestering, ga
|
|
«2 av 2 GRØNN». Tom fil var alt som var avvist."""
|
|
cfg = _CONFIG["runbook"]
|
|
sections = list(cfg.get("sections", ()))
|
|
assert sections, "kontrakten navngir ingen seksjoner — da er «x» en kjørebok"
|
|
# Kroppen er _SECTION_BODY og ikke «noe»: fra 20.09 er en overskrift uten tekst under seg
|
|
# ikke en seksjon (se test_a_runbook_of_headings_alone_is_not_a_runbook), og en rc-0-kontroll
|
|
# som faller på DEN regelen måler ikke lenger den den er skrevet for.
|
|
text = "# kjørebok\n\n" + "\n".join(f"## {s}\n\n{_SECTION_BODY}" for s in sections)
|
|
_write(tmp_path / cfg["path"], text)
|
|
digest = hashlib.sha256(text.encode("utf-8")).hexdigest()
|
|
_attestation_lines(tmp_path, digest)
|
|
row = gate.score_runbook(cfg, tmp_path, now=_NOW)
|
|
assert (row.k, row.n, row.status) == (2, 2, gate.GREEN), row.exceptions
|
|
|
|
_write(tmp_path / cfg["path"], "x\n")
|
|
digest = hashlib.sha256(b"x\n").hexdigest()
|
|
_attestation_lines(tmp_path, digest)
|
|
row = gate.score_runbook(cfg, tmp_path, now=_NOW)
|
|
assert row.status == gate.NOT_MEASURED, 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 på 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 må 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 på en navneteknikalitet (målt 19.09 på ``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 må 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
|
|
|
|
|
|
def test_a_subcommand_door_is_only_driven_when_the_command_stands_in_argv(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
"""For en underkommando ER kommandonavnet døren. En probe som starter CLI-en uten det har
|
|
kjørt en annen dør — eller ingen. Uten denne armen ville kravet kunne fjernes uten at noe
|
|
ble rødt: verktøykassens egne prober har alle navnet i argv."""
|
|
src = _fake_src(tmp_path, chat_client=False)
|
|
_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", required=True)\n'
|
|
' sub.add_parser("naviger-pakke")\n'
|
|
" parser.parse_args(argv)\n"
|
|
" return gjoer_noe()\n\n\n"
|
|
'if __name__ == "__main__":\n raise SystemExit(main())\n',
|
|
)
|
|
steg = _one_step()
|
|
steg[0]["entry"] = {
|
|
"kind": "subcommand",
|
|
"module": "verktoey.py",
|
|
"scope": "main",
|
|
"command": "naviger-pakke",
|
|
}
|
|
_write_probe(tmp_path, door="fakesrc.verktoey", needle="naviger-pakke")
|
|
row = gate.score_toolbox(steg, src, _fake_run_path(), _FAKE_PASSED, tmp_path)
|
|
assert (row.k, row.n) == (1, 1), row.exceptions
|
|
|
|
_write_probe(tmp_path, door="fakesrc.verktoey", needle="gjoer_noe")
|
|
row = gate.score_toolbox(steg, src, _fake_run_path(), _FAKE_PASSED, tmp_path)
|
|
assert (row.k, row.n) == (0, 1), row.exceptions
|
|
assert any("underkommandoen" in x for x in row.exceptions), row.exceptions
|
|
|
|
|
|
#: To former MUTANTKJØRINGEN 20.09 fant, ikke dommeren: en probe som er GRØNN i pytest, kjører en
|
|
#: ekte prosess som ikke er døren, og binder det samme navnet asserten leser i DØD kode. Uten
|
|
#: beskjæringen av død kode teller dørkallet, og proben «beviser» et steg den aldri rørte. Begge
|
|
#: mutantene (`if False:` regnes som levende · kode etter `return` regnes som levende) OVERLEVDE
|
|
#: hele suiten før disse to armene fantes — regelen var ekte, men ingen arm nådde den.
|
|
_DOED_KODE_FORMER: tuple[tuple[str, str], ...] = (
|
|
(
|
|
"doer-kallet-i-doed-gren-samme-navn",
|
|
"import subprocess\nimport sys\n\n\n"
|
|
"def <navn>(tmp_path):\n"
|
|
' proc = subprocess.run([sys.executable, "-c", "pass"], capture_output=True)\n'
|
|
" if False:\n"
|
|
' proc = subprocess.run([sys.executable, "-m", "fakesrc.driver", "--steg", "gjoer_noe"])\n'
|
|
" assert proc.returncode == 0\n",
|
|
),
|
|
(
|
|
"doer-kallet-etter-return-samme-navn",
|
|
"import subprocess\nimport sys\n\n\n"
|
|
"def <navn>(tmp_path):\n"
|
|
' proc = subprocess.run([sys.executable, "-c", "pass"], capture_output=True)\n'
|
|
" assert proc.returncode == 0\n"
|
|
" return\n"
|
|
' proc = subprocess.run([sys.executable, "-m", "fakesrc.driver", "--steg", "gjoer_noe"])\n',
|
|
),
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(("form", "body"), _DOED_KODE_FORMER, ids=[n for n, _ in _DOED_KODE_FORMER])
|
|
def test_a_door_call_that_can_never_run_is_not_an_execution(
|
|
tmp_path: Path, form: str, body: str
|
|
) -> None:
|
|
"""Den ærlige proben teller (rc-0-kontroll), den døde formen ikke — og grunnen skal være at
|
|
døren ikke RØRES, ikke at asserten mangler. Formen er realistisk med vilje: den er grønn i
|
|
pytest, for den lever på en ekte prosess som bare ikke er døren."""
|
|
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("rører ikke døren" in x for x in row.exceptions), (form, row.exceptions)
|
|
|
|
|
|
def test_a_runbook_of_headings_alone_is_not_a_runbook(tmp_path: Path) -> None:
|
|
"""Resten av N3: «x» var avvist, men de fem overskriftene PLUSS «x» (125 tegn i alt) ga
|
|
`2 av 2 GRØNN`. Kontrakten krevde at navnet STO der, ikke at det sto noe under det — og en
|
|
innholdsfortegnelse er ikke en kjørebok noen kan følge."""
|
|
digest = _runbook(tmp_path)
|
|
_attestation_lines(tmp_path, digest)
|
|
kontroll = gate.score_runbook(_CONFIG["runbook"], tmp_path, now=_NOW)
|
|
assert (kontroll.k, kontroll.n, kontroll.status) == (2, 2, gate.GREEN), kontroll.exceptions
|
|
|
|
tynn = "\n".join(f"## {s}" for s in _CONFIG["runbook"]["sections"]) + "\nx\n"
|
|
assert len(tynn) < 200, "armen skal måle den formen sjekkpunktet målte"
|
|
_write(tmp_path / _CONFIG["runbook"]["path"], tynn)
|
|
_attestation_lines(tmp_path, hashlib.sha256(tynn.encode("utf-8")).hexdigest())
|
|
row = gate.score_runbook(_CONFIG["runbook"], tmp_path, now=_NOW)
|
|
assert row.status != gate.GREEN
|
|
assert row.k == 1, row.exceptions
|
|
assert any("står ingenting under" in x for x in row.exceptions), row.exceptions
|
|
|
|
|
|
def test_the_runbook_rule_says_that_a_heading_alone_is_not_a_section() -> None:
|
|
assert "overskrift" in gate.RUNBOOK_RULE
|
|
assert gate.RUNBOOK_MIN_BODY > 0
|