portfolio-optimiser/tests/test_b_gate.py
Kjell Tore Guttormsen 2d957a93c5
test(b-gate): the surface arms hold in an extract too, and one arm owns the git-manifest denominator
Three of the new row-3 arms pinned 512 files and the literal source label, which only a tree WITH
git metadata can answer; a clean extract (git archive | tar -x) would have turned all three red for
the same reason the three already-named extraction artefacts go red. The property they test holds
in both worlds, so they now assert it there: main.py, examples/, spikes/ and contexts/ are inside
the guard whichever manifest was read, and the row prints whatever denominator it measured.

The exact number and the label belong to ONE arm — the one whose subject IS the git manifest. In an
extract it is an extraction artefact of the same class as the others, and says so in its docstring.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 19:42:40 +02:00

1011 lines
44 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
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 = 3
_ROLES_TODAY = ("proposer", "checker")
_PATTERNS_TODAY = 9
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
def test_row1_today_is_red_with_three_of_seventeen_steps_callable_from_outside() -> None:
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)
named = {x.split(":")[0] for x in row.exceptions}
assert "rundebinding" not in named and "gate" not in named
assert "validering" in named and "utboks" in named
assert "prepass-artefakt" in named and "parse-feil" in named
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.
_FAKE_PROBE = "tests/test_konstruert.py::test_doeren_skriver_artefaktet"
_FAKE_PASSED = {_FAKE_PROBE: "passed"}
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)
row = gate.score_toolbox(_one_step(), src, None, _FAKE_PASSED, tmp_path)
assert (row.k, row.n, row.status) == (1, 1, gate.GREEN)
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)
row = gate.score_toolbox(_one_step(), src, None, _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"},
}
]
row = gate.score_toolbox(steps, src, None, _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"},
}
]
row = gate.score_toolbox(steps, src, None, _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)
row = gate.score_toolbox(_one_step(), src, None, _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:
_write(tmp_path / _CONFIG["runbook"]["path"], "# kjørebok\n\nsteg 1 …\n")
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 = 512
_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"}
row = gate.score_toolbox(steps, src, None, _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)
steps = _one_step()
row = gate.score_toolbox(steps, src, None, {}, 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, None, {_FAKE_PROBE: "skipped"}, tmp_path)
assert row.k == 0, "hoppet over er ikke bestått"
row = gate.score_toolbox(steps, src, None, _FAKE_PASSED, tmp_path)
assert (row.k, row.n, row.status) == (1, 1, gate.GREEN)
def test_a_step_without_any_probe_is_never_a_door(tmp_path: Path) -> None:
src = _fake_src(tmp_path, chat_client=False)
steps = _one_step()
steps[0].pop("probe")
row = gate.score_toolbox(steps, src, None, _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 er da et uttrekksartefakt — samme
klasse som de tre som allerede er navngitt. Nevneren pinnes HER, ikke i armen over."""
tracked = subprocess.run(
["git", "ls-files", "-z"], cwd=_REPO, capture_output=True, text=True, check=True
).stdout.split("\0")
mine = {t for t in tracked 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} ulesbare" 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 ----------------------------------------------------------
def _runbook(root: Path) -> str:
text = "# kjørebok\n\nsteg 1: naviger pakken\n"
_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