test(b-gate): nineteen arms that fail because a probe proves nothing, a reason may be empty, and the row's own limit is untrue

RED FIRST, second repair. PM's checkpoint on c1bdb37 measured N2b: row 1 goes from
3 of 17 to 17 of 17 GREEN with fourteen stub doors and ONE unrelated green test used
as the behaviour probe for all of them. The gate only looked up whether a nodeid was
`passed`; nothing bound the probe to its step. EXTERNAL_DOOR promised the probe
"calls the door and reads the artefact it writes" and there was no code for it --
the same class of defect as entry["kind"] one round ago.

20 new arms, 19 RED, every one on an assert about behaviour (43 assert lines, 0
ImportError, 0 AttributeError, 0 collection errors). The twentieth is green by
construction: every held_out entry in the contract does carry a reason today, and
the arm exists so that stops being true loudly.

Each refusal arm carries its own rc-0 control FIRST -- the same probe, the same
surface, accepted -- then breaks exactly one trait. The constructed probe is now
WRITTEN (`_write_probe`), not merely named: the repaired gate reads the probe's own
source, so a nodeid that does not exist cannot be a behaviour.

Red on the three bearing rests:
  probe not bound to its step   door untouched, step unnamed, nothing asserted,
                                test missing, one probe shared by two steps, and
                                N2b against the contract that stands (17 -> k=0)
  limit statement untrue        the six escapes PM measured as unseen, planted one
                                at a time, and the "TWO ways remain" sentence
  held_out accepts empty        a blank reason shrinks the denominator; the summary
                                counts entries, not reasons; the four steps outside
                                the run path are counted without being named

Red on five of the nine small rests: pruned manifest (451 of 512 still GREEN), a
non-UTF-8 file counted but never scanned, a symlink out of the tree, a runbook whose
whole content is "x", and the row's ratio read as a coverage measure.

One test-hygiene change, not a product fix: the git-dependent arm now skips itself
without .git instead of failing, so "red in a clean extract" stops growing with each
new arm of that class.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-19 21:24:06 +02:00
commit 40869e272f
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q

View file

@ -29,6 +29,8 @@ 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
@ -135,10 +137,41 @@ def _fake_src(root: Path, *, chat_client: bool, symbol: str = "gjoer_noe") -> Pa
#: 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"
#: 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,
nodeid: str = _FAKE_PROBE,
) -> str:
"""En probe som DRIVER døren: kaller kommandoen og asserter på det den gjorde.
Hvert av de tre trekkene gaten måler kan skrus av her, ett om gangen: hvilken dør den rører
(``door``), hvilket steg den navngir (``needle``), og om den asserter i det hele tatt."""
file_part, _, name = nodeid.partition("::")
body = (
"import subprocess\n"
"import sys\n\n\n"
f"def {name}(tmp_path):\n"
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 [
{
@ -155,14 +188,16 @@ def _one_step(symbol: str = "gjoer_noe") -> list[dict[str, Any]]:
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)
_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)
row = gate.score_toolbox(_one_step(), src, None, _FAKE_PASSED, tmp_path)
_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)
@ -182,7 +217,8 @@ def test_m1_the_denominator_follows_the_source_and_is_not_the_config_length(
"entry": {"kind": "module-main", "module": "driver.py", "scope": "main"},
}
]
row = gate.score_toolbox(steps, src, None, _FAKE_PASSED, tmp_path)
_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)
@ -200,7 +236,8 @@ def test_a_step_the_driver_no_longer_calls_leaves_the_denominator(tmp_path: Path
"entry": {"kind": "module-main", "module": "driver.py", "scope": "main"},
}
]
row = gate.score_toolbox(steps, src, None, _FAKE_PASSED, tmp_path)
_write_probe(tmp_path)
row = gate.score_toolbox(steps, src, _fake_run_path(), _FAKE_PASSED, tmp_path)
assert row.n == 1
@ -208,7 +245,8 @@ 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)
_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)
@ -739,7 +777,8 @@ def test_a_stub_main_is_not_a_door_without_a_registered_entry(tmp_path: Path) ->
_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)
_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
@ -749,21 +788,23 @@ def test_a_registered_door_without_a_passing_behaviour_probe_does_not_count(
) -> 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, None, {}, tmp_path)
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, None, {_FAKE_PROBE: "skipped"}, tmp_path)
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, None, _FAKE_PASSED, tmp_path)
assert (row.k, row.n, row.status) == (1, 1, gate.GREEN)
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, None, _FAKE_PASSED, tmp_path)
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
@ -795,13 +836,16 @@ def test_the_published_surface_is_the_repo_manifest_and_not_a_handlist() -> None
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}
"""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
@ -1009,3 +1053,364 @@ def test_an_attestation_with_a_bom_is_still_read(tmp_path: Path) -> None:
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``. 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 navngi steget den beviser."""
src = _fake_src(tmp_path, chat_client=False)
_write_probe(tmp_path)
assert gate.score_toolbox(_one_step(), src, _fake_run_path(), _FAKE_PASSED, tmp_path).k == 1
_write_probe(tmp_path, needle="et_helt_annet_steg")
row = gate.score_toolbox(_one_step(), src, _fake_run_path(), _FAKE_PASSED, tmp_path)
assert (row.k, row.n) == (0, 1)
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 ``anthropic``-SDK-en
(to skriveformer), ``npx @anthropic-ai/claude-code`` (to former), ``uvx claude-code`` og et
dynamisk import-kall. Hver plantes én om gangen, med rc-0-kontroll først 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"
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 at hver vakt MÅLER, aldri 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, 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"
text = "# kjørebok\n\n" + "\n".join(f"## {s}\n\nnoe\n" 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