"""The measurements read a FROZEN copy of the vegnormal bases, pinned by sha256 — never another repository's live build directory. Measured 2026-09-17 17:43: ``vegnormal-okf`` rebuilt ``build/ferdig/r761-2025`` while this repository's gate pointed straight at it. Rows 6-7 went "IKKE MÅLT" and five tests fell, for a change no one here made. The failure mode was never falsehood — the gate says IKKE MÅLT and exits non-zero, never green — it was that two projects shared a directory neither owns, so what this repository MEASURES could change without a commit here. The fix is a copy outside both repositories plus a pin this repository tracks. The pin is the whole point: a copy with no pin is the same shared directory one move further away. So the three states are separated by construction, and each has its own arm below: * the copy matches the pin -> it resolves, and that is the only green path; * the copy is GONE -> ``FrozenBundleMissing`` (an ``OSError``): the gate says IKKE MÅLT and fails the exit code exactly as it did before this change; the delivered-corpus tests SKIP, which is MAJOR-3's ceiling rule (a hard error would break ``uv run pytest`` in the handover archive, where no corpus is mounted); * the copy DIFFERS from the pin -> ``FrozenBundleDrift`` (a ``ValueError``): loud, named, and NEVER a skip. A drifted copy is not an unreadable measurement, it is a measurement of the wrong corpus, which is the one thing that produces a silently wrong number. The two exception classes are deliberately unrelated: a caller that catches "missing" to skip must not swallow "drift". ``test_drift_is_not_a_missing_copy`` is that gate. """ from __future__ import annotations import json import re import shutil from pathlib import Path from typing import Any import pytest from portfolio_optimiser import frozen_bundles as fb from portfolio_optimiser.evals import v1_gate as gate from portfolio_optimiser.stress import read_bundle_declarations _REPO = Path(__file__).resolve().parent.parent #: Joined at run time on purpose: this file NAMES the forbidden path spellings, and a literal #: would make the gate below red against its own source. (Implicit concatenation is not enough — #: ``ruff format`` folds ``"a" "b"`` back into one literal, measured here.) _OTHER_REPO = "-".join(("vegnormal", "okf")) #: The two spellings a path to that repository's build directory takes in this codebase. Both were #: present before this change (3 + 4 hits, the known positives recorded in the order's evidence). #: Each is paired with the line that PROVES it can match: a pattern that matches nothing makes a #: gate that can only be green, and the two spellings do not match each other's sample. _FORBIDDEN = ( ( re.compile(re.escape(_OTHER_REPO + "/build")), f'ROOT = Path("~/repos/{_OTHER_REPO}/build/ferdig")', ), ( re.compile("[\"']" + re.escape(_OTHER_REPO) + "[\"']"), 'ROOT = Path.home() / "repos" / "' + _OTHER_REPO + '" / "build" / "ferdig"', ), ) #: Prose that documents history is explicitly allowed by the order; only paths are forbidden. _SCANNED = ("src", "tests", "contexts") # --------------------------------------------------------------------------------------------- # helpers # --------------------------------------------------------------------------------------------- def _bundle(root: Path, body: str = "one") -> Path: root.mkdir(parents=True, exist_ok=True) (root / "index.md").write_text("# base\n\n- [a](krav/a.md)\n", encoding="utf-8") (root / "krav").mkdir(exist_ok=True) (root / "krav" / "a.md").write_text( f"---\ntype: Krav\ntitle: A\n---\n\n{body}\n", encoding="utf-8" ) return root def _store(tmp_path: Path, name: str = "n500-2024", body: str = "one") -> tuple[Path, Path]: """A frozen store holding ONE pinned bundle, and the pin file that names it.""" store = tmp_path / "store" digest, files = fb.digest_bundle(_bundle(tmp_path / "src-of-truth", body)) directory = f"{name}-{digest[: fb.SHORT]}" _bundle(store / directory, body) pin = tmp_path / "pin.json" pin.write_text( json.dumps( { "bundles": {name: {"directory": directory, "sha256": digest, "files": files}}, } ), encoding="utf-8", ) return store, pin def _use(monkeypatch: pytest.MonkeyPatch, store: Path, pin: Path) -> None: monkeypatch.setenv(fb.STORE_ENV, str(store)) monkeypatch.setattr(fb, "PIN_FILE", pin) def _touch_one_byte(base: Path) -> None: doc = base / "krav" / "a.md" doc.write_text(doc.read_text(encoding="utf-8").replace("one", "ONE"), encoding="utf-8") # --------------------------------------------------------------------------------------------- # (a)-(b) the digest # --------------------------------------------------------------------------------------------- def test_the_digest_covers_every_byte_and_every_name(tmp_path: Path) -> None: """Stable across recomputation; moved by one byte AND by a rename that changes no byte — without the name in the hash, a corpus reshuffled under the same bytes would pin clean.""" base = _bundle(tmp_path / "b") first, files = fb.digest_bundle(base) assert (first, files) == fb.digest_bundle(base) assert files == 2 _touch_one_byte(base) changed, _ = fb.digest_bundle(base) assert changed != first renamed = _bundle(tmp_path / "c") (renamed / "krav" / "a.md").rename(renamed / "krav" / "b.md") assert fb.digest_bundle(renamed)[0] != fb.digest_bundle(_bundle(tmp_path / "d"))[0] def test_an_added_or_removed_file_is_drift_even_when_no_kept_byte_changes( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: """The two arms nothing pinned (measured 2026-09-18: a mutant that ignored an extra or a missing file left 197 tests green). Every surviving file is byte-identical to the pin here — only the SET of files moved, and the set is what a copy is.""" store, pin = _store(tmp_path) _use(monkeypatch, store, pin) base = fb.bundle_dir("n500-2024") # control: green before either edit extra = base / "krav" / "b.md" extra.write_text("---\ntype: Krav\ntitle: B\n---\n", encoding="utf-8") with pytest.raises(fb.FrozenBundleDrift): fb.bundle_dir("n500-2024") extra.unlink() assert fb.bundle_dir("n500-2024") == base # the edit, not the fixture, was the cause (base / "krav" / "a.md").unlink() with pytest.raises(fb.FrozenBundleDrift) as exc: fb.bundle_dir("n500-2024") assert "1 filer" in str(exc.value) # what disk holds now: one file of the pinned two def test_the_digest_does_not_depend_on_directory_listing_order( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: """Determinism with no corpus mounted: the same tree listed in ANOTHER order digests the same. Only the corpus tests used to catch a removed sort, and they skip where no corpus exists.""" base = _bundle(tmp_path / "b") for extra in ("z.md", "m.md", "krav/q.md"): (base / extra).write_text(extra, encoding="utf-8") baseline = fb.digest_bundle(base) listed = list(base.rglob("*")) assert len(listed) > 3 real = Path.rglob for order in (list(reversed(listed)), listed[1:] + listed[:1]): monkeypatch.setattr(Path, "rglob", lambda self, pattern, _o=order: iter(_o)) assert fb.digest_bundle(base) == baseline monkeypatch.setattr(Path, "rglob", real) def test_a_matching_copy_resolves_to_the_pinned_directory( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: store, pin = _store(tmp_path) _use(monkeypatch, store, pin) resolved = fb.bundle_dir("n500-2024") assert resolved.parent == store assert (resolved / "index.md").is_file() # --------------------------------------------------------------------------------------------- # (c)-(e) the three states # --------------------------------------------------------------------------------------------- def test_one_changed_byte_is_drift_named_with_both_digests( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: store, pin = _store(tmp_path) _use(monkeypatch, store, pin) base = fb.bundle_dir("n500-2024") # green before the edit — the control expected = json.loads(pin.read_text(encoding="utf-8"))["bundles"]["n500-2024"]["sha256"] _touch_one_byte(base) with pytest.raises(fb.FrozenBundleDrift) as exc: fb.bundle_dir("n500-2024") message = str(exc.value) assert "avviker fra pin" in message assert "n500-2024" in message assert expected[: fb.SHORT] in message # what was pinned assert fb.digest_bundle(base)[0][: fb.SHORT] in message # what is on disk def test_a_missing_copy_is_missing_and_stays_an_oserror( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: """``measure_stress`` already catches ``OSError`` -> IKKE MÅLT + exit 1; that path is unchanged.""" store, pin = _store(tmp_path) _use(monkeypatch, store, pin) shutil.rmtree(store / fb.load_pins()["n500-2024"].directory) with pytest.raises(fb.FrozenBundleMissing) as exc: fb.bundle_dir("n500-2024") assert isinstance(exc.value, OSError) assert "n500-2024" in str(exc.value) def test_drift_is_not_a_missing_copy(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: """A caller that catches "missing" in order to SKIP must never swallow drift: skipping on a wrong corpus is the silent failure this whole change exists to remove.""" assert not issubclass(fb.FrozenBundleDrift, fb.FrozenBundleMissing) assert not issubclass(fb.FrozenBundleMissing, fb.FrozenBundleDrift) assert not isinstance(fb.FrozenBundleDrift("x"), OSError) store, pin = _store(tmp_path) _use(monkeypatch, store, pin) _touch_one_byte(fb.bundle_dir("n500-2024")) with pytest.raises(fb.FrozenBundleDrift): try: fb.bundle_dir("n500-2024") except fb.FrozenBundleMissing: # pragma: no cover - the defect this arm forbids pytest.fail("drift was answered as a missing copy") def test_an_unknown_name_is_refused_by_name( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: store, pin = _store(tmp_path) _use(monkeypatch, store, pin) with pytest.raises(fb.FrozenBundleMissing) as exc: fb.bundle_dir("n100-2023") assert "n100-2023" in str(exc.value) and "n500-2024" in str(exc.value) def test_an_explicit_override_is_unpinned_and_the_operator_named_it( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: """``--bundle-root`` / ``PORTFOLIO_VEGNORMAL_ROOT`` stays an escape hatch: the operator who names a live mount gets it, pin or no pin. Absent it, the frozen store answers.""" store, pin = _store(tmp_path) _use(monkeypatch, store, pin) live = tmp_path / "live" _bundle(live / "n500-2024", body="something else entirely") assert fb.bundle_dir("n500-2024", override=live) == live / "n500-2024" monkeypatch.setenv(fb.OVERRIDE_ENV, str(live)) assert fb.bundle_dir("n500-2024") == live / "n500-2024" # --------------------------------------------------------------------------------------------- # (f)-(g) the tracked pin # --------------------------------------------------------------------------------------------- def test_the_pin_names_every_base_the_context_sets_declare() -> None: declared = { d["name"] for path in sorted((_REPO / "contexts").glob("*/bundle.txt")) for d in read_bundle_declarations(path) } assert declared, "no context set declares a base — the denominator would be vacuous" assert declared <= set(fb.load_pins()) def test_the_pinned_directory_name_carries_the_short_digest() -> None: """A stale copy is visible in ``ls``, not only to the verifier.""" pins = fb.load_pins() assert pins, "the pin file names no bundle" for name, pin in pins.items(): assert pin.directory == f"{name}-{pin.sha256[: fb.SHORT]}" assert pin.files > 0 def test_the_pin_file_carries_no_key_nobody_reads() -> None: """``store`` sat in the pin file with no reader: the store is ``DEFAULT_STORE`` / the env var, and a second, silent spelling of it would drift from the first.""" keys = set(json.loads(fb.PIN_FILE.read_text(encoding="utf-8"))) assert keys == {"source", "renewal", "bundles"} def test_the_bundle_itself_is_never_tracked_here() -> None: """Vegnormal corpora must not reach a public remote: only the pin is tracked.""" tracked = (_REPO / "src" / "portfolio_optimiser" / "frozen_bundles.json").read_text("utf-8") assert "index.md" not in tracked # --------------------------------------------------------------------------------------------- # (h) nothing reads the other repository's build directory any more # --------------------------------------------------------------------------------------------- #: Every text form a path to that build directory could be written in. ``.py/.json/.txt`` alone #: left prose-and-config (md, yaml, toml) outside the denominator, unmeasured. _SUFFIXES = {".py", ".json", ".txt", ".md", ".yaml", ".yml", ".toml"} def _forbidden_hits(root: Path, tops: tuple[str, ...]) -> tuple[list[str], int]: """(hits, files scanned). The count is the denominator: a scan of zero files is not green.""" hits: list[str] = [] scanned = 0 for top in tops: for path in sorted((root / top).rglob("*")): if not path.is_file() or path.suffix not in _SUFFIXES: continue scanned += 1 for n, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1): if any(p.search(line) for p, _ in _FORBIDDEN): hits.append(f"{path.relative_to(root)}:{n}: {line.strip()}") return hits, scanned def test_the_forbidden_path_scan_finds_a_known_positive_in_every_scanned_suffix( tmp_path: Path, ) -> None: """Shown, not assumed: plant each spelling in a file of EACH suffix and see the scan report exactly those files. A suffix the scan skips would read clean here and be red in this test.""" suffixes = (".py", ".json", ".txt", ".md", ".yaml", ".yml", ".toml") # literal, not _SUFFIXES for suffix in suffixes: for i, (_, sample) in enumerate(_FORBIDDEN): planted = tmp_path / "src" / f"planted{i}{suffix}" planted.parent.mkdir(exist_ok=True) planted.write_text(f"before\n{sample}\nafter\n", encoding="utf-8") hits, scanned = _forbidden_hits(tmp_path, ("src",)) assert scanned == len(suffixes) * len(_FORBIDDEN) assert len(hits) == scanned, hits def test_no_measurement_reads_the_other_repos_build_directory() -> None: """Both spellings, each with a known-positive control: a pattern that cannot match anything is a gate that can only be green.""" for pattern, known_positive in _FORBIDDEN: assert pattern.search(known_positive), pattern.pattern hits, scanned = _forbidden_hits(_REPO, _SCANNED) assert scanned > 0 assert hits == [], "\n".join(hits) # --------------------------------------------------------------------------------------------- # (i)-(j) the gate # --------------------------------------------------------------------------------------------- def _evidence(tmp_path: Path) -> tuple[dict[str, Any], Path]: stress_root = tmp_path / "stress" (stress_root / "o").mkdir(parents=True) return { "label": "s", "root": "scratchpad", "runs": [ { "context": "contexts/tunnel-hauglia-2027", "outbox": "o", "run_id": "r", "bundle": None, } ], }, stress_root def test_a_drifted_copy_fails_the_gate_with_the_reason_said( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: store, pin = _store(tmp_path) _use(monkeypatch, store, pin) _touch_one_byte(fb.bundle_dir("n500-2024")) evidence, stress_root = _evidence(tmp_path) m = gate.measure_stress(evidence, _REPO, stress_root, None) assert "avviker fra pin" in m.drift assert m.missing == "" # drift is NOT "artefakter mangler" — the two are named apart row = gate.score_undeclared(["p"], {"p": "passed"}, m, "s") assert (row.k, row.status, row.failing) == (None, gate.NOT_MEASURED, True) assert "pin-drift" in row.reason and "artefakter mangler" not in row.reason assert "pin-drift" in gate.score_named(m, "s").reason assert gate.exit_code([row]) == 1 def test_a_missing_copy_is_not_measured_and_never_green( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: store, pin = _store(tmp_path) _use(monkeypatch, store, pin) evidence, stress_root = _evidence(tmp_path) monkeypatch.setenv(fb.STORE_ENV, str(tmp_path / "gone")) m = gate.measure_stress(evidence, _REPO, stress_root, None) assert m.missing and not m.drift and m.validated == 0 row = gate.score_undeclared(["p"], {"p": "passed"}, m, "s") assert (row.status, row.failing) == (gate.NOT_MEASURED, True) assert "artefakter mangler" in row.reason and "pin-drift" not in row.reason assert gate.exit_code([row]) == 1 # --------------------------------------------------------------------------------------------- # (k) the delivered-corpus tests: missing SKIPS, drift FAILS # --------------------------------------------------------------------------------------------- @pytest.mark.parametrize( "module,helper", [ ("test_context_sets_loadbearing", "_bundle_dir"), ("test_navigation_window_loadbearing", "_delivered"), ("test_inert_identifier_loadbearing", "_base"), ("test_requirement_number_gate_loadbearing", "_base"), ], ) def test_the_corpus_tests_skip_when_absent_but_fail_on_drift( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, module: str, helper: str ) -> None: mod = __import__(module) resolve = getattr(mod, helper) store, pin = _store(tmp_path) _use(monkeypatch, store, pin) assert resolve("n500-2024").is_dir() # control: the matching copy resolves _touch_one_byte(fb.bundle_dir("n500-2024")) # A skip is caught EXPLICITLY, never left to ``pytest.raises``: measured against the mutation # that makes drift a subclass of missing, these four arms SKIPPED instead of failing (5 -> 9 # skipped over the whole suite) and stayed green — a gate that cannot see the one defect it # exists for. try: resolve("n500-2024") except fb.FrozenBundleDrift: pass except pytest.skip.Exception as exc: raise AssertionError(f"{module}.{helper} SKIPPED a drifted copy: {exc}") from None else: raise AssertionError(f"{module}.{helper} accepted a drifted copy") monkeypatch.setenv(fb.STORE_ENV, str(tmp_path / "gone")) with pytest.raises(pytest.skip.Exception): resolve("n500-2024")