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

@ -1,8 +1,9 @@
"""P19 DEL B — a "cost code" must have a FORM when the input offers forms.
**The measured defect.** P18's round 2 ended with two ``validated`` proposals whose
``affected_item`` codes were ordinary words out of a road standard's prose:
``impulsventilator`` (in 4 of 270 N500 documents) and ``bituminøst bærelag`` (4 of 1 133 N200).
``affected_item`` codes were ordinary words out of a standard's prose, in 4 of the 270 and 4 of
the 1 133 documents of the two bases they came from. This file names them by two stand-ins of the
same kind, ``nødstrømsaggregat`` and ``redundant kjøling``.
Both are GROUNDED in P7's sense — they appear verbatim in the input, which is all that stage asks —
and neither is INERT in P18/B1's sense, because neither is anywhere near the 5 % document share.
They are simply not identifiers of a cost line. The gate had no stage that could say so.
@ -17,14 +18,14 @@ be answered in a form it does not use, and refusing there would be a rule about
about grounding — exactly what ``_ground_against_input``'s own docstring refuses for the stage it
sits inside.
**B1 — the third and fourth forms, transcribed from a measurement.** R761's requirement numbers are
bare dotted numbers (``12.1``, ``52.11``); ALL SIX ``ref`` values in
``contexts/kontrakt-sorasen-2027/fasit.json`` are of that shape and NEITHER pre-P19 form matched
one, so r761's whole offer was **3 identifiers over 6.5 MB**. Measured after: **2 332**. And the
first form was WIDENED in the same pass, because P19/B2 made these forms decide ``prose`` as well
as count an offer: this repo's own ``ENERGI-TOTAL-EL`` matched neither, so the classifier called a
real cost code prose and the new gate refused it. A gate may only be wrong in the direction that
admits too much.
**B1 — the third and fourth forms, transcribed from a measurement.** A process catalogue's numbers
are bare dotted numbers (``12.1``, ``52.11``); ALL SIX ``ref`` values in
``contexts/driftsavtale-2027/fasit.json`` are of that shape, and NEITHER pre-P19 form matched one:
the catalogue measured then offered **3 identifiers over 6.5 MB**. Measured after: **2 332**. And
the first form was WIDENED in the same pass, because P19/B2 made these forms decide ``prose`` as
well as count an offer: this repo's own ``ENERGI-TOTAL-EL`` matched neither, so the classifier
called a real cost code prose and the new gate refused it. A gate may only be wrong in the direction
that admits too much.
**Two exemptions, both load-bearing:**
@ -32,7 +33,7 @@ admits too much.
this project, and the weaker stage must not overrule the stronger falsifier (the sentence
``_grounding_text`` already carries about its third source). A derived schedule whose codes are
bare section numbers would otherwise be refused wholesale by the gate meant to protect it;
* ``has_identifier_form`` FULL-matches. ``impulsventilator 12.1`` carrying a process number does
* ``has_identifier_form`` FULL-matches. ``nødstrømsaggregat 12.1`` carrying a process number does
not make the word a cost code, and a substring rule would let any prose code smuggle one along.
What each arm pins:
@ -41,8 +42,8 @@ What each arm pins:
reason verbatim into the next attempt, and "ungrounded" alone teaches nothing);
(b) the generality guard: a base offering no identifier form leaves the gate OFF;
(c) an anchored code is exempt — the stronger falsifier wins;
(d) the 26 known negatives: every ``ref`` in all four fasit files still classifies as an identifier,
and so does this repo's own ``ENERGI-TOTAL-EL``;
(d) the 18 known negatives: every ``ref`` in all three fasit files still classifies as an
identifier, and so does this repo's own ``ENERGI-TOTAL-EL``;
(e) B1's known positives and negatives, one by one;
(f) B2: the classification is reported, and it is the SAME classifier the gate uses.
"""
@ -68,12 +69,12 @@ from portfolio_optimiser.validator import (
#: A base that OFFERS identifier forms: eleven documents carrying requirement numbers, one of which
#: also mentions the prose word. Eleven because ``_GROUNDING_MIN_INERT_DOCUMENTS`` is 10 — with
#: fewer, P18/B1's share rule cannot fire at all and this file would be measuring that instead.
_OFFERING = tuple(f"Krav 10.4.3—{i} om ventilasjon i tunnelen." for i in range(1, 12)) + (
"Krav 8.4.2—1 sier at impulsventilator skal dimensjoneres for brannlast.",
_OFFERING = tuple(f"Krav 10.4.3—{i} om ventilasjon i serverrommet." for i in range(1, 12)) + (
"Krav 8.4.2—1 sier at nødstrømsaggregat skal dimensjoneres for full last.",
)
#: The same corpus with every identifier removed — prose only.
_FORMLESS = ("ingen koder her, bare tekst om ventilasjon", "og enda mer prosa om tunnelen")
_FORMLESS = ("ingen koder her, bare tekst om ventilasjon", "og enda mer prosa om serverrommet")
def _proposal(code: str, *, quantity: float = 4.0, unit_cost: float = 250_000.0) -> SavingsProposal:
@ -91,8 +92,8 @@ def _proposal(code: str, *, quantity: float = 4.0, unit_cost: float = 250_000.0)
def test_a_word_from_the_prose_is_not_a_cost_code() -> None:
"""(a) P18's ``impulsventilator``, in the shape that reached ``validated``."""
verdict = validate_proposal(_proposal("impulsventilator"), grounding=Grounding(_OFFERING))
"""(a) P18's ``nødstrømsaggregat``, in the shape that reached ``validated``."""
verdict = validate_proposal(_proposal("nødstrømsaggregat"), grounding=Grounding(_OFFERING))
assert isinstance(verdict, Rejection)
assert "has no identifier form" in verdict.reason
# The DENOMINATOR, not just a complaint: Step 5 feeds this reason into the next attempt.
@ -124,11 +125,11 @@ def test_a_code_the_baseline_carries_is_never_refused_for_its_shape() -> None:
"""(c) Stage 0 has already ruled it a real line; the weaker stage must not overrule it."""
baseline = CostBaseline(
project_id="t",
items={"impulsventilator": CostBaselineLine(quantity=4.0, unit_cost=250_000.0)},
items={"nødstrømsaggregat": CostBaselineLine(quantity=4.0, unit_cost=250_000.0)},
)
assert isinstance(
validate_proposal(
_proposal("impulsventilator"), baseline=baseline, grounding=Grounding(_OFFERING)
_proposal("nødstrømsaggregat"), baseline=baseline, grounding=Grounding(_OFFERING)
),
ValidatedProposal,
)
@ -142,15 +143,15 @@ def test_a_code_the_baseline_carries_is_never_refused_for_its_shape() -> None:
def test_every_fasit_reference_in_every_context_set_is_an_identifier() -> None:
"""(d) Every fasit reference across every context set, with the denominator.
One of them — ``Krav 3.3.2—1_1`` — is why the second form grew an optional ``_<n>`` suffix.
Measured, not anticipated: before that it was the single reference the classifier called prose.
One of them — ``Krav 5.2.2—1_1`` — carries the optional ``_<n>`` suffix the second form grew
when a reference of that shape was measured as the single one the classifier called prose.
**The denominator MOVED 26 -> 32 with P17b's fifth context set**, and it is asserted rather
than dropped for the reason it was written down in the first place: a list comprehension over
``contexts/*/fasit.json`` that quietly found fewer rows would make this arm weaker without
making it red. The six new ones are four ``Krav x.y.z—n`` from n200-2024 and TWO bare
``prosessnr`` from r761-2025 (``12.11``, ``12.12``) — the punctuation-and-digits form B1 added,
now exercised by a fasit and not only by a known-positive.
**The denominator is 18: six references in each of the three example sets**, and it is asserted
rather than dropped for the reason it was written down in the first place: a list comprehension
over ``contexts/*/fasit.json`` that quietly found fewer rows would make this arm weaker without
making it red. Ten are ``Krav x.y.z—n`` from driftskrav-2027 and eight are bare ``prosessnr``
from prosesskatalog-2027 (``12.1``, ``12.11``, ``12.12``, …) — the punctuation-and-digits form
B1 added, exercised by a fasit and not only by a known-positive.
"""
refs = [
concept["ref"]
@ -158,14 +159,24 @@ def test_every_fasit_reference_in_every_context_set_is_an_identifier() -> None:
for row in json.loads(open(path, encoding="utf-8").read())["must_cite"]
for concept in row["concepts"]
]
assert len(refs) == 32, f"denominator moved: {len(refs)}"
assert len(refs) == 18, f"denominator moved: {len(refs)}"
assert [r for r in refs if not has_identifier_form(r)] == []
assert has_identifier_form("ENERGI-TOTAL-EL"), "this repo's own reference cost code"
@pytest.mark.parametrize(
"token",
["12.1", "12.12", "22.1", "52.1", "52.11", "51.1", "65 ASFALTDEKKER", "SHA-01", "B-20-00-00"],
[
"12.1",
"12.12",
"22.1",
"52.1",
"52.11",
"51.1",
"65 LAGRINGSSYSTEMER",
"SHA-01",
"B-20-00-00",
],
)
def test_b1_known_positives(token: str) -> None:
"""(e) The forms the delivered corpora carry."""
@ -179,8 +190,8 @@ def test_b1_known_positives(token: str) -> None:
"250000",
"15.09.2026",
"2026-09-15",
"impulsventilator",
"bituminøst bærelag",
"nødstrømsaggregat",
"redundant kjøling",
"0.70",
],
)
@ -197,9 +208,9 @@ def test_b1_known_negatives(token: str) -> None:
def test_the_classification_is_reported_and_is_the_gates_own() -> None:
"""(f) One classifier, two consumers (kø-(p)): a report that disagreed with the gate about one
proposal would be evidence about nothing."""
forms = classify_codes(["impulsventilator", "Krav 8.4.2—1", "12.1", "ENERGI-TOTAL-EL"])
forms = classify_codes(["nødstrømsaggregat", "Krav 8.4.2—1", "12.1", "ENERGI-TOTAL-EL"])
assert forms == {
"impulsventilator": "prose",
"nødstrømsaggregat": "prose",
"Krav 8.4.2—1": "identifier",
"12.1": "identifier",
"ENERGI-TOTAL-EL": "identifier",