Measured 2026-09-17 17:43: vegnormal-okf rebuilt build/ferdig/r761-2025 while this repository's v1 gate, the stress judge and four corpus tests pointed straight at it. Rows 6-7 went IKKE MAALT and five tests fell, for a change no one here made. The failure mode was never falsehood - the gate says IKKE MAALT and exits non-zero, never green - it was instability: two projects shared a directory neither owns, so what this repository MEASURES could move without a commit here. A copy alone would push that directory one move away, so the copy comes with a pin. frozen_bundles.json (tracked) carries path + sha256 + file count per base; the bundles themselves are NEVER committed here. Three states, separated by construction: match -> resolves; gone -> FrozenBundleMissing (an OSError, so the gate's existing except OSError gives IKKE MAALT + exit 1 unchanged and the corpus tests SKIP, MAJOR-3's ceiling); drift -> FrozenBundleDrift (a ValueError), loud, named, and never a skip. The two classes are deliberately unrelated: a caller that catches "missing" to skip must not swallow "drift". The NAME is hashed alongside the bytes, and the directory name carries the first 12 chars of the digest so a stale copy is visible in ls. Renewal is a decision: new copy + new pin in the SAME commit (README). --bundle-root / PORTFOLIO_VEGNORMAL_ROOT stays as the operator's explicit, UNPINNED live mount. Iron Law: the tests were written and run RED first (collection error, then two arms of my own making). Load-bearing MEASURED, eight mutations all red against the WHOLE suite with a green control of 1984 passed / 5 skipped / 5 xfailed and a strict node-id superset (1977 -> 1994, 0 removed): M1 the pin is never verified (7) - M2 drift collapsed into missing (5) - M3 the name is not hashed (40) - M4 the gate seam reverted to root/name (1) - M5 the corpus helpers skip on drift too (4, one per file) - M6a the slash spelling back in src (1) - M6b the quoted path segment back in a test (1) - M7 the directory name drops the short digest (1, and 45 skipped, which proves absence is a SKIP and not a false green) - M8 the explicit override ignored (3, two of them in test_stress_judge_loadbearing.py, independent witnesses older than this work). M2 FALSIFIED THE TEST FIRST: the four parametrised arms did not go red, they went to SKIP (5 -> 9 skipped) and stayed green - pytest.skip inside a pytest.raises is not a failure. The arm now catches pytest.skip.Exception explicitly and turns it into an AssertionError. grep -rnE 'vegnormal-okf/build|["'"'"']vegnormal-okf["'"'"']' src tests contexts -> 0 (3 + 4 hits before; the three remaining prose mentions document history and are allowed). Gate re-run against the frozen copy: identical to the live mount (rows 0/3 - 0/3 - 3/8 - no report - 3/8 - IKKE MAALT - 1/20, exit 1). Order 20260917T223645Z-1296211942-from-.claude. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
360 lines
15 KiB
Python
360 lines
15 KiB
Python
"""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(
|
|
{
|
|
"store": str(store),
|
|
"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_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_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
|
|
# ---------------------------------------------------------------------------------------------
|
|
|
|
|
|
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 = [
|
|
f"{path.relative_to(_REPO)}:{n}: {line.strip()}"
|
|
for top in _SCANNED
|
|
for path in sorted((_REPO / top).rglob("*"))
|
|
if path.is_file() and path.suffix in {".py", ".json", ".txt"}
|
|
for n, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1)
|
|
if any(p.search(line) for p, _ in _FORBIDDEN)
|
|
]
|
|
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.missing
|
|
row = gate.score_undeclared(["p"], {"p": "passed"}, m, "s")
|
|
assert (row.k, row.status, row.failing) == (None, gate.NOT_MEASURED, True)
|
|
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 m.validated == 0
|
|
row = gate.score_undeclared(["p"], {"p": "passed"}, m, "s")
|
|
assert (row.status, row.failing) == (gate.NOT_MEASURED, True)
|
|
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")
|