test(frozen-pin): an added or removed file, and listing order, pinned by tests; drift named apart from missing

K5 survived a full run (a mutant ignoring an extra or removed file left the suite green) and K4
(sort removed) was caught only by corpus data. Now pinned with no corpus: the add arm, the remove
arm, and a shuffled directory listing digesting the same. The gate says "pin-drift" for a drifted
copy instead of "artefakter mangler"; the unread "store" key is gone from frozen_bundles.json; the
forbidden-path scan covers md/yaml/yml/toml and proves a known positive per suffix.

Five mutants killed in a scratch clone (drop-last-entry 8 failed, no-sort, no-names, drift-as-missing,
scan-suffixes-narrowed 1 failed each).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-18 18:49:54 +02:00
commit 68079469c3
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
3 changed files with 108 additions and 17 deletions

View file

@ -1114,6 +1114,9 @@ class StressMeasure:
commissioned: int = 0
where: str = ""
missing: str = ""
#: The pinned copy is on disk but is NOT the pinned copy. Kept apart from ``missing``: absent
#: artefacts and a measurement of the wrong corpus are different states with different fixes.
drift: str = ""
#: Declarations with no ``approach_id`` — written before the rule; the row cannot be measured.
unaddressed: int = 0
#: Of ``validated``, how many were the runs' own proposals (M-3).
@ -1170,8 +1173,9 @@ def measure_stress(
return StressMeasure(where=str(stress_root), missing=f"{context}: base ikke entydig")
try:
# The frozen copy this repository pins, unless the operator named a live mount.
# Drift is a ValueError and absence an OSError: both land in ``missing`` below, so a
# corpus that moved is IKKE MÅLT with the reason said, never a silently wrong number.
# Drift is a ValueError and absence an OSError: drift lands in ``drift`` and absence
# in ``missing``, so a corpus that moved is IKKE MÅLT with its own reason said, never
# a silently wrong number and never "artefakter mangler".
base = frozen_bundles.bundle_dir(chosen[0]["name"], override=bundle_root)
verdicts.append(
stress.score_context_set(
@ -1182,6 +1186,8 @@ def measure_stress(
bundle_id=chosen[0]["bundle_id"] if len(declared) > 1 else None,
)
)
except frozen_bundles.FrozenBundleDrift as exc:
return StressMeasure(where=str(stress_root), drift=f"{run_spec['run_id']}: {exc}")
except (stress.EmptyMeasurement, OSError, ValueError) as exc:
return StressMeasure(where=str(stress_root), missing=f"{run_spec['run_id']}: {exc}")
contexts.add(run_spec["context"])
@ -1227,7 +1233,9 @@ def score_undeclared(
k: int | None = None
n: int | None = None
diagnostics: tuple[str, ...] = ()
if m.missing:
if m.drift:
reason = f"{probe_state}; {label}: ikke målt, pin-drift ({m.drift})"
elif m.missing:
reason = f"{probe_state}; {label}: ikke målt, artefakter mangler ({m.missing})"
elif m.unaddressed:
reason = (
@ -1281,14 +1289,16 @@ STRESS_DEPENDENCY = (
def score_named(m: StressMeasure, label: str) -> Row:
title = "7 named (diagnose, ingen terskel)"
if m.missing:
if m.drift or m.missing:
return Row(
"named",
title,
None,
None,
NOT_MEASURED,
f"{label}: ikke målt, artefakter mangler ({m.missing})",
f"{label}: ikke målt, pin-drift ({m.drift})"
if m.drift
else f"{label}: ikke målt, artefakter mangler ({m.missing})",
failing=False,
diagnostics=(NAMED_WARNING,),
)

View file

@ -1,5 +1,4 @@
{
"store": "~/corpora/po-frosne-bundles",
"source": "vegnormal-okf build/ferdig, READ-ONLY copy taken 2026-09-18",
"renewal": "Ny kopi + ny pin i SAMME commit - se README, 'Frosne kunnskapsbaser'.",
"bundles": {

View file

@ -86,7 +86,6 @@ def _store(tmp_path: Path, name: str = "n500-2024", body: str = "one") -> tuple[
pin.write_text(
json.dumps(
{
"store": str(store),
"bundles": {name: {"directory": directory, "sha256": digest, "files": files}},
}
),
@ -127,6 +126,48 @@ def test_the_digest_covers_every_byte_and_every_name(tmp_path: Path) -> None:
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:
@ -239,6 +280,13 @@ def test_the_pinned_directory_name_carries_the_short_digest() -> None:
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")
@ -250,19 +298,49 @@ def test_the_bundle_itself_is_never_tracked_here() -> None:
# ---------------------------------------------------------------------------------------------
#: 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 = [
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)
]
hits, scanned = _forbidden_hits(_REPO, _SCANNED)
assert scanned > 0
assert hits == [], "\n".join(hits)
@ -297,9 +375,12 @@ def test_a_drifted_copy_fails_the_gate_with_the_reason_said(
evidence, stress_root = _evidence(tmp_path)
m = gate.measure_stress(evidence, _REPO, stress_root, None)
assert "avviker fra pin" in m.missing
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
@ -312,9 +393,10 @@ def test_a_missing_copy_is_not_measured_and_never_green(
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
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