feat(frozen-bundles): the measurements read a pinned copy, not another repo's build dir
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>
This commit is contained in:
parent
9825b2677c
commit
50c9763706
12 changed files with 654 additions and 60 deletions
45
CLAUDE.md
45
CLAUDE.md
|
|
@ -3148,6 +3148,51 @@ Python ≥3.10. MAF (`agent-framework-core` 1.16.0, `-orchestrations` 1.1.1 —
|
||||||
uten `approach_id` (eldre artefakter) som `run`; v1-gatens rad 6 sier da «IKKE MÅLT», og IKKE
|
uten `approach_id` (eldre artefakter) som `run`; v1-gatens rad 6 sier da «IKKE MÅLT», og IKKE
|
||||||
MÅLT feller exit-koden (aldri grønn). Load-bearing MÅLT
|
MÅLT feller exit-koden (aldri grønn). Load-bearing MÅLT
|
||||||
(`tests/test_row6_declaration_rule_loadbearing.py` + rad 6-probene), ti mutasjoner alle røde.
|
(`tests/test_row6_declaration_rule_loadbearing.py` + rad 6-probene), ti mutasjoner alle røde.
|
||||||
|
- **Målingene leser en FROSSET kopi av korpuset, pinnet med sha256 — aldri et annet repos levende
|
||||||
|
build-mappe (18.09, ordre `20260917T223645Z-1296211942`):** gaten, stressdommeren og fire
|
||||||
|
korpus-tester leste `~/repos/vegnormal-okf/build/ferdig` DIREKTE. **MÅLT 17.09 17:43:** vegnormal
|
||||||
|
bygget om `r761-2025`, rad 6–7 sa «IKKE MÅLT» og fem tester falt, for en endring ingen her gjorde.
|
||||||
|
**Feilmodusen var aldri usannhet** — gaten sier IKKE MÅLT og exit 1, aldri falskt grønn — den var
|
||||||
|
USTABILITET: to prosjekter delte en mappe ingen av dem eier, så hva dette repoet MÅLER kunne
|
||||||
|
flytte seg uten en commit her. **En kopi alene ville bare skjøvet den mappa ett hakk unna**, så
|
||||||
|
kopien kommer med en PIN: `frozen_bundles.json` (tracked) bærer sti + sha256 + filantall per base,
|
||||||
|
mens BUNDLENE ALDRI committes her (vegnormal-korpus skal ikke til en offentlig flate).
|
||||||
|
**Tre tilstander, skilt VED KONSTRUKSJON, og den tredje er hele poenget:** kopien MATCHER → den
|
||||||
|
eneste stien som måler noe; kopien er BORTE → `FrozenBundleMissing`, en `OSError`, så gatens
|
||||||
|
eksisterende `except OSError` gir IKKE MÅLT + exit 1 uendret og korpus-testene SKIPPER (MAJOR-3s
|
||||||
|
tak: en hard feil ville brutt `uv run pytest` i overleveringsarkivet, der intet korpus er
|
||||||
|
montert); kopien AVVIKER → `FrozenBundleDrift`, en `ValueError`, høylytt og navngitt og ALDRI en
|
||||||
|
skip — en drevet kopi er ikke en uleselig måling, den er en måling av FEIL korpus, altså den ene
|
||||||
|
tilstanden som produserer et stille galt tall. **De to klassene er BEVISST uten slektskap:** en
|
||||||
|
kaller som fanger «mangler» for å skippe må ikke svelge «avvik». **NAVNET hashes ved siden av
|
||||||
|
bytene** — uten det ville et korpus stokket om under samme bytes pinnet rent — og katalognavnet
|
||||||
|
BÆRER de 12 første tegnene av digesten, så en foreldet kopi er synlig i `ls`, ikke bare for
|
||||||
|
verifisereren. **Fornyelse er en BESLUTNING, aldri rydding:** ny kopi + ny pin i SAMME commit
|
||||||
|
(README § Frozen knowledge bases). `--bundle-root` / `PORTFOLIO_VEGNORMAL_ROOT` består som
|
||||||
|
operatørens EKSPLISITTE, UPINNEDE levende montering — måten å se på et ferskt korpus før man
|
||||||
|
bestemmer seg for å fryse på nytt; uten den kan ikke gaten brukes til å ta den beslutningen.
|
||||||
|
**Grep-gaten dekker BEGGE stavemåtene, hver med sin egen kjent-positiv:** `vegnormal-okf/build`
|
||||||
|
(3 treff før) og det siterte sti-segmentet `"vegnormal-okf"` (4 treff før) — en fil-bred gate med
|
||||||
|
ÉN av dem ville vært grønn mot fire av de sju stedene, og prosa som dokumenterer historikk
|
||||||
|
(3 treff) er eksplisitt tillatt. Testfila bygger tokenet med `"-".join(...)`, fordi `ruff format`
|
||||||
|
MÅLT folder `"vegnormal" "-okf"` tilbake til én literal og gaten da blir rød mot sin egen kilde.
|
||||||
|
Load-bearing MÅLT (`tests/test_frozen_bundles_loadbearing.py`, 17 armer), **åtte mutasjoner alle
|
||||||
|
røde mot HELE suiten** + grønn kontroll **1984/5 + 5 xfailed** og node-ID-supersett (1977 → 1994,
|
||||||
|
**0 fjernet**): M1 pinnen verifiseres aldri (7 røde) · M2 avvik kollapset inn i «mangler» (5) ·
|
||||||
|
M3 navnet hashes ikke (40) · M4 gate-sømmen reverteres til `root/name` (1) · M5 korpus-testene
|
||||||
|
skipper på avvik også (4 — én per fil) · M6a `vegnormal-okf/build` tilbake i `src` (1) ·
|
||||||
|
M6b det siterte sti-segmentet tilbake i en test (1) · M7 katalognavnet dropper den korte digesten
|
||||||
|
(1, og 45 skipped — som beviser at fravær er en SKIP, ikke en falsk grønn) · M8 den eksplisitte
|
||||||
|
overstyringen ignoreres (3, hvorav TO i `test_stress_judge_loadbearing.py`, uavhengige vitner
|
||||||
|
eldre enn dette arbeidet). **M2 FALSIFISERTE TESTEN FØRST (repoets vakuøs-gate-klasse,
|
||||||
|
TJUEFJERDE gang):** de fire parametriserte armene gikk ikke røde, de gikk til SKIP (5 → 9 skipped
|
||||||
|
over suiten) og sto grønne — `pytest.skip` inne i en `pytest.raises` er ikke en feil. Armen fanger
|
||||||
|
nå `pytest.skip.Exception` EKSPLISITT og gjør den til en `AssertionError`. **Ærlighets-grenser,
|
||||||
|
uttalt:** en overstyrt montering er UPINNET ved konstruksjon (operatøren navnga den), så gaten
|
||||||
|
kjørt med `--bundle-root` måler ikke det pinnede korpuset og sier det ikke — pinnen er default,
|
||||||
|
ikke et påbud; digesten dekker hver fil, så en `.DS_Store` som dukker opp i kopien er et AVVIK
|
||||||
|
(MÅLT: null `.DS_Store` og null symlenker i alle fire basene da kopien ble tatt); og kopien ble
|
||||||
|
tatt 18.09 fra vegnormals mappe KUN ved lesing — kildens mtimer er uendret.
|
||||||
- **STATE.md er local-only** (gitignored). Voyage session-state er efemert; STATE.md er kanonisk kontinuitet.
|
- **STATE.md er local-only** (gitignored). Voyage session-state er efemert; STATE.md er kanonisk kontinuitet.
|
||||||
- Prosess: Voyage-plugin (`/trekbrief → /trekplan → /trekexecute → /trekreview`) per større fase.
|
- Prosess: Voyage-plugin (`/trekbrief → /trekplan → /trekexecute → /trekreview`) per større fase.
|
||||||
|
|
||||||
|
|
|
||||||
14
README.md
14
README.md
|
|
@ -1040,3 +1040,17 @@ uv sync
|
||||||
uv run pytest
|
uv run pytest
|
||||||
uv run ruff check .
|
uv run ruff check .
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Frozen knowledge bases
|
||||||
|
|
||||||
|
The measurements that read a delivered corpus (the v1 gate's rows 6–7, the stress judge, and four
|
||||||
|
corpus tests) read a **frozen copy** under `~/corpora/po-frosne-bundles/<name>-<short sha256>/`,
|
||||||
|
outside every repository, pinned by `src/portfolio_optimiser/frozen_bundles.json`. The bundles
|
||||||
|
themselves are never committed here; only the pin is. A copy that no longer matches its pin fails
|
||||||
|
loudly (`frosset bundle … avviker fra pin`), and a copy that is absent is `IKKE MÅLT`, never green.
|
||||||
|
|
||||||
|
**Renewing a copy is a decision, not maintenance: a new copy and a new pin go in the SAME commit.**
|
||||||
|
Re-copy the base, recompute with `portfolio_optimiser.frozen_bundles.digest_bundle`, rename the
|
||||||
|
directory to carry the new short digest, and update `frozen_bundles.json` in that same change.
|
||||||
|
`--bundle-root` (or `PORTFOLIO_VEGNORMAL_ROOT`) stays as an explicit, **unpinned** live mount — the
|
||||||
|
way to look at a fresh corpus before deciding to refreeze.
|
||||||
|
|
|
||||||
|
|
@ -760,11 +760,11 @@ def _own_proposals(
|
||||||
|
|
||||||
|
|
||||||
def measure_stress(
|
def measure_stress(
|
||||||
evidence: Mapping[str, Any], repo_root: Path, stress_root: Path, bundle_root: Path
|
evidence: Mapping[str, Any], repo_root: Path, stress_root: Path, bundle_root: Path | None
|
||||||
) -> StressMeasure:
|
) -> StressMeasure:
|
||||||
"""Re-judge every listed outbox with the current judge. Any run that cannot be judged makes the
|
"""Re-judge every listed outbox with the current judge. Any run that cannot be judged makes the
|
||||||
whole measurement absent — a partial one would carry the wrong denominator."""
|
whole measurement absent — a partial one would carry the wrong denominator."""
|
||||||
from portfolio_optimiser import stress
|
from portfolio_optimiser import frozen_bundles, stress
|
||||||
from portfolio_optimiser.mandate import load_mandate
|
from portfolio_optimiser.mandate import load_mandate
|
||||||
|
|
||||||
verdicts = []
|
verdicts = []
|
||||||
|
|
@ -779,8 +779,11 @@ def measure_stress(
|
||||||
chosen = [d for d in declared if wanted in (None, d["name"], d["bundle_id"])]
|
chosen = [d for d in declared if wanted in (None, d["name"], d["bundle_id"])]
|
||||||
if len(chosen) != 1:
|
if len(chosen) != 1:
|
||||||
return StressMeasure(where=str(stress_root), missing=f"{context}: base ikke entydig")
|
return StressMeasure(where=str(stress_root), missing=f"{context}: base ikke entydig")
|
||||||
base = bundle_root / chosen[0]["name"]
|
|
||||||
try:
|
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.
|
||||||
|
base = frozen_bundles.bundle_dir(chosen[0]["name"], override=bundle_root)
|
||||||
verdicts.append(
|
verdicts.append(
|
||||||
stress.score_context_set(
|
stress.score_context_set(
|
||||||
context,
|
context,
|
||||||
|
|
@ -933,10 +936,7 @@ def evaluate(
|
||||||
evidence,
|
evidence,
|
||||||
repo_root,
|
repo_root,
|
||||||
stress_root or repo_root / evidence["root"],
|
stress_root or repo_root / evidence["root"],
|
||||||
bundle_root
|
bundle_root,
|
||||||
or Path(
|
|
||||||
os.environ.get("PORTFOLIO_VEGNORMAL_ROOT", "~/repos/vegnormal-okf/build/ferdig")
|
|
||||||
).expanduser(),
|
|
||||||
)
|
)
|
||||||
return [
|
return [
|
||||||
score_rounds(rounds_dir, required, ai),
|
score_rounds(rounds_dir, required, ai),
|
||||||
|
|
@ -999,7 +999,10 @@ def main(argv: Sequence[str] | None = None) -> int:
|
||||||
"--stress-root", default=None, help="utboks-roten for stressrunden rad 6-7 dømmer"
|
"--stress-root", default=None, help="utboks-roten for stressrunden rad 6-7 dømmer"
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--bundle-root", default=None, help="der stressrundens kunnskapsbaser er montert"
|
"--bundle-root",
|
||||||
|
default=None,
|
||||||
|
help="en EKSPLISITT, UPINNET levende montering; uten den svarer den frosne kopien og "
|
||||||
|
"sha256-pinnen verifiseres",
|
||||||
)
|
)
|
||||||
parser.add_argument("--json", action="store_true", help="maskinlesbar output")
|
parser.add_argument("--json", action="store_true", help="maskinlesbar output")
|
||||||
args = parser.parse_args(argv)
|
args = parser.parse_args(argv)
|
||||||
|
|
|
||||||
27
src/portfolio_optimiser/frozen_bundles.json
Normal file
27
src/portfolio_optimiser/frozen_bundles.json
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"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": {
|
||||||
|
"n100-2023": {
|
||||||
|
"directory": "n100-2023-69c62e5c5414",
|
||||||
|
"sha256": "69c62e5c541455fd4bfafbfde0a45b604ec3788532b61441e1445494c9e70bea",
|
||||||
|
"files": 450
|
||||||
|
},
|
||||||
|
"n200-2024": {
|
||||||
|
"directory": "n200-2024-9950f2f4cc8c",
|
||||||
|
"sha256": "9950f2f4cc8ccec8605c47035e8e9de3ba4028f4a4091c95a64953242a69137d",
|
||||||
|
"files": 1137
|
||||||
|
},
|
||||||
|
"n500-2024": {
|
||||||
|
"directory": "n500-2024-c68f18d6375f",
|
||||||
|
"sha256": "c68f18d6375f4461710c9cea54b2e8335d1b6461446364c9bfaf764fb88efdcb",
|
||||||
|
"files": 274
|
||||||
|
},
|
||||||
|
"r761-2025": {
|
||||||
|
"directory": "r761-2025-58e1ecca6007",
|
||||||
|
"sha256": "58e1ecca60075d77f3ca4776ee7e76466ef2631566099f37856eeef96620ab02",
|
||||||
|
"files": 5564
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
134
src/portfolio_optimiser/frozen_bundles.py
Normal file
134
src/portfolio_optimiser/frozen_bundles.py
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
"""The frozen knowledge bases the measurements read — a copy this repository PINS, never another
|
||||||
|
repository's live build directory.
|
||||||
|
|
||||||
|
Measured 2026-09-17 17:43: ``vegnormal-okf`` rebuilt ``build/ferdig/r761-2025`` while this
|
||||||
|
repository's v1 gate and four corpus tests 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 *instability*: two projects shared a
|
||||||
|
directory neither owns, so what this repository MEASURES could move without a commit here.
|
||||||
|
|
||||||
|
A copy alone would only push that directory one move away, so the copy comes with a pin: the
|
||||||
|
sha256 of the bundle's files in deterministic order, tracked in ``frozen_bundles.json``. Three
|
||||||
|
states, separated by construction:
|
||||||
|
|
||||||
|
* the copy MATCHES the pin -> it resolves, and that is the only path that measures anything;
|
||||||
|
* the copy is GONE -> :class:`FrozenBundleMissing`, an ``OSError``, so the gate's existing
|
||||||
|
``except OSError`` says IKKE MÅLT and fails the exit code exactly as before, and the corpus
|
||||||
|
tests SKIP (MAJOR-3's ceiling: a hard error would break ``uv run pytest`` in the handover
|
||||||
|
archive, where no corpus is mounted);
|
||||||
|
* the copy DIFFERS -> :class:`FrozenBundleDrift`, a ``ValueError``, loud and named and
|
||||||
|
NEVER a skip. A drifted copy is not an unreadable measurement, it is a measurement of the wrong
|
||||||
|
corpus — the one state that produces a silently wrong number.
|
||||||
|
|
||||||
|
The two classes are deliberately unrelated: a caller that catches "missing" in order to skip must
|
||||||
|
not swallow "drift".
|
||||||
|
|
||||||
|
The bundles themselves are NEVER committed here — vegnormal corpora must not reach a public
|
||||||
|
remote. Only the pin is tracked. Renewing a copy is a DECISION, not maintenance: a new copy and a
|
||||||
|
new pin in the SAME commit (see README).
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Mapping
|
||||||
|
|
||||||
|
#: The pin this repository tracks: path fragment + sha256 + file count, one entry per base.
|
||||||
|
PIN_FILE = Path(__file__).with_name("frozen_bundles.json")
|
||||||
|
#: Where the frozen copies live, unless the environment says otherwise. OUTSIDE both repositories:
|
||||||
|
#: inside either one, the copy is a build artefact of a project that did not make it.
|
||||||
|
DEFAULT_STORE = "~/corpora/po-frosne-bundles"
|
||||||
|
#: Moves the store on a machine that keeps its corpora elsewhere. Read at CALL time.
|
||||||
|
STORE_ENV = "PORTFOLIO_FROZEN_BUNDLES"
|
||||||
|
#: The operator's explicit, UNPINNED escape hatch: a live mount, named on purpose. Kept because a
|
||||||
|
#: gate that cannot be pointed at a fresh corpus cannot be used to decide whether to refreeze.
|
||||||
|
OVERRIDE_ENV = "PORTFOLIO_VEGNORMAL_ROOT"
|
||||||
|
#: How much of the digest the directory name carries, so a stale copy is visible in ``ls``.
|
||||||
|
SHORT = 12
|
||||||
|
|
||||||
|
|
||||||
|
class FrozenBundleMissing(FileNotFoundError):
|
||||||
|
"""The pinned copy is not on this machine. Never green; never confused with drift."""
|
||||||
|
|
||||||
|
|
||||||
|
class FrozenBundleDrift(ValueError):
|
||||||
|
"""The copy on disk is not the copy that was pinned."""
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class Pin:
|
||||||
|
name: str
|
||||||
|
directory: str
|
||||||
|
sha256: str
|
||||||
|
files: int
|
||||||
|
|
||||||
|
|
||||||
|
def store_root(store: Path | str | None = None) -> Path:
|
||||||
|
return Path(store or os.environ.get(STORE_ENV) or DEFAULT_STORE).expanduser()
|
||||||
|
|
||||||
|
|
||||||
|
def load_pins(path: Path | None = None) -> dict[str, Pin]:
|
||||||
|
data = json.loads((path or PIN_FILE).read_text(encoding="utf-8"))
|
||||||
|
return {
|
||||||
|
name: Pin(name, spec["directory"], spec["sha256"], int(spec["files"]))
|
||||||
|
for name, spec in data["bundles"].items()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def digest_bundle(root: Path) -> tuple[str, int]:
|
||||||
|
"""(sha256 over every file, file count). Deterministic: sorted by the bundle-relative POSIX
|
||||||
|
path, and the NAME is hashed alongside the bytes — without it a corpus reshuffled under the
|
||||||
|
same bytes would pin clean."""
|
||||||
|
entries = sorted((p.relative_to(root).as_posix(), p) for p in root.rglob("*") if p.is_file())
|
||||||
|
outer = hashlib.sha256()
|
||||||
|
for rel, path in entries:
|
||||||
|
outer.update(rel.encode("utf-8"))
|
||||||
|
outer.update(b"\0")
|
||||||
|
outer.update(hashlib.sha256(path.read_bytes()).hexdigest().encode("ascii"))
|
||||||
|
outer.update(b"\n")
|
||||||
|
return outer.hexdigest(), len(entries)
|
||||||
|
|
||||||
|
|
||||||
|
def bundle_dir(
|
||||||
|
name: str,
|
||||||
|
*,
|
||||||
|
override: Path | str | None = None,
|
||||||
|
store: Path | str | None = None,
|
||||||
|
pins: Mapping[str, Pin] | None = None,
|
||||||
|
) -> Path:
|
||||||
|
"""The directory a measurement reads for the base called ``name``.
|
||||||
|
|
||||||
|
``override`` (or ``PORTFOLIO_VEGNORMAL_ROOT``) is the operator's explicit live mount and is
|
||||||
|
NOT verified — they named it. Without one, the frozen store answers and the pin is checked.
|
||||||
|
"""
|
||||||
|
named = override if override is not None else os.environ.get(OVERRIDE_ENV) or None
|
||||||
|
if named is not None:
|
||||||
|
base = Path(named).expanduser() / name
|
||||||
|
if not base.is_dir():
|
||||||
|
raise FrozenBundleMissing(f"knowledge base {name!r} is not mounted under {named}")
|
||||||
|
return base
|
||||||
|
|
||||||
|
pinned = dict(pins) if pins is not None else load_pins()
|
||||||
|
if name not in pinned:
|
||||||
|
raise FrozenBundleMissing(
|
||||||
|
f"{name!r} is not pinned in {PIN_FILE.name} (pinned: {', '.join(sorted(pinned))})"
|
||||||
|
)
|
||||||
|
pin = pinned[name]
|
||||||
|
base = store_root(store) / pin.directory
|
||||||
|
if not base.is_dir():
|
||||||
|
raise FrozenBundleMissing(
|
||||||
|
f"frozen knowledge base {name!r} is not at {base} "
|
||||||
|
f"(set {STORE_ENV}, or refreeze: new copy + new pin in the same commit)"
|
||||||
|
)
|
||||||
|
actual, files = digest_bundle(base)
|
||||||
|
if actual != pin.sha256:
|
||||||
|
raise FrozenBundleDrift(
|
||||||
|
f"frosset bundle {name!r} avviker fra pin: pinnet {pin.sha256[:SHORT]} "
|
||||||
|
f"({pin.files} filer), på disk {actual[:SHORT]} ({files} filer) i {base}. "
|
||||||
|
"En kopi fornyes BEVISST: ny kopi + ny pin i samme commit."
|
||||||
|
)
|
||||||
|
return base
|
||||||
|
|
@ -67,20 +67,19 @@ from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
from collections.abc import Mapping, Sequence
|
from collections.abc import Mapping, Sequence
|
||||||
from dataclasses import asdict, dataclass
|
from dataclasses import asdict, dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from portfolio_optimiser import okf
|
from portfolio_optimiser import frozen_bundles, okf
|
||||||
from portfolio_optimiser.mandate import Mandate, load_mandate
|
from portfolio_optimiser.mandate import Mandate, load_mandate
|
||||||
from portfolio_optimiser.validator import classify_codes, rejection_stage
|
from portfolio_optimiser.validator import classify_codes, rejection_stage
|
||||||
|
|
||||||
#: Where the vegnormal bases are mounted, unless ``--bundle-root`` says otherwise. Read at CALL
|
#: Where a base is read from: the FROZEN store this repository pins, resolved at CALL time (the
|
||||||
#: time (the ``shared_root()`` idiom) so a test or an operator can move the mount without a reimport.
|
#: ``shared_root()`` idiom). ``--bundle-root`` stays as the operator's explicit, unpinned live
|
||||||
_DEFAULT_BUNDLE_ROOT = "~/repos/vegnormal-okf/build/ferdig"
|
#: mount. See ``frozen_bundles`` for why a shared build directory is not read directly any more.
|
||||||
|
|
||||||
|
|
||||||
class EmptyMeasurement(RuntimeError):
|
class EmptyMeasurement(RuntimeError):
|
||||||
|
|
@ -604,8 +603,9 @@ def main(argv: list[str] | None = None) -> int:
|
||||||
parser.add_argument("--run-id", required=True)
|
parser.add_argument("--run-id", required=True)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--bundle-root",
|
"--bundle-root",
|
||||||
default=os.environ.get("PORTFOLIO_VEGNORMAL_ROOT", _DEFAULT_BUNDLE_ROOT),
|
default=None,
|
||||||
help="directory the set's bundle.txt name is mounted under",
|
help="an explicit, UNPINNED live mount to read the set's bundle.txt name under; without "
|
||||||
|
"it the frozen store answers and its sha256 pin is verified",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--bundle",
|
"--bundle",
|
||||||
|
|
@ -638,7 +638,11 @@ def main(argv: list[str] | None = None) -> int:
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
return 1
|
return 1
|
||||||
base = Path(args.bundle_root).expanduser() / chosen["name"]
|
try:
|
||||||
|
base = frozen_bundles.bundle_dir(chosen["name"], override=args.bundle_root)
|
||||||
|
except (frozen_bundles.FrozenBundleMissing, frozen_bundles.FrozenBundleDrift) as exc:
|
||||||
|
print(f"stress refused: {exc}", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
try:
|
try:
|
||||||
verdict = score_context_set(
|
verdict = score_context_set(
|
||||||
|
|
|
||||||
|
|
@ -39,13 +39,12 @@ keys on. Rule U itself is UNCHANGED, and its known-positive is still red.
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
|
|
||||||
from portfolio_optimiser import okf
|
from portfolio_optimiser import frozen_bundles, okf
|
||||||
from portfolio_optimiser.ir import CostBaseline, CostBaselineLine
|
from portfolio_optimiser.ir import CostBaseline, CostBaselineLine
|
||||||
from portfolio_optimiser.mandate import load_mandate
|
from portfolio_optimiser.mandate import load_mandate
|
||||||
from portfolio_optimiser.stress import read_bundle_declarations
|
from portfolio_optimiser.stress import read_bundle_declarations
|
||||||
|
|
@ -53,10 +52,9 @@ from portfolio_optimiser.stress import read_bundle_declarations
|
||||||
_REPO_ROOT = Path(__file__).resolve().parent.parent
|
_REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||||
_CONTEXT_ROOT = _REPO_ROOT / "contexts"
|
_CONTEXT_ROOT = _REPO_ROOT / "contexts"
|
||||||
|
|
||||||
#: Where the vegnormal bases are mounted. A SYMBOLIC name in ``bundle.txt`` is resolved against
|
#: A SYMBOLIC name in ``bundle.txt`` is resolved against the frozen store, never an absolute path
|
||||||
#: this, never an absolute path in the set: this repository is published, and an absolute path
|
#: in the set: this repository is published, and an absolute path would pin a set to one machine's
|
||||||
#: would pin a set to one machine's home directory and ride out in the handover archive.
|
#: home directory and ride out in the handover archive.
|
||||||
_DEFAULT_BUNDLE_ROOT = Path.home() / "repos" / "vegnormal-okf" / "build" / "ferdig"
|
|
||||||
|
|
||||||
#: The concept types the four bases declare. ``index.md`` carries none of them — it is navigation,
|
#: The concept types the four bases declare. ``index.md`` carries none of them — it is navigation,
|
||||||
#: not content — which is why the file count and the concept count differ.
|
#: not content — which is why the file count and the concept count differ.
|
||||||
|
|
@ -107,8 +105,16 @@ def own_frontmatter(path: Path) -> dict[str, str]:
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
def _bundle_root() -> Path:
|
def _bundle_dir(name: str) -> Path:
|
||||||
return Path(os.environ.get("PORTFOLIO_VEGNORMAL_ROOT", str(_DEFAULT_BUNDLE_ROOT)))
|
"""The FROZEN copy this repository pins, resolved at call time.
|
||||||
|
|
||||||
|
Absence SKIPS (MAJOR-3's ceiling: no corpus is mounted in the handover archive), drift is
|
||||||
|
allowed to propagate and FAIL — a measurement of the wrong corpus is not a missing one.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return frozen_bundles.bundle_dir(name)
|
||||||
|
except frozen_bundles.FrozenBundleMissing as exc:
|
||||||
|
pytest.skip(str(exc))
|
||||||
|
|
||||||
|
|
||||||
#: The ONE reader, imported from production rather than copied here (P17b). It used to be a
|
#: The ONE reader, imported from production rather than copied here (P17b). It used to be a
|
||||||
|
|
@ -167,13 +173,7 @@ _SET_IDS = [p.name for p in _SETS]
|
||||||
|
|
||||||
|
|
||||||
def _require_base(declared: dict[str, str]) -> Path:
|
def _require_base(declared: dict[str, str]) -> Path:
|
||||||
root = _bundle_root()
|
return _bundle_dir(declared["name"])
|
||||||
base = root / declared["name"]
|
|
||||||
if not base.is_dir():
|
|
||||||
pytest.skip(
|
|
||||||
f"knowledge base {declared['name']!r} not mounted under {root} (PORTFOLIO_VEGNORMAL_ROOT)"
|
|
||||||
)
|
|
||||||
return base
|
|
||||||
|
|
||||||
|
|
||||||
def _base_by_approach(set_dir: Path) -> dict[str, Path]:
|
def _base_by_approach(set_dir: Path) -> dict[str, Path]:
|
||||||
|
|
|
||||||
360
tests/test_frozen_bundles_loadbearing.py
Normal file
360
tests/test_frozen_bundles_loadbearing.py
Normal file
|
|
@ -0,0 +1,360 @@
|
||||||
|
"""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")
|
||||||
|
|
@ -33,11 +33,12 @@ floor, and the composition seam run over synthetic input and are UNCONDITIONAL.
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from portfolio_optimiser import frozen_bundles
|
||||||
|
|
||||||
from portfolio_optimiser import okf
|
from portfolio_optimiser import okf
|
||||||
from portfolio_optimiser.ir import AffectedItem, SavingsProposal
|
from portfolio_optimiser.ir import AffectedItem, SavingsProposal
|
||||||
from portfolio_optimiser.validator import (
|
from portfolio_optimiser.validator import (
|
||||||
|
|
@ -48,7 +49,6 @@ from portfolio_optimiser.validator import (
|
||||||
validate_proposal,
|
validate_proposal,
|
||||||
)
|
)
|
||||||
|
|
||||||
_DEFAULT_BUNDLE_ROOT = Path.home() / "repos" / "vegnormal-okf" / "build" / "ferdig"
|
|
||||||
_A4 = Path(
|
_A4 = Path(
|
||||||
"scratchpad/p14-stress/kontrakt-sorasen-2027/"
|
"scratchpad/p14-stress/kontrakt-sorasen-2027/"
|
||||||
"kontrakt-sorasen-2027-01-a4-indeksregulering-proposal.json"
|
"kontrakt-sorasen-2027-01-a4-indeksregulering-proposal.json"
|
||||||
|
|
@ -56,10 +56,15 @@ _A4 = Path(
|
||||||
|
|
||||||
|
|
||||||
def _base(name: str) -> Path:
|
def _base(name: str) -> Path:
|
||||||
root = Path(os.environ.get("PORTFOLIO_VEGNORMAL_ROOT", str(_DEFAULT_BUNDLE_ROOT)))
|
"""The FROZEN copy this repository pins, resolved at call time.
|
||||||
if not (root / name).is_dir():
|
|
||||||
pytest.skip(f"knowledge base {name!r} is not mounted under {root}")
|
Absence SKIPS (MAJOR-3's ceiling: no corpus is mounted in the handover archive), drift is
|
||||||
return root / name
|
allowed to propagate and FAIL — a measurement of the wrong corpus is not a missing one.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return frozen_bundles.bundle_dir(name)
|
||||||
|
except frozen_bundles.FrozenBundleMissing as exc:
|
||||||
|
pytest.skip(str(exc))
|
||||||
|
|
||||||
|
|
||||||
def _grounding_over(name: str) -> Grounding:
|
def _grounding_over(name: str) -> Grounding:
|
||||||
|
|
|
||||||
|
|
@ -34,33 +34,29 @@ run over a synthetic base, so this file can never be silently absent in full.
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from portfolio_optimiser import okf
|
from portfolio_optimiser import frozen_bundles, okf
|
||||||
from portfolio_optimiser.explore import navigator_tools
|
from portfolio_optimiser.explore import navigator_tools
|
||||||
|
|
||||||
_DEFAULT_BUNDLE_ROOT = Path.home() / "repos" / "vegnormal-okf" / "build" / "ferdig"
|
|
||||||
|
|
||||||
#: S7a-3's ceiling, restated here rather than imported: a gate that imported the implementation's
|
#: S7a-3's ceiling, restated here rather than imported: a gate that imported the implementation's
|
||||||
#: own budget would move with it, and raising the budget is exactly the regression it guards.
|
#: own budget would move with it, and raising the budget is exactly the regression it guards.
|
||||||
_CEILING_CHARS = 1_500
|
_CEILING_CHARS = 1_500
|
||||||
|
|
||||||
|
|
||||||
def _root() -> Path:
|
|
||||||
return Path(os.environ.get("PORTFOLIO_VEGNORMAL_ROOT", str(_DEFAULT_BUNDLE_ROOT)))
|
|
||||||
|
|
||||||
|
|
||||||
def _delivered(name: str) -> Path:
|
def _delivered(name: str) -> Path:
|
||||||
base = _root() / name
|
"""The FROZEN copy this repository pins, resolved at call time.
|
||||||
if not base.is_dir():
|
|
||||||
pytest.skip(
|
Absence SKIPS (MAJOR-3's ceiling: no corpus is mounted in the handover archive), drift is
|
||||||
f"knowledge base {name!r} is not mounted under {_root()} (PORTFOLIO_VEGNORMAL_ROOT)"
|
allowed to propagate and FAIL — a measurement of the wrong corpus is not a missing one.
|
||||||
)
|
"""
|
||||||
return base
|
try:
|
||||||
|
return frozen_bundles.bundle_dir(name)
|
||||||
|
except frozen_bundles.FrozenBundleMissing as exc:
|
||||||
|
pytest.skip(str(exc))
|
||||||
|
|
||||||
|
|
||||||
def _tools(bundle_dir: Path) -> dict[str, Any]:
|
def _tools(bundle_dir: Path) -> dict[str, Any]:
|
||||||
|
|
|
||||||
|
|
@ -55,11 +55,12 @@ What each arm pins:
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from portfolio_optimiser import frozen_bundles
|
||||||
|
|
||||||
from portfolio_optimiser import okf
|
from portfolio_optimiser import okf
|
||||||
from portfolio_optimiser.generate import _grounding_text
|
from portfolio_optimiser.generate import _grounding_text
|
||||||
from portfolio_optimiser.ir import CostBaseline, SavingsProposal
|
from portfolio_optimiser.ir import CostBaseline, SavingsProposal
|
||||||
|
|
@ -73,16 +74,20 @@ from portfolio_optimiser.validator import (
|
||||||
validate_proposal,
|
validate_proposal,
|
||||||
)
|
)
|
||||||
|
|
||||||
_DEFAULT_BUNDLE_ROOT = Path.home() / "repos" / "vegnormal-okf" / "build" / "ferdig"
|
|
||||||
_ROUND3 = Path("scratchpad/p19-stress/tunnel-hauglia-2027")
|
_ROUND3 = Path("scratchpad/p19-stress/tunnel-hauglia-2027")
|
||||||
_P17B = Path("scratchpad/p17b-multibase/lindaas")
|
_P17B = Path("scratchpad/p17b-multibase/lindaas")
|
||||||
|
|
||||||
|
|
||||||
def _base(name: str) -> Path:
|
def _base(name: str) -> Path:
|
||||||
root = Path(os.environ.get("PORTFOLIO_VEGNORMAL_ROOT", str(_DEFAULT_BUNDLE_ROOT)))
|
"""The FROZEN copy this repository pins, resolved at call time.
|
||||||
if not (root / name).is_dir():
|
|
||||||
pytest.skip(f"knowledge base {name!r} is not mounted under {root}")
|
Absence SKIPS (MAJOR-3's ceiling: no corpus is mounted in the handover archive), drift is
|
||||||
return root / name
|
allowed to propagate and FAIL — a measurement of the wrong corpus is not a missing one.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return frozen_bundles.bundle_dir(name)
|
||||||
|
except frozen_bundles.FrozenBundleMissing as exc:
|
||||||
|
pytest.skip(str(exc))
|
||||||
|
|
||||||
|
|
||||||
def _grounding_over(name: str) -> Grounding:
|
def _grounding_over(name: str) -> Grounding:
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ from typing import Any
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from portfolio_optimiser import frozen_bundles
|
||||||
from portfolio_optimiser.evals import v1_gate as gate
|
from portfolio_optimiser.evals import v1_gate as gate
|
||||||
from portfolio_optimiser.validator import UNSUPPORTED_REASON
|
from portfolio_optimiser.validator import UNSUPPORTED_REASON
|
||||||
|
|
||||||
|
|
@ -678,12 +679,12 @@ def test_row6_measures_the_stress_outboxes_when_they_exist(tmp_path: Path) -> No
|
||||||
"""Against the real artefacts when this machine has them; otherwise the absence is named."""
|
"""Against the real artefacts when this machine has them; otherwise the absence is named."""
|
||||||
evidence = _CONFIG["stress_evidence"]
|
evidence = _CONFIG["stress_evidence"]
|
||||||
root = _REPO / evidence["root"]
|
root = _REPO / evidence["root"]
|
||||||
bundles = Path("~/repos/vegnormal-okf/build/ferdig").expanduser()
|
bundles = frozen_bundles.store_root()
|
||||||
if not root.is_dir() or not bundles.is_dir():
|
if not root.is_dir() or not bundles.is_dir():
|
||||||
m = gate.measure_stress(evidence, _REPO, tmp_path / "absent", bundles)
|
m = gate.measure_stress(evidence, _REPO, tmp_path / "absent", None)
|
||||||
assert m.missing and m.validated == 0
|
assert m.missing and m.validated == 0
|
||||||
pytest.skip(f"stress artefacts not mounted ({root}, {bundles})")
|
pytest.skip(f"stress artefacts not mounted ({root}, {bundles})")
|
||||||
m = gate.measure_stress(evidence, _REPO, root, bundles)
|
m = gate.measure_stress(evidence, _REPO, root, None)
|
||||||
if m.missing:
|
if m.missing:
|
||||||
# The mount belongs to another repository and can be mid-rebuild; the gate then says
|
# The mount belongs to another repository and can be mid-rebuild; the gate then says
|
||||||
# "ikke målt", which test_row6_missing_artefacts_are_never_zero already pins.
|
# "ikke målt", which test_row6_missing_artefacts_are_never_zero already pins.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue