refactor(examples): replace sector-specific example material with generic, fictitious examples

The context sets, the packaged knowledge bases and the example bundles are
replaced by one fictitious example set about IT operations in an invented
organisation: three context sets (serverrom-2027, driftsavtale-2027 and the
two-base drift-og-avtale-2027), two synthetic knowledge bases under
src/portfolio_optimiser/data/kunnskapsbaser and two example bundles under
src/portfolio_optimiser/data/bundles. Numbers, codes and structural values in
tests and fixtures are kept; names, ids and wording change. Dated measurement
documents that only recorded runs on the replaced material are deleted.

Gate figures measured on the new set are not comparable with earlier ones.
The exclusion gate from the previous commit is green: 0 tracked files hit
outside the shared/ subtree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-23 15:04:21 +02:00
commit 37547fe292
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
1147 changed files with 24138 additions and 9503 deletions

View file

@ -32,34 +32,34 @@ _OFFLINE_MODEL_MAP = {
# therefore OMIT ``assumptions`` and carry explicit magnitudes so each ``claimed_saving_nok`` <= P90.
#
# S4.0: every line below quotes a cost line the project ACTUALLY has, verbatim from
# reference_projects.json — the road path now anchors the validator to ``project.cost_items``, so a
# reference_projects.json — the reference path now anchors the validator to ``project.cost_items``, so a
# reply quoting another project's code (which these fixtures used to do) is rejected as a fabricated
# cost line. Verified against reference_projects.json + validator + ir:
# FV42-GSV-E1 01.1 1x850,000 + 05.2 4300x215 Σ=1,774,500 P90=532,350 claimed 200,000
# RV13-RAS-TP 22.4 610x3,850 Σ=2,348,500 P90=704,550 claimed 130,000 (decoy)
# BRU-LAKS-REHAB 01.1 1x620,000 + 87.3 640x980 Σ=1,247,200 P90=374,160 claimed 210,000
# ``measure`` is byte-identical "Reduce scope" for FV42+BRU (measure-match is exact string
# equality, verdicts.py:68) and "Material substitution" for the decoy, so the BRU<->FV42 pair still
# KONTOR-IT-E1 01.1 1x850,000 + 05.2 4300x215 Σ=1,774,500 P90=532,350 claimed 200,000
# NETT-SIKR-TP 22.4 610x3,850 Σ=2,348,500 P90=704,550 claimed 130,000 (decoy)
# ARKIV-LAGR-MIGR 01.1 1x620,000 + 87.3 640x980 Σ=1,247,200 P90=374,160 claimed 210,000
# ``measure`` is byte-identical "Reduce scope" for KONTOR+ARKIV (measure-match is exact string
# equality, verdicts.py:68) and "Material substitution" for the decoy, so the ARKIV<->KONTOR pair still
# overlaps (shared code 01.1 + measure + magnitude bucket) while the decoy does not. 01.1 replaces
# 05.2 as the shared code because it is the only code both projects genuinely carry.
REPLIES = {
"FV42-GSV-E1": (
"KONTOR-IT-E1": (
'{"measure":"Reduce scope","affected_items":['
'{"code":"01.1","quantity":1,"unit_cost":850000},'
'{"code":"05.2","quantity":4300,"unit_cost":215}],"claimed_saving_nok":200000}'
),
"RV13-RAS-TP": (
"NETT-SIKR-TP": (
'{"measure":"Material substitution","affected_items":['
'{"code":"22.4","quantity":610,"unit_cost":3850}],"claimed_saving_nok":130000}'
),
"BRU-LAKS-REHAB": (
"ARKIV-LAGR-MIGR": (
'{"measure":"Reduce scope","affected_items":['
'{"code":"01.1","quantity":1,"unit_cost":620000},'
'{"code":"87.3","quantity":640,"unit_cost":980}],"claimed_saving_nok":210000}'
),
}
_PORTFOLIO_IDS = ["FV42-GSV-E1", "RV13-RAS-TP", "BRU-LAKS-REHAB"]
_PORTFOLIO_IDS = ["KONTOR-IT-E1", "NETT-SIKR-TP", "ARKIV-LAGR-MIGR"]
async def test_a_fanout_returns_one_runresult_per_project(
@ -122,10 +122,10 @@ async def test_a3_rejected_proposal_excluded_from_aggregate(
make_portfolio_client_factory, fresh_store
) -> None:
"""F2 (rejection arm, previously unexercised): one project's claim exceeds its P90, so the
validator REJECTS it (FV42 800000 > P90 444750, <= Σ 1,482,500), while RV13+BRU validate.
validator REJECTS it (KONTOR 800000 > P90 444750, <= Σ 1,482,500), while NETT+ARKIV validate.
rejected_count counts it and the validated-only sum (run.py:241) EXCLUDES its claim. Also the
F1 portfolio seam: provenance.model mirrors the injected client across all N records."""
replies = {**REPLIES, "FV42-GSV-E1": REPLIES["FV42-GSV-E1"].replace("200000", "800000")}
replies = {**REPLIES, "KONTOR-IT-E1": REPLIES["KONTOR-IT-E1"].replace("200000", "800000")}
result = await run_portfolio(
_PORTFOLIO_IDS,
"local",
@ -149,9 +149,9 @@ async def test_b_shared_store_accumulates_and_surfaces_prior_verdict(
) -> None:
"""SC4 (load-bearing, not mere non-emptiness): the ONE shared store accumulates a distinct
verdict per project (3 -> 3 pairwise-distinct ids), and the cross-project ExpeL retrieval
surfaces the STRUCTURAL match — BRU (runs[2]) retrieves FV42's verdict (runs[0]), since
sim(BRU,FV42)=0.60 (shared code 05.2 + identical "Reduce scope" measure + same magnitude
bucket) ranks above the RV13 decoy at sim(BRU,decoy)=0.15. If retrieval ranking breaks, or
surfaces the STRUCTURAL match — ARKIV (runs[2]) retrieves KONTOR's verdict (runs[0]), since
sim(ARKIV,KONTOR)=0.60 (shared code 05.2 + identical "Reduce scope" measure + same magnitude
bucket) ranks above the NETT decoy at sim(ARKIV,decoy)=0.15. If retrieval ranking breaks, or
two proposals collide to a single minted id, this fails — it asserts the specific match, not
store-non-empty."""
result = await run_portfolio(
@ -163,7 +163,7 @@ async def test_b_shared_store_accumulates_and_surfaces_prior_verdict(
assert len(result.store.verdicts) == 3
ids = [v.id for v in result.store.verdicts]
assert len(set(ids)) == 3 # pairwise distinct minted ids (no collision)
# BRU surfaces FV42 (the structural match), ranked above the decoy.
# ARKIV surfaces KONTOR (the structural match), ranked above the decoy.
assert result.runs[2].retrieved[0].id == result.runs[0].verdict.id
@ -173,28 +173,28 @@ async def test_c_execution_state_isolation_is_load_bearing(
"""SC3 (cap-independent automatic detach guard): each project's execution state (the budget
meter) is built FRESH per run, so per-project token_usage is its OWN only. Built on the
Step-1 ``meter=`` seam + Step-3 ``meter_factory``. The factory emits a UNIFORM but VALID
proposal (REPLIES["FV42-GSV-E1"] for every call) so both projects complete — a bare
proposal (REPLIES["KONTOR-IT-E1"] for every call) so both projects complete — a bare
unparseable default would loop the generate fetch to BudgetExceeded instead. Both arms run
every CI, so the detach is encoded automatically (no manual reviewer-revert)."""
from portfolio_optimiser.budget import Budget, TokenMeter
from portfolio_optimiser.reference_domain import load_reference_projects
f = make_client_factory(REPLIES["FV42-GSV-E1"])
rv13 = {p.id: p for p in load_reference_projects()}["RV13-RAS-TP"]
f = make_client_factory(REPLIES["KONTOR-IT-E1"])
nett = {p.id: p for p in load_reference_projects()}["NETT-SIKR-TP"]
# 1. Baseline: RV13 run standalone (its own fresh meter).
# 1. Baseline: NETT run standalone (its own fresh meter).
standalone = await run_project(
"RV13-RAS-TP",
"NETT-SIKR-TP",
"local",
docs_dir=rv13.docs_dir,
verdict_input=rv13.verdict_input,
docs_dir=nett.docs_dir,
verdict_input=nett.verdict_input,
client_factory=f,
)
# 2. Isolated (default, no meter_factory): RV13 as project 1 in the portfolio. Its usage
# 2. Isolated (default, no meter_factory): NETT as project 1 in the portfolio. Its usage
# equals the standalone baseline — independent of project 0. THE load-bearing assertion:
# if run_portfolio shared a meter by default, runs[1] would be cumulative and this breaks.
iso = await run_portfolio(
["FV42-GSV-E1", "RV13-RAS-TP"],
["KONTOR-IT-E1", "NETT-SIKR-TP"],
"local",
store=fresh_store,
client_factory=f,
@ -204,7 +204,7 @@ async def test_c_execution_state_isolation_is_load_bearing(
# (and that the == arm above would redden under sharing).
shared = TokenMeter(Budget(max_tokens=1_000_000, max_rounds=1000))
sh = await run_portfolio(
["FV42-GSV-E1", "RV13-RAS-TP"],
["KONTOR-IT-E1", "NETT-SIKR-TP"],
"local",
client_factory=f,
meter_factory=lambda: shared,
@ -269,7 +269,7 @@ def test_f_no_hardcoded_project_ids_in_src() -> None:
backing the 'config-only' claim. Pattern: test_budget.py:88-99 (src anti-pattern grep)."""
from pathlib import Path
ids = ("FV42-GSV-E1", "RV13-RAS-TP", "BRU-LAKS-REHAB", "SKOLE-VVS-OPPGR")
ids = ("KONTOR-IT-E1", "NETT-SIKR-TP", "ARKIV-LAGR-MIGR", "SKOLE-VVS-OPPGR")
offenders = [
f"{py.name}: {pid}"
for py in Path("src/portfolio_optimiser").rglob("*.py")