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

@ -171,7 +171,7 @@ container, and it is the first release whose artefacts are published alongside i
- The shared expert-reviewer persona's canonical example verdict is worded domain-neutrally - The shared expert-reviewer persona's canonical example verdict is worded domain-neutrally
("i tilsvarende anlegg" rather than "i kontorbygg"), pulled from the upstream commons repository. ("i tilsvarende anlegg" rather than "i kontorbygg"), pulled from the upstream commons repository.
The walkthrough prints that `rationale` verbatim, so the wording was a building-type justification The walkthrough prints that `rationale` verbatim, so the wording was a building-type justification
read out over a road-lighting project; it could not be fixed downstream, because overriding the read out over an energy project; it could not be fixed downstream, because overriding the
text locally would re-stub the very artifact the shared skill exists to make load-bearing. The text locally would re-stub the very artifact the shared skill exists to make load-bearing. The
`marker` value is byte-unchanged, and the pinned transcript fixture was regenerated against a `marker` value is byte-unchanged, and the pinned transcript fixture was regenerated against a
prediction written before the pull — the printed line is clipped at a fixed width, so the swap prediction written before the pull — the printed line is clipped at a fixed width, so the swap
@ -266,7 +266,7 @@ First tagged release. There is no prior release, so the entries below describe w
Before this, every stage reasoned only about numbers the proposal itself supplied, so an internally Before this, every stage reasoned only about numbers the proposal itself supplied, so an internally
consistent hallucination cleared the whole gate. Validation, never repair: the proposal is consistent hallucination cleared the whole gate. Validation, never repair: the proposal is
rejected, never silently corrected to the baseline. The argument is **optional** (`None` reproduces rejected, never silently corrected to the baseline. The argument is **optional** (`None` reproduces
the earlier behaviour) but both run paths set it — the road path always, the bundle path only when the earlier behaviour) but both run paths set it — the reference path always, the bundle path only when
the bundle ships a `cost-baseline.json`, since a pre-amendment bundle is legitimately unanchored. the bundle ships a `cost-baseline.json`, since a pre-amendment bundle is legitimately unanchored.
A baseline that exists but is malformed raises on both loaders rather than reading as "no A baseline that exists but is malformed raises on both loaders rather than reading as "no
baseline". Method caps are looked up in an injectable `METHOD_CAPS` registry, so a second measure baseline". Method caps are looked up in an injectable `METHOD_CAPS` registry, so a second measure

View file

@ -332,7 +332,7 @@ from portfolio_optimiser.ledger import LedgerEntry, SavingsLedger, to_ore
ledger = SavingsLedger() ledger = SavingsLedger()
ledger.add_realized( ledger.add_realized(
LedgerEntry( LedgerEntry(
project_id="FV42-GSV-E1", project_id="KONTOR-IT-E1",
dimension="rigg", dimension="rigg",
candidate_identity="33fba649cade8529", candidate_identity="33fba649cade8529",
amount_ore=to_ore(40000), amount_ore=to_ore(40000),
@ -593,7 +593,7 @@ when the seam is detached, so the loop cannot silently degrade into theater.
```bash ```bash
# Single-project, offline drill (builds contracts + clients, stops before the first model call): # Single-project, offline drill (builds contracts + clients, stops before the first model call):
uv run python -m portfolio_optimiser.run FV42-GSV-E1 --docs-dir <docs> --bundle-dir <bundle> --live-dry-run uv run python -m portfolio_optimiser.run KONTOR-IT-E1 --docs-dir <docs> --bundle-dir <bundle> --live-dry-run
# Portfolio run with a savings goal checked against an accumulated ledger: # Portfolio run with a savings goal checked against an accumulated ledger:
uv run python -m portfolio_optimiser.run --portfolio --goals goals.json --ledger ledger.json uv run python -m portfolio_optimiser.run --portfolio --goals goals.json --ledger ledger.json
# Read-only value report over an accumulated ledger (human table; add --json for JSON): # Read-only value report over an accumulated ledger (human table; add --json for JSON):
@ -607,7 +607,7 @@ when the seam is detached, so the loop cannot silently degrade into theater.
no outbox artefact, no wiki entry, no verdict. no outbox artefact, no wiki entry, no verdict.
```bash ```bash
uv run python -m portfolio_optimiser.run FV42-GSV-E1 --docs-dir <docs> --bundle-dir <bundle> \ uv run python -m portfolio_optimiser.run KONTOR-IT-E1 --docs-dir <docs> --bundle-dir <bundle> \
--explore "Find the cheapest saving worth testing here" --explore-config exploration.json --explore "Find the cheapest saving worth testing here" --explore-config exploration.json
``` ```
@ -692,7 +692,7 @@ when the seam is detached, so the loop cannot silently degrade into theater.
nobody ever asks. nobody ever asks.
```bash ```bash
uv run python -m portfolio_optimiser.run FV42-GSV-E1 --docs-dir <docs> --bundle-dir <bundle> \ uv run python -m portfolio_optimiser.run KONTOR-IT-E1 --docs-dir <docs> --bundle-dir <bundle> \
--explore "Find the cheapest saving worth testing here" --explore-config exploration.json \ --explore "Find the cheapest saving worth testing here" --explore-config exploration.json \
--plan-review --outbox-dir out --run-id r1 --plan-review --outbox-dir out --run-id r1
``` ```
@ -766,12 +766,12 @@ when the seam is detached, so the loop cannot silently degrade into theater.
```bash ```bash
# day 1 — park the review and exit # day 1 — park the review and exit
uv run python -m portfolio_optimiser.run FV42-GSV-E1 --docs-dir <docs> --bundle-dir <bundle> \ uv run python -m portfolio_optimiser.run KONTOR-IT-E1 --docs-dir <docs> --bundle-dir <bundle> \
--explore "Find the cheapest saving worth testing here" --explore-config exploration.json \ --explore "Find the cheapest saving worth testing here" --explore-config exploration.json \
--checkpoint-dir checkpoints --outbox-dir out --run-id r1 --checkpoint-dir checkpoints --outbox-dir out --run-id r1
# day N — a fresh process, resuming from what is on disk and nothing else # day N — a fresh process, resuming from what is on disk and nothing else
uv run python -m portfolio_optimiser.run FV42-GSV-E1 --docs-dir <docs> --bundle-dir <bundle> \ uv run python -m portfolio_optimiser.run KONTOR-IT-E1 --docs-dir <docs> --bundle-dir <bundle> \
--outbox-dir out --checkpoint-dir checkpoints --review-inbox inbox --resume r1 --outbox-dir out --checkpoint-dir checkpoints --review-inbox inbox --resume r1
``` ```
@ -829,7 +829,7 @@ when the seam is detached, so the loop cannot silently degrade into theater.
- **Deriving the cost baseline from the knowledge base** — `--derive-cost-baseline` (opt-in, - **Deriving the cost baseline from the knowledge base** — `--derive-cost-baseline` (opt-in,
requires `--bundle-dir`). The deterministic validator anchors a proposal's `affected_items` to requires `--bundle-dir`). The deterministic validator anchors a proposal's `affected_items` to
the project's actual cost lines. Normally those come from a hand-written `cost-baseline.json` in the project's actual cost lines. Normally those come from a hand-written `cost-baseline.json` in
the bundle, or — on the road reference path — from the project's own `cost_items`. Neither is the bundle, or — on the reference path — from the project's own `cost_items`. Neither is
available when the bundle was *ingested* from tender documents, so this third route reads the available when the bundle was *ingested* from tender documents, so this third route reads the
priced schedule that is already in the base: one markdown table whose header names a cost code, a priced schedule that is already in the base: one markdown table whose header names a cost code, a
quantity and a unit price. Both table forms the producer emits are read — the pipe tables its quantity and a unit price. Both table forms the producer emits are read — the pipe tables its
@ -879,7 +879,7 @@ when the seam is detached, so the loop cannot silently degrade into theater.
- **An identifier that stands everywhere identifies nothing (P18).** The validator's stage 0b - **An identifier that stands everywhere identifies nothing (P18).** The validator's stage 0b
grounds each proposed cost code in the run's non-model-authored input. Plain containment was not grounds each proposed cost code in the run's non-model-authored input. Plain containment was not
enough: measured on a live run, a proposal put 250 000 NOK on a line coded `R761` — the knowledge enough: measured on a live run, a proposal put 250 000 NOK on a line coded with the knowledge
base's own *name*, carried by all 2 756 of its documents — and the whole gate said `validated`. base's own *name*, carried by all 2 756 of its documents — and the whole gate said `validated`.
A code now grounds only if it is at least 3 characters long **and** appears in fewer than 5 % of A code now grounds only if it is at least 3 characters long **and** appears in fewer than 5 % of
the documents the input is made of, with an absolute floor of 10 documents so the share is never the documents the input is made of, with an absolute floor of 10 documents so the share is never
@ -891,10 +891,10 @@ when the seam is detached, so the loop cannot silently degrade into theater.
can never reach the floor. can never reach the floor.
- **`--docs-dir` is optional when `--bundle-dir` is given (P18).** On the bundle path `docs_dir` is - **`--docs-dir` is optional when `--bundle-dir` is given (P18).** On the bundle path `docs_dir` is
never read — retrieval, the chunk tool and the citation check all live in the road branch — so never read — retrieval, the chunk tool and the citation check all live in the reference branch — so
naming the same directory twice was a requirement for a path that ignores it. Both forms work; naming the same directory twice was a requirement for a path that ignores it. Both forms work;
neither turns retrieval into a substitute for ingesting documents into a knowledge base, and the neither turns retrieval into a substitute for ingesting documents into a knowledge base, and the
road path still requires a real `--docs-dir`. reference path still requires a real `--docs-dir`.
- **Requiring the run to be anchored** — `--require-cost-baseline` (opt-in, requires - **Requiring the run to be anchored** — `--require-cost-baseline` (opt-in, requires
`--bundle-dir`). Without a baseline the validator's stage 0 is skipped, and the run says so on `--bundle-dir`). Without a baseline the validator's stage 0 is skipped, and the run says so on
@ -911,8 +911,8 @@ when the seam is detached, so the loop cannot silently degrade into theater.
``` ```
- **The project's own price schedule** — `--cost-baseline FILE` (requires `--bundle-dir` or - **The project's own price schedule** — `--cost-baseline FILE` (requires `--bundle-dir` or
`--across-bundle`). A knowledge base carries what is REQUIRED, not what things cost: a road `--across-bundle`). A knowledge base carries what is REQUIRED, not what things cost: a
normal, a standard or a regulation has requirements and no amounts, so a run against one has requirements standard or a regulation has requirements and no amounts, so a run against one has
nothing for the validator's stage 0 to reconcile against and that stage is skipped. The price nothing for the validator's stage 0 to reconcile against and that stage is skipped. The price
belongs to the project, and this is where you hand it over: FILE is a `cost-baseline.json` — the belongs to the project, and this is where you hand it over: FILE is a `cost-baseline.json` — the
same `{"project_id": …, "items": {"<code>": {"quantity": …, "unit_cost": …}}}` shape a bundle may same `{"project_id": …, "items": {"<code>": {"quantity": …, "unit_cost": …}}}` shape a bundle may
@ -992,10 +992,11 @@ when the seam is detached, so the loop cannot silently degrade into theater.
`filter` narrows a level instead of paging it: a case-insensitive substring over a document's `filter` narrows a level instead of paging it: a case-insensitive substring over a document's
title and reference number (`req_number` / `prosessnr`) and over a subdirectory's path, answering title and reference number (`req_number` / `prosessnr`) and over a subdirectory's path, answering
with `total_matches` beside `total`. A filter that matches nothing is an answer, never a refusal. with `total_matches` beside `total`. A filter that matches nothing is an answer, never a refusal.
Measured 2026-09-14 over four delivered bases, default listing of the largest level in each: Measured 2026-09-14 over the four requirement and process-catalogue bases used during
`krav/N200` 169 974 → 1 537 characters, `krav/N100` 69 250 → 1 493, R761's root 110 912 → 479 development, default listing of the largest level in each: 169 974 → 1 537 characters,
(its cost was 2 728 *subdirectories*, which is why the window covers both kinds), `krav/N500` 69 250 → 1 493, the process catalogue's root 110 912 → 479 (its cost was 2 728
39 853 → 1 453. The largest single call any caller can now make is ~7 600 characters. *subdirectories*, which is why the window covers both kinds), and 39 853 → 1 453. The largest
single call any caller can now make is ~7 600 characters.
A path the caller **invented** is refused by name rather than failing opaquely: `read_file` on a A path the caller **invented** is refused by name rather than failing opaquely: `read_file` on a
path the base does not hold answers `REFUSED (BundlePathNotFound)` and names the nearest directory path the base does not hold answers `REFUSED (BundlePathNotFound)` and names the nearest directory
@ -1160,7 +1161,7 @@ whether the first deployment works. Gated by `tests/test_handover_package_loadbe
- [Kunnskapsbase for én kjøring](docs/kunnskapsbase-for-en-kjoring.md) *(norsk)* — how to compose - [Kunnskapsbase for én kjøring](docs/kunnskapsbase-for-en-kjoring.md) *(norsk)* — how to compose
the base for ONE specific run: which categories of knowledge follow the project, the domain and the base for ONE specific run: which categories of knowledge follow the project, the domain and
the organisation; a content-type table (owner, delivery form, role in the loop, what happens the organisation; a content-type table (owner, delivery form, role in the loop, what happens
when it is missing); and a worked road project from the commission to a base that passes the when it is missing); and a worked example project from the commission to a base that passes the
dry-run check. Every technical claim is marked verified or assumed. dry-run check. Every technical claim is marked verified or assumed.
- [OKF consumption contracts](docs/okf-konsum-kontrakter.md) — the three cross-repo facts this - [OKF consumption contracts](docs/okf-konsum-kontrakter.md) — the three cross-repo facts this
consumer and the producer are both held to: the falsification threshold, the adjudication consumer and the producer are both held to: the falsification threshold, the adjudication
@ -1187,13 +1188,17 @@ uv run ruff check .
### Frozen knowledge bases ### Frozen knowledge bases
The measurements that read a delivered corpus (the v1 gate's rows 6–7, the stress judge, and four 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>/`, corpus tests) read a **frozen copy** `<name>-<short sha256>/`, pinned by
outside every repository, pinned by `src/portfolio_optimiser/frozen_bundles.json`. The bundles `src/portfolio_optimiser/frozen_bundles.json`. By default the store is the two synthetic example
themselves are never committed here; only the pin is. A copy that no longer matches its pin fails bases that ship with the package, `driftskrav-2027` and `prosesskatalog-2027` (an invented
loudly (`frosset bundle … avviker fra pin`), and a copy that is absent is `IKKE MÅLT`, never green. requirements corpus and process catalogue for "Eksempelvirksomheten", under
`src/portfolio_optimiser/data/kunnskapsbaser/`). To measure against your own corpus, point
`PORTFOLIO_FROZEN_BUNDLES` at your own store and pin it the same way. 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.** **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 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. 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 `--bundle-root` (or `PORTFOLIO_BUNDLE_ROOT`) stays as an explicit, **unpinned** live mount — the
way to look at a fresh corpus before deciding to refreeze. way to look at a fresh corpus before deciding to refreeze.

View file

@ -1,4 +0,0 @@
name: n200-2024
bundle_id: vegnormal-n200-2024
name: r761-2025
bundle_id: vegnormal-r761-2025

View file

@ -1,68 +0,0 @@
{
"project_id": "dekke-og-kontrakt-lindaas-2027",
"must_cite": [
{
"approach_id": "a1-tynnere-forsterkningslag",
"rationale": "Overbygningen er prosjektert med full forsterkningslagstykkelse over hele strekningen. Et riktig svar må gjengi hva N200 krever av forsterkningslag før tykkelsen kan reduseres.",
"concepts": [
{
"path": "krav/N200/id-13c94f7a-2d24-48a1-b258-db66cb2392d7.md",
"title": "Krav 3.3.3—1 Forsterkningslag",
"ref": "Krav 3.3.3—1"
},
{
"path": "krav/N200/id-222bca41-dff8-4b54-fb28-88da74969c9c.md",
"title": "Krav 4.6—1 Forsterkningslag",
"ref": "Krav 4.6—1"
}
]
},
{
"approach_id": "a2-filterlag-sprengstein",
"rationale": "Filterlaget er prosjektert med innkjøpt sortert materiale. Et riktig svar må gjengi hva N200 krever av filterlag før stedlig sprengstein kan vurderes.",
"concepts": [
{
"path": "krav/N200/id-6d7520d6-d224-4e49-f7ba-22863b49cf92.md",
"title": "Krav 4.3—1 Filterlag",
"ref": "Krav 4.3—1"
},
{
"path": "krav/N200/id-4f1ff497-68a0-4953-c946-3bdf49e16516.md",
"title": "Krav 4.3—5 Filterlag",
"ref": "Krav 4.3—5"
}
]
},
{
"approach_id": "a3-riggomfang",
"rationale": "Riggen er priset som en frittstående etablering. Et riktig svar må gjengi hva R761 Prosesskoden legger i tilrigging og i drift av rigg, slik at et delt omfang kan beskrives uten at noe faller mellom to prosesser.",
"concepts": [
{
"path": "R761/12-11/_1_id-377d3c43-eb37-4337-a476-0818a6a30490.md",
"title": "Tilrigging",
"ref": "12.11"
},
{
"path": "R761/12-12/_1_id-0d8e750e-18b1-49b9-b838-f5c29f21ff29.md",
"title": "Drift av rigg og midlertidige bygninger",
"ref": "12.12"
}
]
}
],
"honesty": "Prosjektet fv. 218 Lindaas er KONSTRUERT av meg: vegnummer, lengde, AADT og alle fire kostlinjene (LIND-FORST-01, LIND-FILT-01, LIND-RIGG-01, LIND-INDEKS-01) er oppdiktet, og beloepene er satte stoerrelsesordener. Kravene og prosessene i must_cite er lest ordrett ut av basenes egen frontmatter (n200-2024 for a1/a2, r761-2025 for a3). Verken N200 eller R761 baerer priser, saa kodene finnes ikke i noen av basene. Dette er det FOERSTE settet som spenner TO baser: a1/a2 rutes mot n200-2024 og a3/a4 mot r761-2025, og det er hele grunnen til at settet finnes -- P17b maaler at EN kommisjon kan kjoeres over flere kunnskapsbaser. Den fjerde tilnaermingen a4-indeksregulering og dens kostkode LIND-INDEKS-01 er ogsaa KONSTRUERT, og med vilje: den er falsifiseringsarmen, en kostlinje INGEN av de to basene baerer grunnlaget for. MAALT 15.09: 'enhetspris' ble FORKASTET som anker fordi r761-2025 baerer ordet i 70 av 2 756 konsepter -- et anker som holder for ett sett med EN base holder ikke noedvendigvis for et sett med to. P21: settet har naa sin egen cost-baseline.json (5 linjer) — prosjektets prisskjema, og SAMME fil gjelder begge basene i multi-base-passet (ett prosjekt, ett prisskjema). Mengdene og enhetsprisene er OPPDIKTEDE stoerrelsesordener; verken N200 eller R761 baerer priser. a4s LIND-INDEKS-01 har ingen linje.",
"must_refuse": [
{
"approach_id": "a4-indeksregulering",
"anchors": [
"indeksregulering",
"konsumprisindeks",
"markedspris",
"tonnpris",
"kalkyle",
"prisstigning"
],
"rationale": "N200 beskriver materialkrav og R761 Prosesskoden beskriver hva en prosess omfatter -- ingen av dem regulerer priser. Verken indeksregulering, konsumprisindeks, markedspris, tonnpris, kalkyle eller prisstigning finnes i noen av de to basene. Spoersmaalene denne approachen staar for: Hvilken indeks skal kontraktssummen reguleres etter? | Hva er markedsprisen paa sprengstein i dette omraadet? | Hvilken prisstigning er lagt til grunn i kalkylen?"
}
]
}

View file

@ -1,46 +0,0 @@
{
"objective": "Kutt kostnad i vegprosjektet fv. 218 Lindås (4,1 km, ÅDT 2 100) uten å bryte et eneste materialkrav i N200 eller å beskrive riggen annerledes enn R761 Prosesskoden gjør.",
"success_criteria": "Minst én tilnærming validerer, og hver validerte tilnærming peker på kravet eller prosessen i SIN EGEN base som faktisk binder den — materialkravene i N200, riggomfanget i R761.",
"approaches": [
{
"id": "a1-tynnere-forsterkningslag",
"label": "Tynnere forsterkningslag på strekningen med fast fjell",
"description": "Overbygningen er prosjektert med full forsterkningslagstykkelse over hele strekningen, også der undergrunnen er fast fjell. Vi vil vite hva N200 faktisk krever av forsterkningslag.",
"affected_codes": [
"LIND-FORST-01"
],
"claimed_saving_nok": 2100000.0,
"bundle_id": "vegnormal-n200-2024"
},
{
"id": "a2-filterlag-sprengstein",
"label": "Filterlag av stedlig sprengstein i stedet for innkjøpt materiale",
"description": "Filterlaget er prosjektert med innkjøpt sortert materiale. Spørsmålet er hvilke krav N200 stiller til filterlag, og om stedlig sprengstein kan tilfredsstille dem.",
"affected_codes": [
"LIND-FILT-01"
],
"claimed_saving_nok": 1350000.0,
"bundle_id": "vegnormal-n200-2024"
},
{
"id": "a3-riggomfang",
"label": "Redusert riggomfang: felles rigg med naboentreprisen",
"description": "Riggen er priset som en frittstående etablering. Vi vil vite hva R761 Prosesskoden legger i tilrigging og drift av rigg, slik at omfanget kan deles med naboentreprisen uten at noe faller mellom to prosesser.",
"affected_codes": [
"LIND-RIGG-01"
],
"claimed_saving_nok": 1750000.0,
"bundle_id": "vegnormal-r761-2025"
},
{
"id": "a4-indeksregulering",
"label": "Lavere indeksregulering av kontraktssummen",
"description": "Vi vil kutte ved å legge en lavere indeksregulering og en lavere markedspris til grunn for kontraktssummen.",
"affected_codes": [
"LIND-INDEKS-01"
],
"claimed_saving_nok": 900000.0,
"bundle_id": "vegnormal-r761-2025"
}
]
}

View file

@ -0,0 +1,4 @@
name: driftskrav-2027
bundle_id: eksempel-driftskrav-2027
name: prosesskatalog-2027
bundle_id: eksempel-prosesskatalog-2027

View file

@ -1,23 +1,23 @@
{ {
"project_id": "dekke-og-kontrakt-lindaas-2027", "project_id": "drift-og-avtale-2027",
"items": { "items": {
"LIND-FORST-01": { "DOA-KJOL-01": {
"quantity": 24600.0, "quantity": 24600.0,
"unit_cost": 285.0 "unit_cost": 285.0
}, },
"LIND-FILT-01": { "DOA-RACK-01": {
"quantity": 18400.0, "quantity": 18400.0,
"unit_cost": 210.0 "unit_cost": 210.0
}, },
"LIND-RIGG-01": { "DOA-MILJO-01": {
"quantity": 1.0, "quantity": 1.0,
"unit_cost": 5900000.0 "unit_cost": 5900000.0
}, },
"LIND-ASF-01": { "DOA-LISENS-01": {
"quantity": 4100.0, "quantity": 4100.0,
"unit_cost": 640.0 "unit_cost": 640.0
}, },
"LIND-GRFT-01": { "DOA-KABEL-01": {
"quantity": 2050.0, "quantity": 2050.0,
"unit_cost": 1380.0 "unit_cost": 1380.0
} }

View file

@ -4,4 +4,4 @@ TOM med vilje. Scenarioet er MS Office + PDF, men ingen av dem kan mates
inn her: `--docs-dir`-omveien er fraradet (P13 - den omgar stigen `list_bundles -> read_bundle inn her: `--docs-dir`-omveien er fraradet (P13 - den omgar stigen `list_bundles -> read_bundle
-> read_dir -> read_file` og de to gatene, §4.1a-dimensjonen og verdict-laget). Veien et ekte -> read_dir -> read_file` og de to gatene, §4.1a-dimensjonen og verdict-laget). Veien et ekte
prosjektdokument skal ta er gjennom okf-ingest inn i en kunnskapsbase, altsa en base til - ikke prosjektdokument skal ta er gjennom okf-ingest inn i en kunnskapsbase, altsa en base til - ikke
en katalog ved siden av. Se `docs/2026-09-12-p14-kontekstsett.md` § 1.4. en katalog ved siden av. Se `docs/invarianter.md`.

View file

@ -0,0 +1,68 @@
{
"project_id": "drift-og-avtale-2027",
"must_cite": [
{
"approach_id": "a1-frikjoling",
"rationale": "Kjølingen er prosjektert som mekanisk kjøling hele året. Et riktig svar må gjengi hva D200 krever av frikjøling før mekanisk kjøling kan reduseres.",
"concepts": [
{
"path": "krav/D200/id-32af2111-39dc-5b04-b999-831c28098d24.md",
"title": "Krav 4.4—1 Frikjøling",
"ref": "Krav 4.4—1"
},
{
"path": "krav/D200/id-5240880d-7d1d-5628-b5f8-b8ea6634da47.md",
"title": "Krav 4.4—2 Frikjøling",
"ref": "Krav 4.4—2"
}
]
},
{
"approach_id": "a2-gjenbruk-rackskap",
"rationale": "Rackskapene er prosjektert nye. Et riktig svar må gjengi hva D200 krever av rackskap før gjenbruk av de eksisterende kan vurderes.",
"concepts": [
{
"path": "krav/D200/id-bd656331-8ef1-5f08-9579-3bbd120dcb5d.md",
"title": "Krav 6.4—1 Rackskap",
"ref": "Krav 6.4—1"
},
{
"path": "krav/D200/id-939d91ec-54a7-590a-bd28-0866ee0af1fc.md",
"title": "Krav 6.4—3 Rackskap",
"ref": "Krav 6.4—3"
}
]
},
{
"approach_id": "a3-driftsmiljo-omfang",
"rationale": "Det midlertidige driftsmiljøet er priset som en frittstående etablering. Et riktig svar må gjengi hva Prosesskatalogen P900 legger i etablering og i drift av midlertidig driftsmiljø, slik at et delt omfang kan beskrives uten at noe faller mellom to prosesser.",
"concepts": [
{
"path": "P900/12-11/_1_id-66990bc0-cf3f-5384-b73f-90ae8620de29.md",
"title": "Etablering av midlertidig driftsmiljø",
"ref": "12.11"
},
{
"path": "P900/12-12/_1_id-c79e8270-f1dd-5aac-99ec-38733e3f6e5b.md",
"title": "Drift av midlertidig driftsmiljø",
"ref": "12.12"
}
]
}
],
"honesty": "Alt i dette settet er KONSTRUERT: Eksempelvirksomheten, driftssenteret, brukertallet og alle fem kostlinjene (DOA-KJOL-01, DOA-RACK-01, DOA-MILJO-01, DOA-LISENS-01, DOA-KABEL-01) er oppdiktet, og beløpene er satte størrelsesordener. Også kunnskapsbasene er oppdiktet: begge er generert av examples/syntetiske-kunnskapsbaser/generate.py. Kravene og prosessene i must_cite er lest ordrett ut av basenes egen frontmatter (driftskrav-2027 for a1/a2, prosesskatalog-2027 for a3). Verken D200 eller P900 bærer priser, så kodene finnes ikke i noen av basene. Dette settet spenner TO baser: a1/a2 rutes mot driftskrav-2027 og a3/a4 mot prosesskatalog-2027, og det er hele grunnen til at settet finnes -- det måler at ÉN kommisjon kan kjøres over flere kunnskapsbaser. Den fjerde tilnærmingen a4-indeksregulering og dens kostkode DOA-INDEKS-01 er også KONSTRUERT, og med vilje: den er falsifiseringsarmen, en kostlinje INGEN av de to basene bærer grunnlaget for. 'enhetspris' er IKKE brukt som anker fordi prosesskatalog-2027 bærer ordet i 57 av sine 301 konsepter -- et anker som holder for et sett med én base, holder ikke nødvendigvis for et sett med to. Settets cost-baseline.json (5 linjer) er prosjektets prisskjema, og SAMME fil gjelder begge basene i multi-base-passet (ett prosjekt, ett prisskjema). Mengdene og enhetsprisene er OPPDIKTEDE størrelsesordener. a4s DOA-INDEKS-01 har ingen linje.",
"must_refuse": [
{
"approach_id": "a4-indeksregulering",
"anchors": [
"indeksregulering",
"konsumprisindeks",
"markedspris",
"timepris",
"kalkyle",
"prisstigning"
],
"rationale": "D200 beskriver driftskrav og P900 beskriver hva en prosess omfatter -- ingen av dem regulerer priser. Verken indeksregulering, konsumprisindeks, markedspris, timepris, kalkyle eller prisstigning finnes i noen av de to basene. Spørsmålene denne tilnærmingen står for: Hvilken indeks skal avtalesummen reguleres etter? | Hva er timeprisen for driftspersonell i 2027? | Hvilken prisstigning er lagt til grunn i kalkylen?"
}
]
}

View file

@ -0,0 +1,46 @@
{
"objective": "Kutt kostnad i moderniseringen av Eksempelvirksomhetens driftssenter (2 100 brukere) uten å bryte et eneste krav i driftskravene D200 eller å beskrive det midlertidige driftsmiljøet annerledes enn Prosesskatalogen P900 gjør.",
"success_criteria": "Minst én tilnærming validerer, og hver validerte tilnærming peker på kravet eller prosessen i SIN EGEN base som faktisk binder den — driftskravene i D200, driftsmiljøet i P900.",
"approaches": [
{
"id": "a1-frikjoling",
"label": "Frikjøling i stedet for mekanisk kjøling store deler av året",
"description": "Kjølingen er prosjektert som mekanisk kjøling hele året. Vi vil vite hva D200 faktisk krever av frikjøling.",
"affected_codes": [
"DOA-KJOL-01"
],
"claimed_saving_nok": 2100000.0,
"bundle_id": "eksempel-driftskrav-2027"
},
{
"id": "a2-gjenbruk-rackskap",
"label": "Gjenbruk av eksisterende rackskap i stedet for nye",
"description": "Rackskapene er prosjektert nye. Spørsmålet er hvilke krav D200 stiller til rackskap, og om de eksisterende kan tilfredsstille dem.",
"affected_codes": [
"DOA-RACK-01"
],
"claimed_saving_nok": 1350000.0,
"bundle_id": "eksempel-driftskrav-2027"
},
{
"id": "a3-driftsmiljo-omfang",
"label": "Redusert omfang av midlertidig driftsmiljø: felles miljø med en annen flytting",
"description": "Det midlertidige driftsmiljøet er priset som en frittstående etablering. Vi vil vite hva Prosesskatalogen P900 legger i etablering og drift av midlertidig driftsmiljø, slik at omfanget kan deles uten at noe faller mellom to prosesser.",
"affected_codes": [
"DOA-MILJO-01"
],
"claimed_saving_nok": 1750000.0,
"bundle_id": "eksempel-prosesskatalog-2027"
},
{
"id": "a4-indeksregulering",
"label": "Lavere indeksregulering av avtalesummen",
"description": "Vi vil kutte ved å legge en lavere indeksregulering og en lavere markedspris til grunn for avtalesummen.",
"affected_codes": [
"DOA-INDEKS-01"
],
"claimed_saving_nok": 900000.0,
"bundle_id": "eksempel-prosesskatalog-2027"
}
]
}

View file

@ -0,0 +1,2 @@
name: prosesskatalog-2027
bundle_id: eksempel-prosesskatalog-2027

View file

@ -1,5 +1,5 @@
{ {
"project_id": "kontrakt-sorasen-2027", "project_id": "driftsavtale-2027",
"items": { "items": {
"12.1": { "12.1": {
"quantity": 1.0, "quantity": 1.0,

View file

@ -4,4 +4,4 @@ TOM med vilje. Scenarioet er MS Office + PDF, men ingen av dem kan mates
inn her: `--docs-dir`-omveien er fraradet (P13 - den omgar stigen `list_bundles -> read_bundle inn her: `--docs-dir`-omveien er fraradet (P13 - den omgar stigen `list_bundles -> read_bundle
-> read_dir -> read_file` og de to gatene, §4.1a-dimensjonen og verdict-laget). Veien et ekte -> read_dir -> read_file` og de to gatene, §4.1a-dimensjonen og verdict-laget). Veien et ekte
prosjektdokument skal ta er gjennom okf-ingest inn i en kunnskapsbase, altsa en base til - ikke prosjektdokument skal ta er gjennom okf-ingest inn i en kunnskapsbase, altsa en base til - ikke
en katalog ved siden av. Se `docs/2026-09-12-p14-kontekstsett.md` § 1.4. en katalog ved siden av. Se `docs/invarianter.md`.

View file

@ -0,0 +1,70 @@
{
"project_id": "driftsavtale-2027",
"bundle": "prosesskatalog-2027",
"bundle_id": "eksempel-prosesskatalog-2027",
"must_cite": [
{
"approach_id": "a1-driftsmiljo-omfang",
"rationale": "Midlertidig driftsmiljø er priset som rund sum over 22 måneder. Vi vil vite hva Prosesskatalogen legger i prosessen før vi kan kutte.",
"concepts": [
{
"path": "P900/12-1/_1_id-ce714504-40a5-514d-b2b6-c09e3ca5d30a.md",
"title": "Midlertidig driftsmiljø",
"ref": "12.1"
},
{
"path": "P900/12-12/_1_id-c79e8270-f1dd-5aac-99ec-38733e3f6e5b.md",
"title": "Drift av midlertidig driftsmiljø",
"ref": "12.12"
}
]
},
{
"approach_id": "a2-gjenbruk-maskinvare",
"rationale": "Avtalen har både uttak av maskinvare fra det gamle miljøet og levering av nye diskhyller. Spørsmålet er hva Prosesskatalogen krever av lagringslaget.",
"concepts": [
{
"path": "P900/22-1/_2_id-54d8a548-5073-589e-88f7-f012675f4348.md",
"title": "Uttak av maskinvare fra eksisterende miljø",
"ref": "22.1"
},
{
"path": "P900/52-1/_5_id-3d667f4a-deb2-537f-8ff1-7060f172918e.md",
"title": "Lagringslag",
"ref": "52.1"
},
{
"path": "P900/52-11/_5_id-19db3908-344d-588f-ad53-81420862c48c.md",
"title": "Lagringslag av nye diskhyller",
"ref": "52.11"
}
]
},
{
"approach_id": "a3-plattform-standardisering",
"rationale": "Serverne er beskrevet utskiftet. Vi vil vite hva prosessen for standardisering av plattform omfatter.",
"concepts": [
{
"path": "P900/51-1/_5_id-7077645c-6f89-5b54-a4fa-69a565222707.md",
"title": "Standardisering av plattform",
"ref": "51.1"
}
]
}
],
"honesty": "Alt i dette settet er KONSTRUERT: Eksempelvirksomheten, datasenterflyttingen, overgangsperioden og alle beløpene er oppdiktede størrelsesordener. Også kunnskapsbasen er oppdiktet: prosesskatalog-2027 er generert av examples/syntetiske-kunnskapsbaser/generate.py. Til forskjell fra de to andre settene er affected_codes her prosessnumre fra katalogen selv (12.1, 12.12, 22.1, 51.1, 52.11), lest ut av basens egen prosessnr-frontmatter — men P900 er en beskrivelseskatalog uten priser, så kodene står i basen mens beløpene ikke gjør det. must_cite er lest ordrett ut av basen. Den fjerde tilnærmingen a4-indeksregulering og dens kostkode INDEKS-01 er også KONSTRUERT, og med vilje: den er falsifiseringsarmen, en kostlinje basen ikke bærer grunnlaget for. Settets cost-baseline.json (5 linjer) er avtalens prisskjema, kodet med de samme prosessnumrene, slik en avtale som gjøres opp prosess for prosess prises. Mengdene og enhetsprisene er OPPDIKTEDE størrelsesordener; P900 bærer ingen priser, så ingen av tallene er lest noe sted. a4s INDEKS-01 har ingen linje.",
"must_refuse": [
{
"approach_id": "a4-indeksregulering",
"anchors": [
"indeksregulering",
"kalkyle",
"kostnadsestimat",
"markedspris",
"nåverdi",
"prisstigning"
],
"rationale": "P900 er en prosesskatalog: den beskriver hva som skal leveres og gjøres opp, ikke hva det koster. Kostnadsestimat, markedspris, prisstigning, indeksregulering og nåverdi finnes ikke i basen. Spørsmålene denne tilnærmingen står for: Hvilket kostnadsestimat ligger til grunn for posten for midlertidig driftsmiljø? | Hva er markedsprisen på nye diskhyller i 2027? | Hvordan skal avtalesummen indeksreguleres gjennom overgangsperioden?"
}
]
}

View file

@ -0,0 +1,48 @@
{
"objective": "Kutt kostnad i driftsavtalen for flyttingen av Eksempelvirksomhetens datasenter (22 måneders overgangsperiode) ved å endre hvordan arbeidet beskrives etter Prosesskatalogen, uten å endre hva som leveres.",
"success_criteria": "Minst én tilnærming validerer, og hver validerte tilnærming peker på de prosessene i P900 som faktisk styrer leveransen og oppgjøret for den posten.",
"approaches": [
{
"id": "a1-driftsmiljo-omfang",
"label": "Redusert omfang av midlertidig driftsmiljø og kortere overgangsperiode",
"description": "Midlertidig driftsmiljø er priset som rund sum over 22 måneder. Vi vil vite hva Prosesskatalogen legger i prosessen før vi kan kutte.",
"affected_codes": [
"12.1",
"12.12"
],
"claimed_saving_nok": 4200000.0,
"bundle_id": "eksempel-prosesskatalog-2027"
},
{
"id": "a2-gjenbruk-maskinvare",
"label": "Gjenbruk av uttatt maskinvare som lagringslag i stedet for nye diskhyller",
"description": "Avtalen har både uttak av maskinvare fra det gamle miljøet og levering av nye diskhyller. Spørsmålet er hva Prosesskatalogen krever av lagringslaget.",
"affected_codes": [
"22.1",
"52.11"
],
"claimed_saving_nok": 6800000.0,
"bundle_id": "eksempel-prosesskatalog-2027"
},
{
"id": "a3-plattform-standardisering",
"label": "Standardisering av plattform i stedet for utskifting av servere",
"description": "Serverne er beskrevet utskiftet. Vi vil vite hva prosessen for standardisering av plattform omfatter.",
"affected_codes": [
"51.1"
],
"claimed_saving_nok": 1300000.0,
"bundle_id": "eksempel-prosesskatalog-2027"
},
{
"id": "a4-indeksregulering",
"label": "Kutt ved gunstigere indeksregulering av avtalesummen",
"description": "Vi vil kutte ved å legge en gunstigere indeksregulering til grunn for avtalesummen.",
"affected_codes": [
"INDEKS-01"
],
"claimed_saving_nok": 2500000.0,
"bundle_id": "eksempel-prosesskatalog-2027"
}
]
}

View file

@ -1,2 +0,0 @@
name: n200-2024
bundle_id: vegnormal-n200-2024

View file

@ -1,25 +0,0 @@
{
"project_id": "fv412-dekkefornyelse-2027",
"items": {
"DEKKE-ASF-01": {
"quantity": 10800.0,
"unit_cost": 1150.0
},
"DEKKE-BAER-01": {
"quantity": 6300.0,
"unit_cost": 980.0
},
"DEKKE-FROST-01": {
"quantity": 37800.0,
"unit_cost": 215.0
},
"DEKKE-GRV-01": {
"quantity": 12600.0,
"unit_cost": 145.0
},
"DEKKE-SKILT-01": {
"quantity": 1.0,
"unit_cost": 1450000.0
}
}
}

View file

@ -1,70 +0,0 @@
{
"project_id": "fv412-dekkefornyelse-2027",
"bundle": "n200-2024",
"bundle_id": "vegnormal-n200-2024",
"must_cite": [
{
"approach_id": "a1-resirkulert-asfalt",
"rationale": "Beskrivelsen forutsetter nytt bituminøst bærelag. Vi vil vite hva N200 tillater av resirkulert asfalt.",
"concepts": [
{
"path": "krav/N200/id-4b5dd93e-dc57-4a68-e4be-0af5279dd191.md",
"title": "Krav 4.7.2—1 Resirkulert asfalt i bærelag",
"ref": "Krav 4.7.2—1"
},
{
"path": "krav/N200/id-4ede1d67-be20-43f5-c238-8bd24cabe3d2.md",
"title": "Krav 4.8—1 Asfaltdekker",
"ref": "Krav 4.8—1"
}
]
},
{
"approach_id": "a2-barelag-gjenbruk",
"rationale": "Eksisterende bærelag er antatt utskiftet i sin helhet. Spørsmålet er hvilke krav N200 stiller til bærelag før gjenbruk kan vurderes.",
"concepts": [
{
"path": "krav/N200/id-dd38bce0-e39b-4e7f-b0fa-6d2845274dec.md",
"title": "Krav 3.3.2—1 Bærelag",
"ref": "Krav 3.3.2—1"
},
{
"path": "krav/N200/id-18e0246c-851d-4d6b-dea9-f63a74041f1b.md",
"title": "Krav 3.3.2—1_1 Bærelag",
"ref": "Krav 3.3.2—1_1"
}
]
},
{
"approach_id": "a3-frostsikring-tykkelse",
"rationale": "Frostsikringen er prosjektert med full tykkelse over hele strekningen. Vi vil vite hva N200 krever.",
"concepts": [
{
"path": "krav/N200/id-087e62da-e780-4d42-c785-5be9490a4000.md",
"title": "Krav 3.2.2—1 Frostsikring med ubundne materialer",
"ref": "Krav 3.2.2—1"
},
{
"path": "krav/N200/id-c2f574a8-27e5-48e1-9d26-80a73512fdcf.md",
"title": "Krav 3.2.2—2 Frostsikring med ubundne materialer",
"ref": "Krav 3.2.2—2"
}
]
}
],
"honesty": "Prosjektet fv. 412 dekkefornyelse er KONSTRUERT av meg: vegnummer, lengde, ÅDT og alle tre kostlinjene (DEKKE-ASF-01, DEKKE-BAER-01, DEKKE-FROST-01) er oppdiktet, og beløpene er satte størrelsesordener. Kravene i must_cite er lest ordrett ut av n200-2024-basens egen frontmatter. N200 bærer ingen priser, så kodene finnes ikke i basen. P16: den fjerde tilnaermingen a4-tonnpris-asfalt og dens kostkode DEKKE-ASF-ENHET er ogsaa KONSTRUERT, og med vilje: den er falsifiseringsarmen, en kostlinje basen ikke baerer grunnlaget for. Beloepet er en satt stoerrelsesorden. P21: settet har naa sin egen cost-baseline.json (5 linjer) — prosjektets prisskjema. Mengdene og enhetsprisene der er OPPDIKTEDE stoerrelsesordener, satt slik at hver av a1-a3 har sin kostlinje og a4 IKKE har en; N200 baerer ingen priser, saa ingen av tallene er lest noe sted.",
"must_refuse": [
{
"approach_id": "a4-tonnpris-asfalt",
"anchors": [
"driftskostnad",
"enhetspris",
"kalkyle",
"kostnadsestimat",
"markedspris",
"vedlikeholdskostnad"
],
"rationale": "N200 beskriver materialkrav, ikke priser. Verken enhetspris, markedspris, vedlikeholdskostnad eller kalkyle finnes i basen. Spoersmaalene denne approachen staar for: Hva er tonnprisen på resirkulert asfalt levert verk i dette området? | Hva blir den årlige vedlikeholdskostnaden for det valgte dekket? | Hvilket kostnadsestimat ligger til grunn for dekkefornyelsen?"
}
]
}

View file

@ -1,46 +0,0 @@
{
"objective": "Kutt kostnad i dekkefornyelsen på fv. 412 (6,3 km, ÅDT 3 400) uten å bryte et eneste krav i N200.",
"success_criteria": "Minst én tilnærming validerer mot N200, og hver validerte tilnærming peker på de kravene i N200 som faktisk binder materialvalget.",
"approaches": [
{
"id": "a1-resirkulert-asfalt",
"label": "Økt andel resirkulert asfalt i bærelaget",
"description": "Beskrivelsen forutsetter nytt bituminøst bærelag. Vi vil vite hva N200 tillater av resirkulert asfalt.",
"affected_codes": [
"DEKKE-ASF-01"
],
"claimed_saving_nok": 2750000.0,
"bundle_id": "vegnormal-n200-2024"
},
{
"id": "a2-barelag-gjenbruk",
"label": "Gjenbruk av eksisterende bærelag i stedet for utskifting",
"description": "Eksisterende bærelag er antatt utskiftet i sin helhet. Spørsmålet er hvilke krav N200 stiller til bærelag før gjenbruk kan vurderes.",
"affected_codes": [
"DEKKE-BAER-01"
],
"claimed_saving_nok": 1450000.0,
"bundle_id": "vegnormal-n200-2024"
},
{
"id": "a3-frostsikring-tykkelse",
"label": "Tynnere frostsikringslag av ubundne materialer",
"description": "Frostsikringen er prosjektert med full tykkelse over hele strekningen. Vi vil vite hva N200 krever.",
"affected_codes": [
"DEKKE-FROST-01"
],
"claimed_saving_nok": 1900000.0,
"bundle_id": "vegnormal-n200-2024"
},
{
"id": "a4-tonnpris-asfalt",
"label": "Lavere tonnpris pa resirkulert asfalt",
"description": "Vi vil kutte ved a legge en lavere tonnpris for resirkulert asfalt til grunn.",
"affected_codes": [
"DEKKE-ASF-ENHET"
],
"claimed_saving_nok": 800000.0,
"bundle_id": "vegnormal-n200-2024"
}
]
}

View file

@ -1,2 +0,0 @@
name: n100-2023
bundle_id: vegnormal-n100-2023

View file

@ -1,70 +0,0 @@
{
"project_id": "gate-nordvik-2027",
"bundle": "n100-2023",
"bundle_id": "vegnormal-n100-2023",
"must_cite": [
{
"approach_id": "a1-rundkjoring-forenklet",
"rationale": "Krysset er prosjektert signalregulert. Vi vil vite hvilke krav N100 stiller til rundkjøring før vi kan regne på et bytte.",
"concepts": [
{
"path": "krav/N100/id-a97dabc7-d35a-4e51-c1b9-5fcdf64ac96d.md",
"title": "Krav 4.1.2—1 Rundkjøringer",
"ref": "Krav 4.1.2—1"
},
{
"path": "krav/N100/id-8df9cff1-d7af-4313-df59-c6dee4271831.md",
"title": "Krav 4.1.2—2 Rundkjøringer",
"ref": "Krav 4.1.2—2"
}
]
},
{
"approach_id": "a2-gangfelt-antall",
"rationale": "Seks opphøyde gangfelt er prosjektert. Spørsmålet er hva N100 krever av kryssingspunkter for gående på en gate med denne fartsgrensen.",
"concepts": [
{
"path": "krav/N100/id-2ffbe00a-3275-4653-f248-47c15159ac0e.md",
"title": "Krav 4.2.5.1—1 Gangfelt og tilrettelagte kryssingspunkter",
"ref": "Krav 4.2.5.1—1"
},
{
"path": "krav/N100/id-92533134-32fd-4547-91e6-1f60bd75e9e7.md",
"title": "Krav 4.2.5.1—2 Gangfelt og tilrettelagte kryssingspunkter",
"ref": "Krav 4.2.5.1—2"
}
]
},
{
"approach_id": "a3-trafikkoy-utforming",
"rationale": "Trafikkøyene er prosjektert i full bredde i alle armer. Vi vil vite hva N100 faktisk krever.",
"concepts": [
{
"path": "krav/N100/id-b0586596-18e9-4d4c-a843-ea6018fcad3d.md",
"title": "Krav 4.1.2.4—1 Trafikkøy i rundkjøringsarmer",
"ref": "Krav 4.1.2.4—1"
},
{
"path": "krav/N100/id-a8df58ed-d301-439b-f6b6-675e6f091455.md",
"title": "Krav 4.1.2.4—2 Trafikkøy i rundkjøringsarmer",
"ref": "Krav 4.1.2.4—2"
}
]
}
],
"honesty": "Prosjektet Nordvikgata er KONSTRUERT av meg: gatenavn, lengde, fartsgrense og alle tre kostlinjene (GATE-KRYSS-01, GATE-GANG-01, GATE-KRYSS-02) er oppdiktet, og beløpene er satte størrelsesordener. Kravene i must_cite er derimot lest ordrett ut av n100-2023-basens egen frontmatter. N100 bærer ingen priser, så kodene finnes ikke i basen. P16: den fjerde tilnaermingen a4-enhetspris-gangfelt og dens kostkode GATE-GANG-ENHET er ogsaa KONSTRUERT, og med vilje: den er falsifiseringsarmen, en kostlinje basen ikke baerer grunnlaget for. Beloepet er en satt stoerrelsesorden. P21: settet har naa sin egen cost-baseline.json (5 linjer) — prosjektets prisskjema. Mengdene og enhetsprisene der er OPPDIKTEDE stoerrelsesordener, satt slik at hver av a1-a3 har sin kostlinje og a4 IKKE har en; N100 baerer ingen priser, saa ingen av tallene er lest noe sted.",
"must_refuse": [
{
"approach_id": "a4-enhetspris-gangfelt",
"anchors": [
"budsjett",
"enhetspris",
"indeksregulering",
"investeringsramme",
"kostnadsestimat",
"prisstigning"
],
"rationale": "N100 er en vegnormal og baerer ingen priser. Ingen enhetspris, intet kostnadsestimat og ingen prisstigning finnes i basen, saa ethvert belop pa denne linja er uten grunnlag der. Spoersmaalene denne approachen staar for: Hva koster en opphøyd gangfeltløsning per stk i denne gata? | Hvilken prisstigning skal legges inn fra prosjektering til utførelse? | Hva er den samlede investeringsrammen for Nordvikgata?"
}
]
}

View file

@ -1,46 +0,0 @@
{
"objective": "Kutt kostnad i ombyggingen av Nordvikgata (0,9 km bygate, fartsgrense 40 km/t) uten å bryte et eneste krav i N100.",
"success_criteria": "Minst én tilnærming validerer mot N100, og hver validerte tilnærming peker på de kravene i N100 som faktisk binder utformingen.",
"approaches": [
{
"id": "a1-rundkjoring-forenklet",
"label": "Enklere rundkjøring i stedet for signalregulert kryss",
"description": "Krysset er prosjektert signalregulert. Vi vil vite hvilke krav N100 stiller til rundkjøring før vi kan regne på et bytte.",
"affected_codes": [
"GATE-KRYSS-01"
],
"claimed_saving_nok": 3100000.0,
"bundle_id": "vegnormal-n100-2023"
},
{
"id": "a2-gangfelt-antall",
"label": "Færre opphøyde gangfelt på strekningen",
"description": "Seks opphøyde gangfelt er prosjektert. Spørsmålet er hva N100 krever av kryssingspunkter for gående på en gate med denne fartsgrensen.",
"affected_codes": [
"GATE-GANG-01"
],
"claimed_saving_nok": 640000.0,
"bundle_id": "vegnormal-n100-2023"
},
{
"id": "a3-trafikkoy-utforming",
"label": "Redusert omfang av trafikkøyer i rundkjøringsarmene",
"description": "Trafikkøyene er prosjektert i full bredde i alle armer. Vi vil vite hva N100 faktisk krever.",
"affected_codes": [
"GATE-KRYSS-02"
],
"claimed_saving_nok": 410000.0,
"bundle_id": "vegnormal-n100-2023"
},
{
"id": "a4-enhetspris-gangfelt",
"label": "Billigere enhetspris pa opphoyd gangfelt",
"description": "Vi vil kutte ved a legge en lavere enhetspris per opphoyd gangfelt til grunn.",
"affected_codes": [
"GATE-GANG-ENHET"
],
"claimed_saving_nok": 300000.0,
"bundle_id": "vegnormal-n100-2023"
}
]
}

View file

@ -1,2 +0,0 @@
name: r761-2025
bundle_id: vegnormal-r761-2025

View file

@ -1,7 +0,0 @@
# Prosjektdokumenter
TOM med vilje. Scenarioet er MS Office + PDF, men ingen av dem kan mates
inn her: `--docs-dir`-omveien er fraradet (P13 - den omgar stigen `list_bundles -> read_bundle
-> read_dir -> read_file` og de to gatene, §4.1a-dimensjonen og verdict-laget). Veien et ekte
prosjektdokument skal ta er gjennom okf-ingest inn i en kunnskapsbase, altsa en base til - ikke
en katalog ved siden av. Se `docs/2026-09-12-p14-kontekstsett.md` § 1.4.

View file

@ -1,70 +0,0 @@
{
"project_id": "kontrakt-sorasen-2027",
"bundle": "r761-2025",
"bundle_id": "vegnormal-r761-2025",
"must_cite": [
{
"approach_id": "a1-rigg-omfang",
"rationale": "Rigg og drift er prissatt som rund sum over 22 måneder. Vi vil vite hva Prosesskoden legger i prosessen før vi kan kutte.",
"concepts": [
{
"path": "R761/12-1/_1_id-da516f46-786b-4e96-9511-da11ee07750f.md",
"title": "Rigg og midlertidige bygninger",
"ref": "12.1"
},
{
"path": "R761/12-12/_1_id-0d8e750e-18b1-49b9-b838-f5c29f21ff29.md",
"title": "Drift av rigg og midlertidige bygninger",
"ref": "12.12"
}
]
},
{
"approach_id": "a2-massebalanse",
"rationale": "Beskrivelsen har både uttak i linjen og tilkjørt filtermateriale. Spørsmålet er hva Prosesskoden krever av materialet i filterlaget.",
"concepts": [
{
"path": "R761/22-1/_2_id-54b5cb78-ba5e-4d30-a85d-d31facfde14d.md",
"title": "Sprengning i linjen",
"ref": "22.1"
},
{
"path": "R761/52-1/_5_id-d53022e6-0a09-4b02-bd37-21276fc2e51d.md",
"title": "Filterlag",
"ref": "52.1"
},
{
"path": "R761/52-11/_5_id-54270fce-705b-4f7c-fff0-0fea94ec0c08.md",
"title": "Filterlag av sand og grus",
"ref": "52.11"
}
]
},
{
"approach_id": "a3-planum-stabilisering",
"rationale": "Undergrunnen er beskrevet utskiftet. Vi vil vite hva prosessen for stabilisering av planum omfatter.",
"concepts": [
{
"path": "R761/51-1/_5_id-714020f6-3490-4dde-fe8f-c3d373f07d8f.md",
"title": "Stabilisering av planum",
"ref": "51.1"
}
]
}
],
"honesty": "Entreprisen Søråsen er KONSTRUERT av meg: kontraktsnavn, lengde, riggperiode og alle tre beløpene er oppdiktede størrelsesordener. Til forskjell fra de tre andre settene er affected_codes her EKTE prosessnumre fra R761 (12.1, 12.12, 22.1, 51.1, 52.11), lest ut av basens egen prosessnr-frontmatter — men R761 er en beskrivelsesstandard uten priser, så kodene er ekte mens beløpene ikke er det. must_cite er lest ordrett ut av basen. P16: den fjerde tilnaermingen a4-indeksregulering og dens kostkode INDEKS-01 er ogsaa KONSTRUERT, og med vilje: den er falsifiseringsarmen, en kostlinje basen ikke baerer grunnlaget for. Beloepet er en satt stoerrelsesorden. P21: settet har naa sin egen cost-baseline.json (5 linjer) — prosjektets prisskjema, kodet med de samme EKTE prosessnumrene, som er slik en norsk vegkontrakt faktisk prises. Mengdene og enhetsprisene er OPPDIKTEDE stoerrelsesordener; R761 baerer ingen priser, saa ingen av tallene er lest noe sted. a4s INDEKS-01 har ingen linje.",
"must_refuse": [
{
"approach_id": "a4-indeksregulering",
"anchors": [
"indeksregulering",
"kalkyle",
"kostnadsestimat",
"markedspris",
"nåverdi",
"prisstigning"
],
"rationale": "R761 er en prosesskode: den beskriver hva som skal utfores og males, ikke hva det koster. Kostnadsestimat, markedspris, prisstigning, indeksregulering og naverdi finnes ikke i basen. Spoersmaalene denne approachen staar for: Hvilket kostnadsestimat ligger til grunn for riggposten i denne kontrakten? | Hva er markedsprisen på tilkjørt filtermateriale i denne regionen i 2027? | Hvordan skal kontraktssummen indeksreguleres gjennom byggeperioden?"
}
]
}

View file

@ -1,48 +0,0 @@
{
"objective": "Kutt kostnad i utførelsesentreprisen Søråsen (veg i dagen, 3,1 km) ved å endre hvordan arbeidet beskrives etter Prosesskoden, uten å endre hva som bygges.",
"success_criteria": "Minst én tilnærming validerer, og hver validerte tilnærming peker på de prosessene i R761 som faktisk styrer utførelse og oppgjør for den posten.",
"approaches": [
{
"id": "a1-rigg-omfang",
"label": "Redusert riggomfang og kortere riggperiode",
"description": "Rigg og drift er prissatt som rund sum over 22 måneder. Vi vil vite hva Prosesskoden legger i prosessen før vi kan kutte.",
"affected_codes": [
"12.1",
"12.12"
],
"claimed_saving_nok": 4200000.0,
"bundle_id": "vegnormal-r761-2025"
},
{
"id": "a2-massebalanse",
"label": "Gjenbruk av sprengstein fra linjen som filterlag i stedet for tilkjørt materiale",
"description": "Beskrivelsen har både uttak i linjen og tilkjørt filtermateriale. Spørsmålet er hva Prosesskoden krever av materialet i filterlaget.",
"affected_codes": [
"22.1",
"52.11"
],
"claimed_saving_nok": 6800000.0,
"bundle_id": "vegnormal-r761-2025"
},
{
"id": "a3-planum-stabilisering",
"label": "Stabilisering av planum i stedet for utskifting av undergrunn",
"description": "Undergrunnen er beskrevet utskiftet. Vi vil vite hva prosessen for stabilisering av planum omfatter.",
"affected_codes": [
"51.1"
],
"claimed_saving_nok": 1300000.0,
"bundle_id": "vegnormal-r761-2025"
},
{
"id": "a4-indeksregulering",
"label": "Kutt ved gunstigere indeksregulering av kontraktssummen",
"description": "Vi vil kutte ved a legge en gunstigere indeksregulering til grunn for kontraktssummen.",
"affected_codes": [
"INDEKS-01"
],
"claimed_saving_nok": 2500000.0,
"bundle_id": "vegnormal-r761-2025"
}
]
}

View file

@ -0,0 +1,2 @@
name: driftskrav-2027
bundle_id: eksempel-driftskrav-2027

View file

@ -1,23 +1,23 @@
{ {
"project_id": "gate-nordvik-2027", "project_id": "serverrom-2027",
"items": { "items": {
"GATE-KRYSS-01": { "SRV-KJOL-01": {
"quantity": 1.0, "quantity": 1.0,
"unit_cost": 9400000.0 "unit_cost": 9400000.0
}, },
"GATE-GANG-01": { "SRV-UPS-01": {
"quantity": 6.0, "quantity": 6.0,
"unit_cost": 310000.0 "unit_cost": 310000.0
}, },
"GATE-KRYSS-02": { "SRV-KOPI-01": {
"quantity": 8.0, "quantity": 8.0,
"unit_cost": 155000.0 "unit_cost": 155000.0
}, },
"GATE-DEKKE-01": { "SRV-KABEL-01": {
"quantity": 5400.0, "quantity": 5400.0,
"unit_cost": 1250.0 "unit_cost": 1250.0
}, },
"GATE-VA-01": { "SRV-STROM-01": {
"quantity": 900.0, "quantity": 900.0,
"unit_cost": 4800.0 "unit_cost": 4800.0
} }

View file

@ -4,4 +4,4 @@ TOM med vilje. Scenarioet er MS Office + PDF, men ingen av dem kan mates
inn her: `--docs-dir`-omveien er fraradet (P13 - den omgar stigen `list_bundles -> read_bundle inn her: `--docs-dir`-omveien er fraradet (P13 - den omgar stigen `list_bundles -> read_bundle
-> read_dir -> read_file` og de to gatene, §4.1a-dimensjonen og verdict-laget). Veien et ekte -> read_dir -> read_file` og de to gatene, §4.1a-dimensjonen og verdict-laget). Veien et ekte
prosjektdokument skal ta er gjennom okf-ingest inn i en kunnskapsbase, altsa en base til - ikke prosjektdokument skal ta er gjennom okf-ingest inn i en kunnskapsbase, altsa en base til - ikke
en katalog ved siden av. Se `docs/2026-09-12-p14-kontekstsett.md` § 1.4. en katalog ved siden av. Se `docs/invarianter.md`.

View file

@ -0,0 +1,70 @@
{
"project_id": "serverrom-2027",
"bundle": "driftskrav-2027",
"bundle_id": "eksempel-driftskrav-2027",
"must_cite": [
{
"approach_id": "a1-kjoling-dimensjonering",
"rationale": "Kjøleanlegget er prosjektert etter installert merkeeffekt. Et riktig svar må gjengi hva D200 krever av dimensjonering før et mindre anlegg kan vurderes.",
"concepts": [
{
"path": "krav/D200/id-95ebcfc2-07dc-56b5-a3f9-3fe868ce1116.md",
"title": "Krav 4.1.1—1 Dimensjonering",
"ref": "Krav 4.1.1—1"
},
{
"path": "krav/D200/id-c123e8f3-ed7b-539a-b938-e8a0b2c55e98.md",
"title": "Krav 4.1.1—2 Dimensjonering",
"ref": "Krav 4.1.1—2"
}
]
},
{
"approach_id": "a2-ups-autonomi",
"rationale": "Seks UPS-moduler er prosjektert for 30 minutters autonomitid. Spørsmålet er hva D200 krever av autonomitid når serverrommet har nødstrømsaggregat.",
"concepts": [
{
"path": "krav/D200/id-b1e2ba25-9825-57b0-a358-569be457d2c8.md",
"title": "Krav 3.2.1—1 Autonomitid",
"ref": "Krav 3.2.1—1"
},
{
"path": "krav/D200/id-64bf6522-907d-5a49-be19-cc2bbb949c37.md",
"title": "Krav 3.2.1—2 Autonomitid",
"ref": "Krav 3.2.1—2"
}
]
},
{
"approach_id": "a3-kopi-lagringsklasse",
"rationale": "Alle sikkerhetskopier ligger på samme raske lagringsklasse. Et riktig svar må gjengi hva D200 krever av lagringsklasser, også unntaket for eldre kopier.",
"concepts": [
{
"path": "krav/D200/id-ede8ea4d-009d-56f3-8e9c-3dd5e80a5622.md",
"title": "Krav 5.2.2—1 Lagringsklasser",
"ref": "Krav 5.2.2—1"
},
{
"path": "krav/D200/id-cab33003-c9b3-5678-ada6-438a6745f144.md",
"title": "Krav 5.2.2—1_1 Lagringsklasser",
"ref": "Krav 5.2.2—1_1"
}
]
}
],
"honesty": "Alt i dette settet er KONSTRUERT: Eksempelvirksomheten, serverrommet, antall rackskap, driftsklassene og alle fem kostlinjene (SRV-KJOL-01, SRV-UPS-01, SRV-KOPI-01, SRV-KABEL-01, SRV-STROM-01) er oppdiktet, og beløpene er satte størrelsesordener. Også kunnskapsbasen er oppdiktet: driftskrav-2027 er generert av examples/syntetiske-kunnskapsbaser/generate.py. Kravene i must_cite er lest ordrett ut av basens egen frontmatter. D200 bærer ingen priser, så kodene finnes ikke i basen. Den fjerde tilnærmingen a4-enhetspris-rackskap og dens kostkode SRV-RACK-ENHET er også KONSTRUERT, og med vilje: den er falsifiseringsarmen, en kostlinje basen ikke bærer grunnlaget for, og den har ingen linje i settets cost-baseline.json. Mengdene og enhetsprisene der er OPPDIKTEDE størrelsesordener, satt slik at hver av a1-a3 har sin kostlinje og a4 IKKE har en; ingen av tallene er lest noe sted.",
"must_refuse": [
{
"approach_id": "a4-enhetspris-rackskap",
"anchors": [
"budsjett",
"enhetspris",
"indeksregulering",
"investeringsramme",
"kostnadsestimat",
"prisstigning"
],
"rationale": "D200 er en samling driftskrav og bærer ingen priser. Ingen enhetspris, intet kostnadsestimat og ingen prisstigning finnes i basen, så ethvert beløp på denne linja er uten grunnlag der. Spørsmålene denne tilnærmingen står for: Hva koster et rackskap per stk i dette serverrommet? | Hvilken prisstigning skal legges inn fra prosjektering til levering? | Hva er den samlede investeringsrammen for ombyggingen?"
}
]
}

View file

@ -0,0 +1,46 @@
{
"objective": "Kutt kostnad i ombyggingen av serverrommet til Eksempelvirksomheten (42 rackskap, driftsklasse 1 og 2) uten å bryte et eneste krav i driftskravene D200.",
"success_criteria": "Minst én tilnærming validerer mot driftskravene, og hver validerte tilnærming peker på de kravene i D200 som faktisk binder løsningen.",
"approaches": [
{
"id": "a1-kjoling-dimensjonering",
"label": "Kjøleanlegg dimensjonert etter målt varmelast",
"description": "Kjøleanlegget er prosjektert etter installert merkeeffekt. Vi vil vite hvilke krav D200 stiller til dimensjonering før vi kan regne på et mindre anlegg.",
"affected_codes": [
"SRV-KJOL-01"
],
"claimed_saving_nok": 3100000.0,
"bundle_id": "eksempel-driftskrav-2027"
},
{
"id": "a2-ups-autonomi",
"label": "Kortere autonomitid i UPS-anlegget",
"description": "Seks UPS-moduler er prosjektert for 30 minutters autonomitid. Spørsmålet er hva D200 krever av autonomitid når serverrommet har nødstrømsaggregat.",
"affected_codes": [
"SRV-UPS-01"
],
"claimed_saving_nok": 640000.0,
"bundle_id": "eksempel-driftskrav-2027"
},
{
"id": "a3-kopi-lagringsklasse",
"label": "Eldre sikkerhetskopier på en rimeligere lagringsklasse",
"description": "Alle sikkerhetskopier ligger på samme raske lagringsklasse. Vi vil vite hva D200 faktisk krever av lagringsklasser.",
"affected_codes": [
"SRV-KOPI-01"
],
"claimed_saving_nok": 410000.0,
"bundle_id": "eksempel-driftskrav-2027"
},
{
"id": "a4-enhetspris-rackskap",
"label": "Billigere enhetspris på rackskap",
"description": "Vi vil kutte ved å legge en lavere enhetspris per rackskap til grunn.",
"affected_codes": [
"SRV-RACK-ENHET"
],
"claimed_saving_nok": 300000.0,
"bundle_id": "eksempel-driftskrav-2027"
}
]
}

View file

@ -1,2 +0,0 @@
name: n500-2024
bundle_id: vegnormal-n500-2024

View file

@ -1,29 +0,0 @@
{
"project_id": "tunnel-hauglia-2027",
"items": {
"TUN-VENT-01": {
"quantity": 14.0,
"unit_cost": 465000.0
},
"TUN-FROST-01": {
"quantity": 360.0,
"unit_cost": 21500.0
},
"TUN-LYS-01": {
"quantity": 2400.0,
"unit_cost": 1850.0
},
"TUN-SPRENG-01": {
"quantity": 168000.0,
"unit_cost": 410.0
},
"TUN-SIKRING-01": {
"quantity": 2400.0,
"unit_cost": 6900.0
},
"TUN-PORTAL-01": {
"quantity": 2.0,
"unit_cost": 3850000.0
}
}
}

View file

@ -1,7 +0,0 @@
# Prosjektdokumenter
TOM med vilje. Scenarioet er MS Office + PDF, men ingen av dem kan mates
inn her: `--docs-dir`-omveien er fraradet (P13 - den omgar stigen `list_bundles -> read_bundle
-> read_dir -> read_file` og de to gatene, §4.1a-dimensjonen og verdict-laget). Veien et ekte
prosjektdokument skal ta er gjennom okf-ingest inn i en kunnskapsbase, altsa en base til - ikke
en katalog ved siden av. Se `docs/2026-09-12-p14-kontekstsett.md` § 1.4.

View file

@ -1,79 +0,0 @@
{
"project_id": "tunnel-hauglia-2027",
"bundle": "n500-2024",
"bundle_id": "vegnormal-n500-2024",
"must_cite": [
{
"approach_id": "a1-ventilasjon-dimensjonering",
"rationale": "Prosjekteringsgrunnlaget forutsetter full lengdeventilasjon over hele profilet. Vi vil vite om N500 tillater en lavere installert effekt for dette tunnelprofilet og denne ÅDT-en.",
"concepts": [
{
"path": "krav/N500/id-bfb0edb4-15ad-4fb1-96e8-a1ffb65f2b40.md",
"title": "Krav 10.4.3—1 Mekanisk ventilasjon (impulsventilator)",
"ref": "Krav 10.4.3—1"
},
{
"path": "krav/N500/id-e897fcb1-36ab-4198-ee5f-764833cce9d2.md",
"title": "Krav 10.4.3—2 Mekanisk ventilasjon (impulsventilator)",
"ref": "Krav 10.4.3—2"
},
{
"path": "krav/N500/id-15ccfe60-c88c-4174-8b28-5c660e6a7d9e.md",
"title": "Krav 10.4.3—3 Mekanisk ventilasjon (impulsventilator)",
"ref": "Krav 10.4.3—3"
}
]
},
{
"approach_id": "a2-frostsikring-lengde",
"rationale": "Frostsikringen er lagt 180 m inn fra hver portal. Spørsmålet er hva N500 faktisk krever av frostmengde og isolasjonsløsning.",
"concepts": [
{
"path": "krav/N500/id-92e2836a-90af-4797-df73-981ab02da81d.md",
"title": "Krav 8.1.2—1 Frostdimensjonering",
"ref": "Krav 8.1.2—1"
},
{
"path": "krav/N500/id-7c24c166-3659-414c-fe81-07373f33b2b5.md",
"title": "Krav 8.4.2—1 Frostisolering med PE-skum eller XPS",
"ref": "Krav 8.4.2—1"
},
{
"path": "krav/N500/id-2b68ddbd-2037-469f-a181-b5db969dc829.md",
"title": "Krav 8.4.2—2 Frostisolering med PE-skum eller XPS",
"ref": "Krav 8.4.2—2"
}
]
},
{
"approach_id": "a3-belysning-adaptiv",
"rationale": "Belysningsanlegget er dimensjonert for konstant nivå. Vi vil vite hvilke kvalitetskrav N500 stiller før vi kan kutte installert effekt.",
"concepts": [
{
"path": "krav/N500/id-dc16192f-4ef6-41e7-b47f-be8f1cd581fb.md",
"title": "Krav 10.3.2—5 Belysningens kvalitet",
"ref": "Krav 10.3.2—5"
},
{
"path": "krav/N500/id-d77adb34-725c-4826-eb26-5fc72f27e711.md",
"title": "Krav 10.3.3—1 Belysning av tunnelveggene",
"ref": "Krav 10.3.3—1"
}
]
}
],
"honesty": "Prosjektet Hauglia-tunnelen er KONSTRUERT av meg: navn, lengde, ÅDT og alle tre kostlinjene (TUN-VENT-01, TUN-FROST-01, TUN-LYS-01) er oppdiktet, og de tre beløpene er plausible størrelsesordener jeg har satt, ikke tall fra et prosjekt. Det som IKKE er konstruert er kravene: hver konsept-sti, tittel og kravnummer i must_cite er lest ordrett ut av n500-2024-basens egen frontmatter. N500 bærer ingen priser, så kodene finnes ikke i basen — settet er derfor bevisst IKKE kjørbart via --proposals-from-mandate. P16: den fjerde tilnaermingen a4-enhetspris-ventilator og dens kostkode TUN-VENT-ENHET er ogsaa KONSTRUERT, og med vilje: den er falsifiseringsarmen, en kostlinje basen ikke baerer grunnlaget for. Beloepet er en satt stoerrelsesorden. P21: settet har naa sin egen cost-baseline.json (6 linjer) — prosjektets prisskjema. Mengdene og enhetsprisene der er OPPDIKTEDE stoerrelsesordener, satt slik at hver av a1-a3 har sin kostlinje og a4 IKKE har en; N500 baerer ingen priser, saa ingen av tallene er lest noe sted.",
"must_refuse": [
{
"approach_id": "a4-enhetspris-ventilator",
"anchors": [
"driftskostnad",
"enhetspris",
"markedspris",
"nåverdi",
"timepris"
],
"rationale": "N500 er en vegnormal og baerer ingen priser, ingen markedspriser, ingen timepriser og ingen naverdiberegning. Et belop pa denne linja har intet grunnlag i basen. Spoersmaalene denne approachen staar for: Hva koster en impulsventilator per stk levert og montert i norsk tunnelentreprise? | Hva er nåverdien over 20 år av å velge adaptiv LED framfor fast belysningsnivå? | Hvilken timepris skal legges til grunn for elektromontør i dette prosjektet?"
}
]
}

View file

@ -1,46 +0,0 @@
{
"objective": "Kutt kostnad i byggefasen for Hauglia-tunnelen (2,4 km ettløps vegtunnel, ÅDT 7 200) uten å bryte et eneste krav i N500.",
"success_criteria": "Minst én tilnærming validerer mot N500, og hver validerte tilnærming peker på de kravene i N500 som faktisk binder den.",
"approaches": [
{
"id": "a1-ventilasjon-dimensjonering",
"label": "Redusert antall impulsventilatorer ved ny dimensjonering av lengdeventilasjon",
"description": "Prosjekteringsgrunnlaget forutsetter full lengdeventilasjon over hele profilet. Vi vil vite om N500 tillater en lavere installert effekt for dette tunnelprofilet og denne ÅDT-en.",
"affected_codes": [
"TUN-VENT-01"
],
"claimed_saving_nok": 1850000.0,
"bundle_id": "vegnormal-n500-2024"
},
{
"id": "a2-frostsikring-lengde",
"label": "Kortere frostsikret sone ved begge portaler",
"description": "Frostsikringen er lagt 180 m inn fra hver portal. Spørsmålet er hva N500 faktisk krever av frostmengde og isolasjonsløsning.",
"affected_codes": [
"TUN-FROST-01"
],
"claimed_saving_nok": 2400000.0,
"bundle_id": "vegnormal-n500-2024"
},
{
"id": "a3-belysning-adaptiv",
"label": "Adaptiv LED-belysning i stedet for fast installert nivå",
"description": "Belysningsanlegget er dimensjonert for konstant nivå. Vi vil vite hvilke kvalitetskrav N500 stiller før vi kan kutte installert effekt.",
"affected_codes": [
"TUN-LYS-01"
],
"claimed_saving_nok": 1200000.0,
"bundle_id": "vegnormal-n500-2024"
},
{
"id": "a4-enhetspris-ventilator",
"label": "Billigere enhetspris pa impulsventilator",
"description": "Vi vil kutte ved a legge en lavere enhetspris per impulsventilator til grunn.",
"affected_codes": [
"TUN-VENT-ENHET"
],
"claimed_saving_nok": 900000.0,
"bundle_id": "vegnormal-n500-2024"
}
]
}

File diff suppressed because it is too large Load diff

View file

@ -1,450 +0,0 @@
<meta charset="utf-8">
<title>Systemet som sier nei til seg selv</title>
<style>
:root {
--ground: #F6F5F1;
--surface: #FFFFFF;
--surface-2: #EFEDE6;
--ink: #22272B;
--muted: #5C6570;
--line: #D9D6CC;
--accent: #C89B00;
--accent-ink: #7A5F00;
--steel: #35566F;
--ok-bg: #E3F0E7; --ok-fg: #1F5C38;
--warn-bg: #F6ECD4; --warn-fg: #7A5410;
--bad-bg: #F5E0DD; --bad-fg: #8C3128;
--code-bg: #EEECE4;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--ground: #15181B;
--surface: #1D2126;
--surface-2: #23282E;
--ink: #E9E7E1;
--muted: #9AA3AC;
--line: #343A41;
--accent: #E3B93F;
--accent-ink: #E3B93F;
--steel: #8FB4D2;
--ok-bg: #1E3327; --ok-fg: #8FCCA6;
--warn-bg: #38301A; --warn-fg: #E0BE6A;
--bad-bg: #3A2523; --bad-fg: #E09A92;
--code-bg: #232830;
}
}
:root[data-theme="dark"] {
--ground: #15181B;
--surface: #1D2126;
--surface-2: #23282E;
--ink: #E9E7E1;
--muted: #9AA3AC;
--line: #343A41;
--accent: #E3B93F;
--accent-ink: #E3B93F;
--steel: #8FB4D2;
--ok-bg: #1E3327; --ok-fg: #8FCCA6;
--warn-bg: #38301A; --warn-fg: #E0BE6A;
--bad-bg: #3A2523; --bad-fg: #E09A92;
--code-bg: #232830;
}
* { box-sizing: border-box; }
body {
background: var(--ground);
color: var(--ink);
font-family: Charter, "Bitstream Charter", Cambria, Georgia, serif;
font-size: 17px;
line-height: 1.65;
margin: 0;
padding: 0 20px 80px;
}
.page { max-width: 860px; margin: 0 auto; }
.prose { max-width: 72ch; }
h1, h2, h3, h4, .sans {
font-family: -apple-system, "Segoe UI", system-ui, "Helvetica Neue", Arial, sans-serif;
}
h1 { font-size: 2.1rem; font-weight: 650; letter-spacing: -0.015em; line-height: 1.15; text-wrap: balance; margin: 0.4rem 0 0.6rem; }
h2 { font-size: 1.35rem; font-weight: 650; letter-spacing: -0.01em; margin: 0 0 0.9rem; text-wrap: balance; }
h3 { font-size: 1.05rem; font-weight: 650; margin: 1.6rem 0 0.5rem; }
p { margin: 0 0 1rem; }
a { color: var(--steel); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
strong { font-weight: 650; }
.eyebrow {
font-family: -apple-system, "Segoe UI", system-ui, sans-serif;
font-size: 0.72rem; font-weight: 650;
text-transform: uppercase; letter-spacing: 0.09em;
color: var(--accent-ink);
}
header.doc { padding: 56px 0 8px; }
.meta { display: flex; flex-wrap: wrap; gap: 8px; margin: 14px 0 0; }
.chip {
font-family: -apple-system, "Segoe UI", system-ui, sans-serif;
font-size: 0.78rem; color: var(--muted);
border: 1px solid var(--line); border-radius: 999px;
padding: 3px 11px; background: var(--surface);
}
.lead { font-size: 1.06rem; color: var(--muted); max-width: 66ch; margin-top: 10px; }
section { border-top: 1px solid var(--line); padding: 34px 0 10px; }
.secmark { display: flex; align-items: baseline; gap: 12px; margin-bottom: 14px; }
.secmark .no {
font-family: -apple-system, "Segoe UI", system-ui, sans-serif;
font-variant-numeric: tabular-nums;
font-size: 0.8rem; font-weight: 650; color: var(--accent-ink);
border-bottom: 2px solid var(--accent); padding-bottom: 2px;
}
.callout {
background: var(--surface);
border: 1px solid var(--line);
border-left: 3px solid var(--accent);
padding: 18px 22px;
font-size: 1.08rem;
max-width: 72ch;
}
.callout p { margin: 0; }
.callout p + p { margin-top: 0.8rem; }
.flag { color: var(--warn-fg); font-weight: 600; white-space: nowrap; }
code {
font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
font-size: 0.85em;
background: var(--code-bg);
border-radius: 3px;
padding: 1px 5px;
}
/* Forbehold */
.forbehold { display: grid; gap: 14px; margin: 14px 0 6px; }
.fb {
background: var(--surface);
border: 1px solid var(--line);
padding: 16px 20px;
display: grid; grid-template-columns: 34px 1fr; gap: 14px;
}
.fb .n {
font-family: -apple-system, "Segoe UI", system-ui, sans-serif;
font-weight: 650; font-size: 1.1rem; color: var(--accent-ink);
font-variant-numeric: tabular-nums; line-height: 1.5;
}
.fb p { margin: 0; }
.fb .t { font-family: -apple-system, "Segoe UI", system-ui, sans-serif; font-weight: 650; display: block; margin-bottom: 4px; }
/* Regnestykket på skjermen */
.tally { display: grid; gap: 8px; margin: 18px 0 20px; }
.tally-row {
display: grid; grid-template-columns: 132px 1fr;
gap: 16px; align-items: baseline;
background: var(--surface); border: 1px solid var(--line);
border-left: 3px solid var(--line);
padding: 12px 18px;
font-family: -apple-system, "Segoe UI", system-ui, sans-serif;
font-size: 0.92rem;
}
.tally-row.hit { border-left-color: var(--accent); background: var(--surface-2); }
.tally-row .amt { font-weight: 650; font-variant-numeric: tabular-nums; white-space: nowrap; font-size: 1.02rem; }
.tally-row .what { color: var(--muted); }
.tally-row .what b { color: var(--ink); font-weight: 650; }
@media (max-width: 560px) {
.tally-row { grid-template-columns: 1fr; gap: 4px; }
}
/* Faser / steg */
.phase {
background: var(--surface);
border: 1px solid var(--line);
margin: 0 0 18px;
padding: 20px 24px 14px;
}
.phase-head { display: flex; align-items: baseline; gap: 14px; border-bottom: 1px solid var(--line); padding-bottom: 12px; margin-bottom: 14px; flex-wrap: wrap; }
.phase-no {
font-family: -apple-system, "Segoe UI", system-ui, sans-serif;
font-weight: 700; font-size: 0.95rem;
color: var(--accent-ink);
border: 2px solid var(--accent); border-radius: 4px;
padding: 1px 8px; white-space: nowrap;
}
.phase-title { font-family: -apple-system, "Segoe UI", system-ui, sans-serif; font-weight: 650; font-size: 1.08rem; }
.phase-when { color: var(--muted); font-family: -apple-system, "Segoe UI", system-ui, sans-serif; font-size: 0.85rem; margin-left: auto; white-space: nowrap; }
.phase h4 {
font-size: 0.74rem; font-weight: 650; text-transform: uppercase; letter-spacing: 0.08em;
color: var(--muted); margin: 1.1rem 0 0.4rem;
}
.phase ul, .prose ul, .prose ol { margin: 0 0 1rem; padding-left: 1.3rem; }
.phase li, .prose li { margin-bottom: 0.45rem; }
.phase p:last-child { margin-bottom: 0.6rem; }
.krit { background: var(--surface-2); border-left: 3px solid var(--accent); padding: 10px 16px; font-size: 0.95rem; }
.krit p { margin: 0; }
/* Tabeller */
.table-scroll { overflow-x: auto; border: 1px solid var(--line); background: var(--surface); margin: 14px 0 20px; }
table {
border-collapse: collapse; width: 100%;
font-family: -apple-system, "Segoe UI", system-ui, sans-serif;
font-size: 0.86rem; line-height: 1.5;
font-variant-numeric: tabular-nums;
}
th {
text-align: left; font-weight: 650; font-size: 0.74rem;
text-transform: uppercase; letter-spacing: 0.06em;
color: var(--steel);
border-bottom: 2px solid var(--line);
padding: 10px 14px; white-space: nowrap;
}
td { border-bottom: 1px solid var(--line); padding: 10px 14px; vertical-align: top; }
tr:last-child td { border-bottom: none; }
td.num { white-space: nowrap; color: var(--muted); }
.pill {
display: inline-block; border-radius: 999px;
padding: 1px 10px; font-size: 0.78rem; font-weight: 600; white-space: nowrap;
}
.pill.ok { background: var(--ok-bg); color: var(--ok-fg); }
.pill.warn { background: var(--warn-bg); color: var(--warn-fg); }
.pill.bad { background: var(--bad-bg); color: var(--bad-fg); }
.foot { border-top: 1px solid var(--line); margin-top: 40px; padding-top: 18px; color: var(--muted); font-size: 0.85rem; font-family: -apple-system, "Segoe UI", system-ui, sans-serif; }
/* TOC */
nav.toc {
font-family: -apple-system, "Segoe UI", system-ui, sans-serif;
font-size: 0.88rem;
display: flex; flex-wrap: wrap; gap: 6px 18px;
padding: 16px 0 26px;
}
nav.toc a { color: var(--muted); text-decoration: none; }
nav.toc a:hover { color: var(--steel); text-decoration: underline; }
nav.toc .no { color: var(--accent-ink); font-weight: 650; font-size: 0.78rem; margin-right: 4px; }
</style>
<div class="page">
<header class="doc">
<div class="eyebrow">Demo-underlag · portfolio-optimiser v1.0.0</div>
<h1>Systemet som sier nei til seg selv</h1>
<p class="lead">Et rammeverk som leter etter kostnadsbesparelser inne i hvert prosjekt — der ingen besparelse er godkjent før et deterministisk regnestykke har fått avvise den, og der din fagvurdering blir varig kunnskap i systemet. Dette underlaget er skrevet for deg som kan faget, ikke maskineriet, og som skal kunne svare for dette overfor dem som sitter på budsjettet.</p>
<div class="meta">
<span class="chip">13. august 2026</span>
<span class="chip">Kode: v1.0.0, 810 tester grønne</span>
<span class="chip">Demoen er skriptet — se del 6</span>
<span class="chip">⚠️ = ikke verifisert</span>
</div>
</header>
<nav class="toc" aria-label="Innhold">
<a href="#anbefaling"><span class="no">1</span>Kortversjonen</a>
<a href="#problemet"><span class="no">2</span>Problemet</a>
<a href="#grepet"><span class="no">3</span>Grepet</a>
<a href="#skjermen"><span class="no">4</span>Det du ser</a>
<a href="#fagfolk"><span class="no">5</span>Fagfolkene</a>
<a href="#forbehold"><span class="no">6</span>Tre forbehold</a>
<a href="#status"><span class="no">7</span>Status i dag</a>
<a href="#neste"><span class="no">8</span>Hva vi ber om</a>
<a href="#sporsmaal"><span class="no">9</span>Spørsmål du får</a>
<a href="#verifisering"><span class="no">10</span>Verifiseringslogg</a>
</nav>
<section id="anbefaling">
<div class="secmark"><span class="no">1</span><h2>Kortversjonen</h2></div>
<div class="callout">
<p>Det finnes mange verktøy som kan <em>foreslå</em> kostnadskutt. Problemet i en offentlig etat er ikke å få forslag — det er å vite hvilke av dem som tåler å bli lagt fram. <strong>Dette systemet er bygget rundt en kontroll som kan avvise systemets eget beste forslag, og som gjør det uten å spørre modellen om lov.</strong></p>
<p>I demoen skjer nettopp det: forslaget påstår 2,1 millioner i besparelse, kontrollen regner etter og avviser det, og det som til slutt godkjennes er 445 500 kroner. <strong>Det er ikke en svakhet ved demoen — det er produktet.</strong></p>
</div>
<div class="prose">
<p>Kjernen er én setning: <em>maskinen får foreslå, men den får ikke godkjenne seg selv — og kontrollen som avgjør er vanlig regnekode, ikke en språkmodell.</em> Resten av dokumentet er belegg for den setningen, og forbeholdene i del 6 avgrenser hva den ikke betyr.</p>
</div>
</section>
<section id="problemet">
<div class="secmark"><span class="no">2</span><h2>Problemet vi prøver å løse</h2></div>
<div class="prose">
<p><strong>Et forslag om penger er verdiløst hvis ingen kan si om tallet holder.</strong> En språkmodell kan skrive et velformulert notat om at man sparer to millioner på å bytte armaturer. Notatet vil se riktig ut, argumentene vil henge sammen, og kildene vil bli nevnt. Det som mangler er den ene tingen en etat trenger før tallet kan brukes: noen som har regnet etter, uavhengig av den som foreslo.</p>
<p><strong>Det er derfor KI stopper ved notatet i dag.</strong> Forslaget må uansett gjennom en manuell fagvurdering før noen tør å bruke det, og da har man flyttet arbeid, ikke spart det. Verre: et flytende formulert feilaktig tall er farligere enn ingen tall, fordi det er vanskeligere å avvise i et møte.</p>
<p><strong>Og det andre problemet: fagvurderingen forsvinner.</strong> Når en erfaren fagperson sier «dette realiseres erfaringsvis ikke fullt ut i drift», blir det stående i en e-post eller i et referat. Neste gang samme spørsmål dukker opp, i et annet prosjekt, må vedkommende si det på nytt. Kunnskapen finnes i organisasjonen, men den akkumulerer ikke noe sted et system kan bruke den.</p>
</div>
</section>
<section id="grepet">
<div class="secmark"><span class="no">3</span><h2>Grepet — to uavhengige kontroller, og det er regnestykket som blokkerer</h2></div>
<div class="prose">
<p>Systemet setter to helt ulike kontroller på hvert forslag, og det er avgjørende at de er ulike:</p>
<ul>
<li><strong>Den ene leser resonnementet.</strong> En egen agent har som eneste jobb å angripe begrunnelsen: henger argumentet sammen, er forutsetningene rimelige, er noe utelatt? Dette er språkarbeid, og en språkmodell er god til det.</li>
<li><strong>Den andre regner.</strong> En deterministisk kontroll — vanlig programkode, ingen modell involvert — sjekker hver kostnadslinje mot prosjektets erklærte kostnadsgrunnlag og kjører beregningen som avgjør om beløpet er innenfor det som er praktisk oppnåelig. Den kan ikke overtales, den gir samme svar hver gang, og den er obligatorisk.</li>
</ul>
<p><strong>Når de er uenige, vinner den som regner.</strong> Det er hele arkitekturen i én setning. En godkjennelse fra språkmodellen er ikke nok til å slippe et tall gjennom; en avvisning fra regnestykket er nok til å stoppe det.</p>
<p>Kontrollen gjør dessuten én ting til, før den i det hele tatt begynner å regne: den sjekker at kostnadslinjene forslaget viser til, <em>finnes i prosjektet</em>, og at mengdene og enhetsprisene stemmer med det prosjektet faktisk har oppgitt. Et oppdiktet tall kommer altså aldri fram til beregningen. Og systemet retter ikke opp — det avviser. Å la maskinen «korrigere» et tall til noe som passer, ville vært den ene tingen som gjorde hele kontrollen verdiløs.</p>
</div>
</section>
<section id="skjermen">
<div class="secmark"><span class="no">4</span><h2>Det du ser på skjermen — fire bevegelser</h2></div>
<div class="prose">
<p>Demoen kjører på under tre sekunder og viser ett prosjekt: utskifting av veglysarmaturer på en fylkesvei. Den går gjennom åtte steg, men for å gjenfortelle den holder det med fire bevegelser.</p>
</div>
<div class="phase">
<div class="phase-head"><span class="phase-no">1</span><span class="phase-title">Den leser seg opp</span><span class="phase-when">skjermens øverste del</span></div>
<p>Systemet navigerer seg gjennom en kunnskapsbase om prosjektet — fagkilder, tidligere tiltak, metodebeskrivelser. Det er verdt å merke seg at det <em>navigerer</em>: det følger lenker mellom dokumentene slik et menneske ville gjort, i stedet for å klippe ut tekstbiter som ligner på søkeordene.</p>
<p>Legg merke til linja som sier <strong>«tidligere dommer hentet for kandidaten: 0»</strong>. Første kjøring skjer mot en tom erfaringsbase, med vilje. Det er kontrollen som gjør at vi senere kan bevise at læringen faktisk skjedde, og ikke bare lå der fra før.</p>
</div>
<div class="phase">
<div class="phase-head"><span class="phase-no">2</span><span class="phase-title">Den foreslår, og en annen agent utfordrer</span><span class="phase-when">Steg 2–3</span></div>
<p>Forslaget kommer med parametere og kostnadslinjer: bytte 2 500 eldre armaturer, påstått besparelse <strong>2 100 000 kroner</strong>. En andre agent går løs på begrunnelsen og konkluderer med at resonnementet holder.</p>
<p>På dette punktet ville de fleste KI-verktøy vært ferdige. Her er det halvveis.</p>
</div>
<div class="phase">
<div class="phase-head"><span class="phase-no">3</span><span class="phase-title">Regnestykket avviser det</span><span class="phase-when">Steg 4–6 · øyeblikket å legge merke til</span></div>
<p>Den deterministiske kontrollen regner, og avviser:</p>
<div class="tally">
<div class="tally-row"><span class="amt">2 100 000 kr</span><span class="what">påstått av forslaget, og <b>godkjent av den agenten som leste resonnementet</b></span></div>
<div class="tally-row"><span class="amt">1 769 915 kr</span><span class="what">det kontrollen regner ut som realistisk øvre grense for dette prosjektet</span></div>
<div class="tally-row hit"><span class="amt">445 500 kr</span><span class="what"><b>det som til slutt godkjennes</b>, etter at forslaget er bedt om å prøve på nytt med begrunnelsen for avvisningen i hånda</span></div>
</div>
<p>Avvisningen sendes tilbake til forslagsstilleren som en begrunnelse, ikke som et blankt nei — men forsøkene er <strong>tellet og begrenset</strong>. Systemet får ikke lov til å prøve i det uendelige til noe glir gjennom. Det er forskjellen på en kontroll og en formalitet.</p>
<div class="krit"><p><strong>Setningen som bærer det hele:</strong> den ene kontrollen godkjente resonnementet, den andre avviste tallet — og det er den som regner som blokkerer.</p></div>
</div>
<div class="phase">
<div class="phase-head"><span class="phase-no">4</span><span class="phase-title">En fagperson dømmer, og systemet husker det</span><span class="phase-when">Steg 7–8 og kjøring B</span></div>
<p>En fagekspert vurderer utfallet og godkjenner det — men med en korreksjon: i drift realiseres erfaringsvis rundt <strong>79 %</strong> av en slik beregnet besparelse. Den vurderingen løftes inn i kunnskapsbasen.</p>
<p>Så kjøres det samme prosjektet én gang til. Nå står det <strong>3</strong> tidligere dommer i stedet for 0, og fagpersonens korreksjon er med i grunnlaget når neste forslag formes. Utfallet blir det samme tiltaket til samme beløp — og det er riktig og verdt å si høyt: <strong>læringen endret ikke svaret her, den endret grunnlaget svaret ble formet på.</strong></p>
</div>
</section>
<section id="fagfolk">
<div class="secmark"><span class="no">5</span><h2>Din rolle — hvorfor dette ikke er «KI som erstatter fagvurdering»</h2></div>
<div class="prose">
<p>Mennesker er inne i begge ender av kjeden, og bevisst ikke i midten. Dere lager kunnskapsgrunnlaget systemet leser, og dere dømmer utfallet når maskinen er ferdig. Grovarbeidet i mellom — å gå gjennom prosjekt etter prosjekt og lete etter kandidater — er det maskinen gjør. <strong>Det er ikke dømmekraften som settes ut; det er letingen.</strong></p>
<p><strong>Vurderingen kan avgis når det passer deg.</strong> Systemet venter ikke med åpen skjerm. Du kan legge svaret ditt i en innboks dager etter kjøringen, i ditt eget fagspråk, og neste kjøring plukker det opp. Demoen viser begge tidsskalaene: en vurdering avgitt underveis, og et driftsnotat som kom etterpå.</p>
<p><strong>Bare det et menneske har godkjent, blir varig kunnskap.</strong> Porten inn til kunnskapsbasen er stengt for alt annet: rå maskinoutput kommer aldri inn. Det er den mekanismen som hindrer at systemet over tid lærer av seg selv og driver av gårde.</p>
<p>Fagpersonenes egen gjennomgang av hva dette betyr for dem, ligger i det andre underlaget til denne demoen — <em>«Fagfolk dømmer. Maskinen gjør grovarbeidet.»</em></p>
</div>
</section>
<section id="forbehold">
<div class="secmark"><span class="no">6</span><h2>Tre forbehold</h2></div>
<div class="prose"><p>Grunnregelen systemet er bygget på, er at det ikke får påstå mer enn det gjør. En demo som overselger, bryter med akkurat det den demonstrerer — så disse tre står like tydelig som resten.</p></div>
<div class="forbehold">
<div class="fb"><span class="n">1</span>
<p><span class="t">Agentenes svar i demoen er skriptet — det er ingen levende språkmodell i rommet.</span>Det som demonstreres er at dataflyten virker, at den deterministiske ryggraden faktisk blokkerer, og at læringssløyfa lukkes. Rammeverket selv har kjørt mot en levende modell én gang, 14. august 2026 — etter at dette underlaget ble skrevet: modellen svarte i den formen systemet bestiller, den fant opp en kostnadslinje som ikke finnes i kunnskapsbasen, og regneporten avviste forslaget. At et forslag fra en levende modell kommer <em>gjennom</em> porten, er fortsatt ikke vist.</p>
</div>
<div class="fb"><span class="n">2</span>
<p><span class="t">Kunnskapsbasen i demoen er laget for hånd.</span>Et menneske har skrevet den. Det finnes en vei for å hente eksterne kilder inn i formatet — og den skanner nå innholdet for manipulert kildetekst før det skrives — men eksempelet her gikk ikke gjennom den, og den generiske «fabrikken» som skal produsere slike baser for vilkårlige fagområder, er bevisst ikke bygget ennå.</p>
</div>
<div class="fb"><span class="n">3</span>
<p><span class="t">Tallene er modellerte, ikke målte.</span><span class="flag">⚠️</span> Ingen pilot har validert dem i drift. 445 500 kroner er hva beregningen gir for et syntetisk eksempel med oppgitte forutsetninger — det er ikke en besparelse noen har realisert. <strong>Ingen bør sitere et kronebeløp fra denne demoen som en oppnådd gevinst.</strong> Det demoen viser, er at metoden avviser det den ikke kan forsvare; hva den er verdt i kroner, er nettopp det en pilot skal svare på.</p>
</div>
</div>
</section>
<section id="status">
<div class="secmark"><span class="no">7</span><h2>Status i dag — hva finnes, og hva finnes ikke</h2></div>
<div class="table-scroll">
<table>
<thead><tr><th>Område</th><th>Status</th><th>Hva det betyr</th></tr></thead>
<tbody>
<tr><td>Hele kjeden fra kontekst til lagret dom</td><td><span class="pill ok">Bygget</span></td><td>Alle åtte steg er koblet sammen og kjører ende til ende. Det er dette demoen viser.</td></tr>
<tr><td>Den deterministiske kontrollen</td><td><span class="pill ok">Bygget</span></td><td>Obligatorisk og blokkerende — kan ikke slås av eller gjøres til en valgfri tilleggsmodul.</td></tr>
<tr><td>Læring fra fagvurderinger</td><td><span class="pill ok">Bygget</span></td><td>Begge tidsskalaer: vurdering avgitt underveis, og vurdering avgitt dager etterpå.</td></tr>
<tr><td>Kodekvalitet</td><td><span class="pill ok">810 tester</span></td><td>810 automatiske tester går grønt, 4 er hoppet over. Målt i dag på den versjonen som demonstreres.</td></tr>
<tr><td>Kjøring mot ekte språkmodell</td><td><span class="pill warn">Ikke prøvd i skala</span></td><td>Rammeverket støtter det (Azure og lokal profil), men er ikke kjørt i omfang — det koster penger vi ikke har brukt.</td></tr>
<tr><td>Fabrikk for kunnskapsbaser</td><td><span class="pill warn">Bevisst utsatt</span></td><td>Kunnskapsbaser lages for hånd i dag. Å automatisere det er et eget prosjekt.</td></tr>
<tr><td>Pilot på ekte prosjektdata</td><td><span class="pill bad">Ikke gjort</span></td><td>Dette er hovedhullet, og det er dette del 8 handler om.</td></tr>
</tbody>
</table>
</div>
<div class="prose">
<p>Koden er åpen og fritt tilgjengelig (MIT-lisens), bygget på Microsofts Agent Framework. Det er ingen leverandørbinding og ingen lisenskostnad i selve rammeverket.</p>
</div>
</section>
<section id="neste">
<div class="secmark"><span class="no">8</span><h2>Hva vi ber om</h2></div>
<div class="callout">
<p><strong>Vi ber ikke om en budsjettpost. Vi ber om en beslutning om å prøve metoden på ekte tall, én gang, i avgrenset form.</strong></p>
</div>
<div class="prose">
<p>Det er den ærlige bestillingen på dette stadiet. Å be om finansiering av et program før metoden har møtt ekte prosjektdata, ville vært å be om tillit vi ikke har målt oss fram til ennå — og det er den samme feilen systemet selv er bygget for å unngå.</p>
</div>
<h3>Hva en pilot krever</h3>
<div class="prose">
<ol>
<li><strong>Én portefølje med ekte kostnadstall.</strong> Ikke en stor en. Metoden trenger prosjekter med et oppgitt kostnadsgrunnlag å avstemme mot — det er nettopp det avstemmingen forutsetter.</li>
<li><strong>Navngitte fagpersoner som får dømme.</strong> Uten dem finnes ingen læringssløyfe, og da er halve poenget borte. Innsatsen per vurdering er liten, men den må være noens jobb, ikke noens overskuddstid.</li>
<li><strong>Et modellbudsjett.</strong> <span class="flag">⚠️</span> Størrelsen er ikke estimert ennå — den avhenger av hvor mange prosjekter piloten omfatter, og må regnes ut når omfanget er valgt. Rammeverket har harde tak på forbruk innebygd, nettopp fordi kostnadskontroll ikke kan være noe man husker på.</li>
<li><strong>En avtalt målestokk på forhånd.</strong> Hva skal piloten ha vist for at den regnes som vellykket? Det bør bestemmes før den kjøres, ikke etterpå.</li>
</ol>
</div>
<h3>Hva piloten skal svare på</h3>
<div class="krit"><p>Finner metoden besparelser i ekte prosjekter som fagfolk faktisk godkjenner — og hvor mange av maskinens forslag blir avvist av kontrollen underveis? Begge tallene er interessante. Et system som aldri avviser noe, er ikke et system som har kontrollert noe.</p></div>
<h3>Hva vi ber om fra dere i dag</h3>
<div class="prose">
<p>Dere er de eneste i rommet som kan avgjøre det som betyr noe her, og det er verdt å si rett ut: <strong>vi ber dere gjøre mot denne metoden nøyaktig det systemet ber dere gjøre mot hvert enkelt forslag.</strong> Døm den. Tre spørsmål, og et ærlig nei på noen av dem er et nyttigere utfall enn en høflig ja:</p>
<ol>
<li><strong>Er dette gjenkjennelig fra ditt fagfelt?</strong> Er den typen tiltak, og den typen forbehold om realisering i drift, slik du ville formulert det selv?</li>
<li><strong>Ville du stolt på et tall som har vært gjennom denne kontrollen?</strong> Ikke stolt nok til å slutte å se på det — men nok til at det er verdt din tid å vurdere det.</li>
<li><strong>Er det verdt å prøve på ekte tall?</strong> Og i så fall: hvilken portefølje er den riktige å begynne med?</li>
</ol>
<p>Sier dere ja til det tredje, er det den anbefalingen som skal videre til budsjettsiden — <em>fra dere</em>, ikke fra teknologimiljøet. En metode for å vurdere kostnadstall har ikke troverdighet fordi den er teknisk velbygget; den har troverdighet når fagfolk med ansvar sier at den regner riktig.</p>
</div>
</section>
<section id="sporsmaal">
<div class="secmark"><span class="no">9</span><h2>Spørsmål du kan få — og svarene</h2></div>
<div class="table-scroll">
<table>
<thead><tr><th>Spørsmål</th><th>Svar</th></tr></thead>
<tbody>
<tr><td>Er dette en ekte KI-modell?</td><td>Ikke i demoen — agentsvarene er skriptet, og det står i åpningsbildet. Det som er ekte er dataflyten, den deterministiske kontrollen og at læringen faktisk går gjennom fil.</td></tr>
<tr><td>Hva hvis modellen finner på et tall?</td><td>Kontrollen avstemmer hver kostnadslinje mot prosjektets erklærte kostnadsgrunnlag før beregningen i det hele tatt starter. En ukjent kostnadskode, eller en mengde som ikke stemmer, blir avvist. Systemet retter ikke opp — det avviser.</td></tr>
<tr><td>Erstatter dette fagfolk?</td><td>Nei. Mennesker lager grunnlaget og dømmer utfallet. Maskinen gjør letearbeidet i mellom.</td></tr>
<tr><td>Hvorfor viste første kjøring null tidligere erfaringer?</td><td>Med vilje — første kjøring går mot tom erfaringsbase. Uten den kontrollen kunne man ikke skille «systemet lærte noe» fra «det lå der fra før».</td></tr>
<tr><td>Kan vi styre hva som analyseres?</td><td>Ja. En kjøring kan bestilles med en oppdragsfil der du skriver hva den er til for og hvilke tilnærminger du vil ha vurdert. Men bestillingen styrer hva som <em>vurderes</em>, aldri hva som <em>godkjennes</em> — kontrollen gjelder uendret. Det er ikke vist i denne demoen.</td></tr>
<tr><td>Hva koster det å bruke?</td><td>Rammeverket er åpen kildekode uten lisenskostnad. Driftskostnaden er modellbruk, og den har innebygde tak. <span class="flag">⚠️</span> Konkret beløp avhenger av omfang og er ikke estimert.</td></tr>
<tr><td>Går dataene våre ut av huset?</td><td>Det bestemmer den som setter det opp. Rammeverket kan kjøre helt lokalt, og det gjør ingen nettverkskall som ikke er konfigurert eksplisitt. Personvernvurdering og risikovurdering tilhører den som tar systemet i bruk — det er bevisst ikke bygget inn påstander om compliance.</td></tr>
<tr><td>Kan den kjøre en hel portefølje?</td><td>Biblioteket har porteføljekjøring med globalt kostnadstak. Denne demoen kjører <em>ett</em> prosjekt, og porteføljestien er ikke prøvekjørt denne uka — ikke lov bort en live demonstrasjon av den.</td></tr>
</tbody>
</table>
</div>
</section>
<section id="verifisering">
<div class="secmark"><span class="no">10</span><h2>Verifiseringslogg — hvor tallene i dette dokumentet kommer fra</h2></div>
<div class="prose"><p>Hver tallpåstand over er målt, ikke gjengitt fra hukommelsen. Dette er kildene, slik at den som blir utfordret kan svare presist.</p></div>
<div class="table-scroll">
<table>
<thead><tr><th>Påstand</th><th>Kilde</th><th>Status</th></tr></thead>
<tbody>
<tr><td>2 100 000 påstått · 1 769 915 grense · 445 500 godkjent</td><td>Det innsjekkede demo-transkriptet, linje 19, 24 og 27</td><td><span class="pill ok">Målt</span></td></tr>
<tr><td>Realiseringsgrad 79 % (fagpersonens korreksjon)</td><td>Samme transkript, linje 32 og 45</td><td><span class="pill ok">Målt</span></td></tr>
<tr><td>3 tidligere dommer i andre kjøring, 1 fra basen + 2 lært</td><td>Samme transkript, linje 50–51 — regnet ut av kjøringen selv</td><td><span class="pill ok">Målt</span></td></tr>
<tr><td>810 tester grønne, 4 hoppet over</td><td><code>uv run pytest</code> kjørt 13.08.2026 på v1.0.0</td><td><span class="pill ok">Målt</span></td></tr>
<tr><td>Demoen kjører på under 3 sekunder</td><td>Målt ved generalprøve 12.08.2026</td><td><span class="pill ok">Målt</span></td></tr>
<tr><td>Versjon v1.0.0 satt og publisert</td><td><code>git describe</code> og oppslag mot server, begge 13.08.2026</td><td><span class="pill ok">Målt</span></td></tr>
<tr><td>Kostnaden ved en pilot</td><td>Ingen — omfanget er ikke valgt ennå</td><td><span class="pill bad">Ikke estimert</span></td></tr>
<tr><td>Gevinst i kroner ved bruk i etaten</td><td>Ingen — ingen pilot har kjørt</td><td><span class="pill bad">Ikke målt</span></td></tr>
</tbody>
</table>
</div>
<div class="prose">
<p>De to nederste radene er de viktigste i tabellen. At de står der tomme, er ikke en mangel ved dokumentet — det er grunnen til at bestillingen i del 8 er en pilot og ikke et program.</p>
</div>
</section>
<div class="foot">
Underlag til demo 13. august 2026 · portfolio-optimiser v1.0.0 · åpen kildekode (MIT), bygget på Microsoft Agent Framework.<br>
Søsterdokument for fagekspertene: «Fagfolk dømmer. Maskinen gjør grovarbeidet.»
</div>
</div>

View file

@ -1,283 +0,0 @@
# Syretesten vei A/B — de tre Vegnormal-basene gjennom portfolio-optimiser
**Ordre:** `20260825T111038Z-1174613178-from-.claude` (programplanens spor 3, gap G12).
**Dato:** 2026-08-25 (økt 60). **Mandat: MÅL, IKKE BYGG.** Ingen fil under `src/` er endret;
`uv run pytest -q` → **1021 passed / 5 skipped (166 s)**, identisk med tallet før økten.
**Eksponerings-grense (ordrens harde krav, holdt):** dette repoet pusher til `open/`. Rapporten
bærer derfor kun **tall, stier, kommandoer og egne observasjoner**. Ingen bundle-fil er kopiert,
og ikke én linje kravtekst fra et konsept er lest inn eller gjengitt — alle konsept-tall under er
lengdemålinger, ikke innhold.
**Stopp-betingelsen var oppfylt:** multi-base-ordren `20260825T080753Z-103813595` lå i
`orders/archive/` (commit `18af86e` + `785261f`) da økten startet. Multi-base-formen er lest slik
den **faktisk landet** (`run.py`, `explore.py`, `mandate.py`, README), ikke slik planens § C.7
omtalte den.
---
## 1. Sammendrag — én setning per målepunkt
| # | Punkt | Status | Kjernetall |
|---|---|---|---|
| 1 | Katalogen (`index_summary`, konsepter, cost-baseline, verdicts) | **MÅLT** | 446 / 1017 / 270 konsepter; **0 av 3** har `cost-baseline.json`; **0 av 3** har `validator-input.json`; **0** `type: verdict`-filer i alle tre |
| 2 | Kontekstkostnad (`bundle_context(navigate_bundle(...))`) | **MÅLT** | 93 422 / 250 785 / 85 937 o200k_base-tokens — **sum 430 144** mot 3861/12595/10406 for de tre eksempelbundlene (instrumentet reproduserte commons' tall eksakt) |
| 3 | Dry-run med tre baser i multi-base-formen | **MÅLT — og formen finnes ikke fra CLI-en** | Fire `--bundle-dir` gir **exit 0** og en kjøring mot ÉN base (siste vinner, stille); bibliotekdøra `run_mandate_across_bundles` **ruter korrekt** men feiler i dispatch: `FileNotFoundError @ okf.py:433` |
| 4 | Offline-simuleringen med samme oppsett | **MÅLT** | Golden `demo-transcript.stdout` **BYTE-UENDRET** (`ea8c534773acdbe41ae68f2c55724d69aaf8be4f`), stderr 4 linjer; demoen **kan ikke** peke på en Vegnormal-base (samme `okf.py:433`) |
| 5 | `--explore` med full seks-felts `--explore-config` | **MÅLT — delvis vakuøst, som ordren forutså** | Sløyfa **fullfører** offline mot tre baser og returnerer et **rutet** mandat (`bundle_id='B-n100-…'`, `stop=None`); men **0 verktøykall** og **0 `quick_validate`** — navigatoren åpnet aldri en base. Fra CLI-en er utforskningen **ikke kjørbar offline i det hele tatt** (`KeyError: 'navigator'`) |
| 6 | G14: navigasjon inn i nestede `index.md` | **IKKE PRØVBAR HER — nevner oppgitt** | **0 nestede `index.md` av 1733 konsepter** i alle tre basene (0 underkataloger); kjent-positiv kontroll `nav-golden-hierarchy/bundle` finner 2 nestede index og 2 dypt-nådde kontekstfiler, så instrumentet **kan** se dem |
---
## 2. Tabellen (punkt 1 + 2)
Kommando bak hver rad: `okf.navigate_bundle(d)` → `okf.bundle_context(bundle)`, med
`tiktoken.get_encoding("o200k_base")` (kjørt via `uv run --with tiktoken`; `tiktoken` er **ikke**
lagt til som prosjekt-avhengighet).
| Base | Konsepter | Kontekstfiler | `index_summary` (tegn / tokens) | `bundle_context` tegn | bytes | **o200k_base-tokens** | verdicts | cost-baseline | validator-input | skipped links |
|---|---:|---:|---|---:|---:|---:|---:|:--:|:--:|---:|
| `B-n100-2023-uten-sources-importert` | 446 | 446 | 51 214 / 28 289 | 221 916 | 226 430 | **93 422** | 0 | nei | **nei** | 0 |
| `B-n200-2024-uten-sources-importert` | 1017 | 1017 | 116 879 / 64 764 | 616 179 | 626 034 | **250 785** | 0 | nei | **nei** | 0 |
| `B-n500-2024-uten-sources-importert` | 270 | 270 | 30 974 / 17 197 | 240 714 | 245 251 | **85 937** | 0 | nei | **nei** | 0 |
| **Sum, tre baser** | **1733** | **1733** | 199 067 / 110 250 | 1 078 809 | 1 097 715 | **430 144** | 0 | — | — | 0 |
| *kontroll:* `veglys-fv-soer` | 6 | 5 | 3 646 / — | 32 201 | 32 884 | **10 406** | 1 | ja | ja | 1 |
| *kontroll:* `tunnel-hauglia` | 6 | 5 | 4 763 / — | 39 583 | 40 475 | **12 595** | 1 | ja | ja | 1 |
| *kontroll:* `bygg-energi-mikro` | 5 | 4 | 1 884 / — | 12 005 | 12 270 | **3 861** | 1 | nei | ja | 1 |
**Instrumentet er validert mot kjent fasit** (Verifiseringsloven ansikt 4): de tre
kontrollradene reproduserer commons' egne tall — 3861 / 12 595 / 10 406 — eksakt. Uten den
kontrollen ville Vegnormal-tallene vært en måling ingen visste kunne treffe.
**Ordrens tall bekreftet mot ground truth** før noe ble bygget på dem: `447 / 1018 / 271` `.md`-filer
på disk = `446 / 1017 / 270` konsepter + `index.md` i hver. Hver `index.md` har nøyaktig like mange
lenker som det er konsepter (446 / 1017 / 270), alle unike, alle fulgt — `skipped = 0`.
**Det manageren faktisk ser.** Ett `list_bundles()`-kall (`explore.py:419-438`) returnerer hele
`index_summary` for **alle** baser samtidig:
| | tegn | bytes | **o200k_base-tokens** |
|---|---:|---:|---:|
| `list_bundles()` over de tre basene | 201 196 | 201 196 | **112 116** |
Dette er ett verktøykall, og det er katalogverktøyets **eneste** form.
### Hva tallene sier om `.claude`s § 9.1-analyse
`.claude` sin `docs/okf-bundle-prosessen.md § 9.1` («en fil uten lenke finnes ikke; alt som lenkes
leses helt») er **bekreftet mot et ekte korpus, og den er kostbar her**: alle 1733 konsepter er
lenket fra rot-`index.md`, `skipped = 0`, og «leses helt» betyr 430 144 tokens for de tre basene.
Progressiv disclosure gir ingen lettelse på denne bundle-formen, fordi importformen legger *alt*
på ett nivå — se funn **MINOR-1**.
---
## 3. Funn
### BLOCKER-1 — kontekstkostnaden gjør en live utforskning mot disse basene ugjennomførbar som de står
`list_bundles()` = **112 116 tokens** i ett kall; `read_bundle("B-n200-…")` = **250 785 tokens**.
`ExplorationContract.max_tokens` (`explore.py:70`) er ledgeren `BudgetMiddleware` håndhever, og et
enkelt katalogkall bruker mer enn et normalt tak. En 128k-modell kan ikke ta N200 i det hele tatt.
**Dette er ikke en defekt i rammeverket** — det er korpusets form møtt av § 9.1-kontrakten. Men det
er den harde grensen for vei A/B live, og den var ikke målt før i dag.
**Fil:linje:** `src/portfolio_optimiser/explore.py:419-438` (`list_bundles`), `:444-445` (`read_bundle`).
> **Oppdatert 2026-08-26 (økt 65, ordre `20260825T213645Z-9019120455`):** katalog-halvdelen er
> **lukket**. `list_bundles()` over de samme tre basene koster nå **362 tokens** (fra 112 116), og
> over alle 171 grenbaser **21 448** (fra 124 942). `read_bundle`-halvdelen ble lukket på korpussiden
> av `vegnormal-okf` `8145c23` (grener som egne baser). Måling og gate:
> [docs/2026-08-26-katalogkostnaden.md](2026-08-26-katalogkostnaden.md). Setningen over står som
> den ble målt 25.08 — den er historikk, ikke en gjeldende tilstand.
### MAJOR-1 — gjentatt `--bundle-dir` forkastes STILLE; kjøringen ser ut som multi-base og er det ikke
Ordrens pkt. 3 forutsatte at CLI-en tar tre baser. Målt:
```
uv run python -m portfolio_optimiser.run VEGLYS-FV-SOER \
--docs-dir shared/examples/veglys-fv-soer \
--bundle-dir <N100> --bundle-dir <N200> --bundle-dir <N500> \
--bundle-dir shared/examples/veglys-fv-soer --live-dry-run
→ EXIT 0, "VEGLYS-FV-SOER: LIVE-DRY-RUN OK (…)"
```
**Exit 0.** De tre Vegnormal-basene ble droppet uten ett ord. Bytter man rekkefølgen slik at en
Vegnormal-base står sist, feiler samme kommando i stedet (`live-dry-run refused: IR projection not
found in bundle: 'validator-input.json'`) — altså **siste `--bundle-dir` vinner**, som er argparse
sin default når `action="append"` mangler.
Dette er repoets egen defektklasse, anvendt på operatørflaten: hosting-whitelisten nekter ukjente
felt **ved navn** nettopp fordi stille dropping er uleselig utenfra, og multi-base-invarianten
avviser en andre `bundle_dir` på `run_project`-signaturen fordi den ville tvunget «et stille
velg-en». Her *er* det et stille velg-en — bare i argv i stedet for i signaturen.
**Fil:linje:** `src/portfolio_optimiser/run.py:1581-1583` (`add_argument("--bundle-dir", default=None…)`,
ingen `action="append"`), konsumert `run.py:2070` (`bundle_dirs=(args.bundle_dir,)`) og `run.py:607-610`.
**Minste ærlige rettelse (ikke bygget — ordren er MÅL, IKKE BYGG):** nekt et gjentatt `--bundle-dir`
ved navn, på linje med de åtte eksisterende utforskningsnektene. STATE fører allerede
«repeterbart `--bundle-dir`» som en **åpen operatørbeslutning** fra økt 58; denne målingen sier at
inntil den er tatt, er *stillheten* selv problemet — ikke fraværet av funksjonen.
### MAJOR-2 — `--explore --scripted-replies` krasjer med rå traceback: `KeyError: 'navigator'`
Den ene offline-døra CLI-en har til `--explore` er ubrukelig. `_SCRIPTED_ROLES = ("proposer",
"checker")` er debattens to roller; utforskningen trenger i tillegg `navigator`, `hypothesiser` og
`manager`. `_load_scripted_replies` er eksplisitt fail-fast **for de to den kjenner** («a missing
role would otherwise surface as a `KeyError` deep inside `scripted_factory`'s lookup, mid-run») —
og så inntreffer nøyaktig det den advarer mot, for de tre den ikke kjenner:
```
File ".../explore.py", line 576, in fresh_exploration_workflow
client_factory(role),
File ".../simulation.py", line 470, in factory
reply = replies[role]
KeyError: 'navigator'
```
Ingen `run refused:`-linje, ingen rc-1 med forklaring — en traceback, som er den kanalen
økt 57 betalte for å holde konfigurasjonsfeil UTE av.
**Positivt målt i samme kjøring:** `finally`-blokka holdt. `{run_id}-exploration.json` ble skrevet
selv om kjøringen krasjet, med `"completed": false` og `"stop": null` — nøyaktig det
`completed`-feltet finnes for.
**Fil:linje:** `src/portfolio_optimiser/run.py:1531` (`_SCRIPTED_ROLES`), `:1559-1564`
(fail-fast-listen), krasjer i `src/portfolio_optimiser/simulation.py:470`.
**Merk:** `simulation.scripted_exploration_factory` (`simulation.py:713-736`) dekker allerede alle
fem rollene. Sømmen finnes; CLI-en når den bare ikke.
### MAJOR-3 — regelverksbaser kan ikke være baser i multi-base-dispatchen (arkitektonisk, ikke en bug)
Bibliotekdøra `run_mandate_across_bundles` ble målt direkte med de tre basene og et mandat med én
approach per base:
```
STEG 1 route_by_bundle → B-n100…: ['a0'] B-n200…: ['a1'] B-n500…: ['a2'] ✅ korrekt partisjon
STEG 2 run_mandate_across_bundles → FileNotFoundError @ okf.py:433
"IR projection not found in bundle: 'validator-input.json'"
```
**Partisjonen virker perfekt.** Dispatchen gjør det ikke, fordi `run.py:1491` leser hver bases
prosjekt fra **den basens egen** IR-projeksjon — som er selve multi-base-invariantens designvalg
(«en kaller-oppgitt konstant kunne uansett bare vært riktig for én base av N»).
Konsekvensen er den viktigste innsikten i hele syretesten: **multi-base betyr N prosjekter, ikke
1 prosjekt × N referansebaser.** Vegnormal-basene er *regelverk* — de har verken prosjekt eller
kostbaseline, og skal ikke ha det. Ordrens mentale modell («ett veglysprosjekt + tre normalbaser
som kontekst») er en **annen form**, og den finnes allerede — bare ikke i dispatchen:
| Dør | `bundle_dirs` betyr | Passer regelverk? |
|---|---|---|
| `run.run_mandate_across_bundles` (`run.py:1388`) | N **prosjektbaser** → N kjøringer | **Nei** — krever `validator-input.json` per base |
| `explore.explore` (`explore.py:~840`) | N **lesekilder** for navigator/hypothesiser | **Ja** — målt, se under |
**Fil:linje:** `src/portfolio_optimiser/okf.py:431-433`, kalt fra `run.py:1491` (dispatchen),
`run.py:609` (enkeltkjøringen) og `simulation.py` (demoen) — alle tre feiler på samme sted.
### MINOR-1 — importformen legger alt på ett nivå, så progressiv disclosure gir null lettelse
0 underkataloger, 0 nestede `index.md`, 1733 av 1733 konsepter lenket direkte fra rot. Navigasjonen
har ingenting å utsette; hele korpuset er ett flatt nivå. Dette er en egenskap ved **kilden**, ikke
ved `okf.py`.
> **Rettet 2026-08-26 (økt 65): tilskrivelsen var feil, og `vegnormal-okf` har rett.** Flatheten er
> **Dør C** sin, ikke vegnormals emitterform. Verifisert mot kilden, ikke mot deres melding:
> `llm-ingestion-okf` `src/llm_ingestion_okf/importer.py` (§6-index-blokka) kaller
> `link_in_index(bundle, entry.path.name, _index_label(entry.concept_path))` per merget oppføring —
> altså én flat lenke i rot-`index.md` for hvert konsept, uansett hvor nestet konseptstien er.
> Vegnormals emitter skriver allerede et tonivåtre. Setningen over sto uendret som «vegnormal-okf
> sin importform» til dette punktet.
Dette var også hele grunnen til BLOCKER-1: med nestede indekser kunne manageren åpnet én gren om
gangen. Løsningen ble en annen — grener som **egne baser** (`vegnormal-okf` `8145c23`), med den
målte begrunnelsen at basegrensen er der OKF-navigasjonen stopper, så ingen indeksstruktur INNE i en
base senker prisen på å åpne den.
### NICE-1 — `read_bundle` nekter ukjent base ved navn, som lovet
```
read_bundle("finnes-ikke") → ExplorationError: unknown knowledge base 'finnes-ikke';
configured: B-n100-2023-…, B-n200-2024-…, B-n500-2024-…
```
Nekten navngir det konfigurerte settet. **Fil:linje:** `explore.py:390-393`.
---
## 4. Hva punkt 5 faktisk viste — og hvor grensen for offline går
Ordren ba om at vakuiteten skulle måles, ikke antas. Målt, med `scripted_exploration_factory`
(alle fem roller) mot de tre basene:
| Arm | Utfall |
|---|---|
| **B** — hypotese **uten** `bundle_id`, tre baser | `HypothesisParseError @ explore.py:732` — «a marked hypothesis must name its knowledge base when several are configured». **Multi-base-nekten fyrer korrekt mot et ekte korpus.** |
| **C** — hypotese **med** `bundle_id`, tre baser | `stop=None`, **1 approach**, `bundle_id='B-n100-2023-uten-sources-importert'`, 2 ledger-runder, 6 modell-prompts. **Sløyfa fullfører og produserer et rutet mandat.** |
Og så det ærlige forbeholdet, som er poenget:
- `quick_validate`-kall: **0**
- Prompt-strøm-forekomster av basenavnene: `N100` **2**, `N200` **0**, `N500` **0** — begge fra
instruksjonene, ingen fra et verktøyresultat.
**Navigatoren åpnet aldri en base.** Den scriptede klienten returnerer tekst og emitterer ingen
verktøykall — nøyaktig den grensen økt 56s måling allerede slo fast, nå bekreftet mot et eksternt
korpus. Lesesømmen **selv** er derimot bevist mot disse basene, ved direkte kall (samme kontroll
økt 56 måtte innføre da den oppdaget at armen lå utenfor gaten): `list_bundles()` → 3 baser,
`read_bundle()` → 221 916 / 616 179 / 240 714 tegn.
**Konklusjon for punkt 5, uten pynt:** *plumbingen* er bevist ende-til-ende mot ekte eksterne
baser — ruting, nekt, mandatform, artefaktskriving. *Verdien* er ikke bevist, og kan ikke bli det
offline. **Syretesten trenger en levende modell.** Det er et funn, ikke en feil.
---
## 5. Hva som MÅ til for en live-kjøring
Målt i denne økten, ikke antatt:
| # | Mangler | Målt tilstand | Konkret |
|---|---|---|---|
| 1 | **Kontekstbudsjettet** | `list_bundles()` = 112 116 tokens; `read_bundle(N200)` = 250 785 | **Den harde blokkeringen.** Enten en modell med svært stort vindu, eller — mer realistisk — en bundle-form med nestede indekser slik at manageren kan åpne én gren. Kilden eies av `vegnormal-okf`. |
| 2 | **Foundry-endepunkt** | `PORTFOLIO_FOUNDRY_PROJECT_ENDPOINT` **ikke satt**, `FOUNDRY_PROJECT_ENDPOINT` **ikke satt** | Én av de to må eksporteres. Låst av operatørbeslutningen om `DisableLocalAuth` (ordre `20260821T094949Z`, `docs/2026-08-18-vurdering-azure-omdoeping.md`). **Ikke rørt her.** |
| 3 | **Modell-map** | `PORTFOLIO_MODEL_MAP` ikke satt; `src/portfolio_optimiser/data/model_map.json` bærer `REPLACE-WITH-FOUNDRY-DEPLOYMENT` for alle azure-roller | `resolve_model("azure", r)` **nekter for alle fem roller**, inkl. `manager`/`navigator`/`hypothesiser`. Under `local` faller alle fem til `qwen3:4b` via `default` — utforskningsrollene er fortsatt ikke eksplisitt mappet (kjent ærlighets-grense fra økt 56). |
| 4 | **En prosjektbase** | 0 av 3 Vegnormal-baser har `validator-input.json` eller `cost-baseline.json` | Kjøringen trenger et **prosjekt** å optimere. Vegnormal-basene er regelverket det optimeres *innenfor*. Riktig oppsett: `--bundle-dir <prosjektbase>` for pipelinen + de tre normalbasene som `explore(bundle_dirs=…)`-lesekilder — men det krever MAJOR-1 løst, siden CLI-en i dag sender **én** base til begge. |
| 5 | **Offline-generalprøve** | `KeyError: 'navigator'` | MAJOR-2 må lukkes før en betalt kjøring, ellers er første live-kjøring også første gjennomkjøring. Repoets egen måleprotokoll: bevis så mye som mulig gratis, så en feil er attribuerbar. |
**Rekkefølge, uten å foregripe operatørens valg:** 5 → 1 → 2/3 → 4. Punkt 5 er gratis, punkt 1
avgjør om vei A/B i det hele tatt er mulig med denne bundle-formen, og punktene 2–3 koster penger
og er Azure-gatet.
---
## 6. Kommandologg
Hver tabellverdi over stammer fra én av disse, kjørt i denne økten:
```bash
# pkt 1: konsepter, index-lenker, verdicts, cost-baseline
find <base> -name '*.md' | wc -l ; grep -oE '\]\([^)]+\)' <base>/index.md | wc -l
grep -lE '^type: *verdict' <base>/*.md | wc -l
# pkt 1+2: navigasjon, kontekst, tokens (instrument validert mot commons' tre fasittall)
uv run --with tiktoken python # okf.navigate_bundle / okf.bundle_context / o200k_base
# pkt 3: CLI, fire --bundle-dir, begge rekkefølger
uv run python -m portfolio_optimiser.run VEGLYS-FV-SOER --docs-dir … --bundle-dir … --live-dry-run
# pkt 3: bibliotekdøra
python # mandate.route_by_bundle + run.run_mandate_across_bundles
# pkt 4: golden-regresjon
uv run python -m portfolio_optimiser.simulation | shasum # ea8c534773acdbe41ae68f2c55724d69aaf8be4f
# pkt 5: CLI-en, og deretter explore() direkte med alle fem roller scriptet
uv run python -m portfolio_optimiser.run … --explore … --explore-config … --scripted-replies …
# pkt 6: nestede index, med nav-golden-hierarchy som kjent-positiv kontroll
# regresjon
uv run pytest -q # 1021 passed, 5 skipped, 166.00s
```

View file

@ -17,7 +17,7 @@ kravtekst er gjengitt — alle konsept-tall er lengdemålinger, ikke innhold.
med `uv run --with tiktoken` (`tiktoken` er fortsatt **ikke** en prosjekt-avhengighet). Samme med `uv run --with tiktoken` (`tiktoken` er fortsatt **ikke** en prosjekt-avhengighet). Samme
instrument som syretesten 25.08. instrument som syretesten 25.08.
**Kjent-positiv kontroll (Verifiseringsloven ansikt 4):** de tre flate Vegnormal-basene målte **Kjent-positiv kontroll (Verifiseringsloven ansikt 4):** de tre flate kravbasene (et kravkorpus brukt under utviklingen) målte
**201 196 tegn / 112 116 tokens** før endringen — tallet syretesten publiserte, reprodusert eksakt. **201 196 tegn / 112 116 tokens** før endringen — tallet syretesten publiserte, reprodusert eksakt.
Uten den kontrollen ville «etter»-tallet vært en måling ingen visste kunne treffe. Uten den kontrollen ville «etter»-tallet vært en måling ingen visste kunne treffe.
@ -26,17 +26,17 @@ Uten den kontrollen ville «etter»-tallet vært en måling ingen visste kunne t
| Katalog | Baser (nevner) | Tegn før | **Tokens før** | Tegn etter | **Tokens etter** | Endring | | Katalog | Baser (nevner) | Tegn før | **Tokens før** | Tegn etter | **Tokens etter** | Endring |
|---|---:|---:|---:|---:|---:|---:| |---|---:|---:|---:|---:|---:|---:|
| Tre flate baser (syretestens sett) | 3 | 201 196 | **112 116** | 877 | **362** | **−99,7 %** | | Tre flate baser (syretestens sett) | 3 | 201 196 | **112 116** | 877 | **362** | **−99,7 %** |
| Grener, N100:2023 | 40 | 63 375 | **33 889** | 11 679 | **5 017** | −85,2 % | | Grener, kravbase A | 40 | 63 375 | **33 889** | 11 679 | **5 017** | −85,2 % |
| Grener, N200:2024 | 99 | 134 667 | **71 726** | 28 894 | **12 396** | −82,7 % | | Grener, kravbase B | 99 | 134 667 | **71 726** | 28 894 | **12 396** | −82,7 % |
| Grener, N500:2024 | 32 | 36 569 | **19 329** | 9 408 | **4 037** | −79,1 % | | Grener, kravbase C | 32 | 36 569 | **19 329** | 9 408 | **4 037** | −79,1 % |
| **Alle grener samlet** | **171** | 234 611 | **124 942** | 49 981 | **21 448** | **−82,8 %** | | **Alle grener samlet** | **171** | 234 611 | **124 942** | 49 981 | **21 448** | **−82,8 %** |
| *kontroll:* commons' tre eksempelbaser | 3 | 11 050 | 3 472 | 989 | 321 | −90,8 % | | *kontroll:* commons' tre eksempelbaser | 3 | 11 050 | 3 472 | 989 | 321 | −90,8 % |
Per base: **731 → 125 tokens** i grenformen, **37 372 → 121** i den flate. Per base: **731 → 125 tokens** i grenformen, **37 372 → 121** i den flate.
**Nevner-avvik mot ordren, uttalt:** ordren oppgir 34 grener for N100:2023. Målt på disk **Nevner-avvik mot ordren, uttalt:** ordren oppgir 34 grener for kravbase A. Målt på disk
(`ls ~/repos/vegnormal-okf/build | grep -c '^B-n100-2023-gren-.*-importert$'`) er tallet **40**. (grenkatalogene i korpusbyggets `build/`, talt med `grep -c`) er tallet **40**.
N200:2024 = 99 og N500:2024 = 32 stemmer. Tallene over bruker den målte nevneren, ikke ordrens. Kravbase B = 99 og kravbase C = 32 stemmer. Tallene over bruker den målte nevneren, ikke ordrens.
**Ordrens hypotese bekreftet:** grenformen lukket bundle-siden og gjorde katalogsiden **verre** — **Ordrens hypotese bekreftet:** grenformen lukket bundle-siden og gjorde katalogsiden **verre** —
124 942 tokens over 171 grener mot 112 116 over tre flate baser. Etter endringen er hele 124 942 tokens over 171 grener mot 112 116 over tre flate baser. Etter endringen er hele
@ -56,7 +56,7 @@ Hver oppføring er nå bundet ved konstruksjon: `id`, en **ordrett prefiks** av
**Et premiss ble felt FØR noe ble bygget på det.** «Indeksbodyen forteller en manager hva basen **Et premiss ble felt FØR noe ble bygget på det.** «Indeksbodyen forteller en manager hva basen
handler om» er **usant** for maskin-importerte baser: grenbasenes `index.md` har verken frontmatter handler om» er **usant** for maskin-importerte baser: grenbasenes `index.md` har verken frontmatter
eller prosa — den er en ren lenkeliste (målt: `B-n200-2024-gren-1-1-importert/index.md`, 959 bytes, eller prosa — den er en ren lenkeliste (målt: én grenbases `index.md` i kravbase B, 959 bytes,
første tegn er `-`). Feltet var altså ikke bare dyrt, det var dyrt **og** innholdsløst der. En første tegn er `-`). Feltet var altså ikke bare dyrt, det var dyrt **og** innholdsløst der. En
avkortet prefiks taper ingenting en manager brukte. avkortet prefiks taper ingenting en manager brukte.
@ -117,4 +117,4 @@ M9 ble kjørt fordi `documents` var et felt uten gate — et felt ingen test kan
- **Ingen levende modell har kalt det nye verktøyet.** Formen er bevist offline; at en manager - **Ingen levende modell har kalt det nye verktøyet.** Formen er bevist offline; at en manager
faktisk velger bedre med et utdrag enn med hele indeksen er ikke målt (samme klasse som faktisk velger bedre med et utdrag enn med hele indeksen er ikke målt (samme klasse som
structured-output-grensen). structured-output-grensen).
- **N101 er ikke berørt** — utenfor bestillingen (operatørpresisering 26.08). - **En fjerde base i samme korpus er ikke berørt** — utenfor bestillingen (operatørpresisering 26.08).

View file

@ -7,7 +7,7 @@ står i § 8, og ingen tall er sitert fra `STATE.md`. **Mandat: MÅL, IKKE BYGG.
den ucommittede F15-diffen `tests/test_maf_version_guard.py` 28+/15−, som ordren ba meg se og ikke den ucommittede F15-diffen `tests/test_maf_version_guard.py` 28+/15−, som ordren ba meg se og ikke
røre, pluss to fremmede utrackede stier). røre, pluss to fremmede utrackede stier).
**Baseline:** [syretesten 25.08](2026-08-25-syretest-vei-ab.md) · **Baseline:** syretesten 25.08 (funnene står i [invariant-hovedboken](invarianter.md)) ·
[katalogkostnaden 26.08](2026-08-26-katalogkostnaden.md) · [katalogkostnaden 26.08](2026-08-26-katalogkostnaden.md) ·
[misjonsreview 25.08](2026-08-25-fable-misjonsreview.md). Golden `demo-transcript.stdout` [misjonsreview 25.08](2026-08-25-fable-misjonsreview.md). Golden `demo-transcript.stdout`
**byte-uendret** (`ea8c534773acdbe41ae68f2c55724d69aaf8be4f`, målt `shasum` ved øktstart). **byte-uendret** (`ea8c534773acdbe41ae68f2c55724d69aaf8be4f`, målt `shasum` ved øktstart).
@ -32,7 +32,7 @@ siteres kun frontmatter-NØKLER.
| M1 | Åpner navigatøren en base etter `444fea7`? | **Fra CLI-døra: nei, og det KAN den ikke** (konstant tekst per rolle kan ikke emittere et verktøykall). **Fra biblioteket: ja, alle fire verktøy, på alle fire baser.** | CLI: 0 verktøykall, 0 approaches, 1 runde × 4 baser. Bibliotek: `list_bundles`/`read_bundle`/`read_file`/`quick_validate` 1/1/1/1 × 4 baser, 1 rutet approach hver | | M1 | Åpner navigatøren en base etter `444fea7`? | **Fra CLI-døra: nei, og det KAN den ikke** (konstant tekst per rolle kan ikke emittere et verktøykall). **Fra biblioteket: ja, alle fire verktøy, på alle fire baser.** | CLI: 0 verktøykall, 0 approaches, 1 runde × 4 baser. Bibliotek: `list_bundles`/`read_bundle`/`read_file`/`quick_validate` 1/1/1/1 × 4 baser, 1 rutet approach hver |
| M2 | Blir FORSLAGET bedre av `--plan-review revise`, eller bare planen? | **Bare planen — og planen operatøren viser er ULESELIG:** `<agent_framework._types.Message object at 0x…>` på terminalen, i `{run_id}-exploration.json` og i den parkerte `{run_id}-plan-review.json` | Feedback nådde 5 manager-prompts, 0 hypotesiser-prompts; mandat identisk i begge armer; 3 av 4 CLI-artefakter byte-identiske | | M2 | Blir FORSLAGET bedre av `--plan-review revise`, eller bare planen? | **Bare planen — og planen operatøren viser er ULESELIG:** `<agent_framework._types.Message object at 0x…>` på terminalen, i `{run_id}-exploration.json` og i den parkerte `{run_id}-plan-review.json` | Feedback nådde 5 manager-prompts, 0 hypotesiser-prompts; mandat identisk i begge armer; 3 av 4 CLI-artefakter byte-identiske |
| M3 | Hvor lander feedback på et LEVERT forslag? | **I NESTE kjøring** (innboks → `VerdictStore` → hypotese-prompt), bevist med kontroll. **Ingen vei til «forbedret forslag i samme kjøring» for et menneske** — kun validatorens egen avvisning sløyfes tilbake | Run B: ekspertteksten i proposerens prompt; kontroll uten innboks: 0. In-run: 1 refinement, 4 proposer-kall | | M3 | Hvor lander feedback på et LEVERT forslag? | **I NESTE kjøring** (innboks → `VerdictStore` → hypotese-prompt), bevist med kontroll. **Ingen vei til «forbedret forslag i samme kjøring» for et menneske** — kun validatorens egen avvisning sløyfes tilbake | Run B: ekspertteksten i proposerens prompt; kontroll uten innboks: 0. In-run: 1 refinement, 4 proposer-kall |
| M4 | Tokenprofil per fase | **Bundle-konteksten re-sendes 3× i debatten og 7× i utforskningen = 73–93 % av alle prompt-tokens.** Ledgeren offline er 8 tok/kall (skriptet) — ekte usage finnes kun fra 14.08 (15 306) | Tunnel: debatt 40 605 tok (93 % kontekst), utforskning 103 649 tok (85 %); tre største poster navngitt i § 4 | | M4 | Tokenprofil per fase | **Bundle-konteksten re-sendes 3× i debatten og 7× i utforskningen = 73–93 % av alle prompt-tokens.** Ledgeren offline er 8 tok/kall (skriptet) — ekte usage finnes kun fra 14.08 (15 306) | Energi-B: debatt 40 605 tok (93 % kontekst), utforskning 103 649 tok (85 %); tre største poster navngitt i § 4 |
| M5 | Klar for K2? | **Nei — og det største hullet er ikke `adjudication`:** ingen kode lager `validator-input.json`/`cost-baseline.json` fra et ingestert korpus, og uten dem nekter pipelinen | Produsentens segmenterte v0.2-golden navigeres (3 filer, 0 hopp), `adjudication` leses som streng, 0 konsumenter i `src/` | | M5 | Klar for K2? | **Nei — og det største hullet er ikke `adjudication`:** ingen kode lager `validator-input.json`/`cost-baseline.json` fra et ingestert korpus, og uten dem nekter pipelinen | Produsentens segmenterte v0.2-golden navigeres (3 filer, 0 hopp), `adjudication` leses som streng, 0 konsumenter i `src/` |
| M6 | Vises MCP-kallet i provenance? Egress-linjen? | **Ja, begge — målt mot en EKTE stdio-subprosess** (første gang på kjørestien; B4-testen bruker en stand-in) | `Contacts: prisregister (lookup_unit_price)` · `external_calls=[{prisregister, lookup_unit_price}]` i resultat OG outbox · serverens svar i neste prompt | | M6 | Vises MCP-kallet i provenance? Egress-linjen? | **Ja, begge — målt mot en EKTE stdio-subprosess** (første gang på kjørestien; B4-testen bruker en stand-in) | `Contacts: prisregister (lookup_unit_price)` · `external_calls=[{prisregister, lookup_unit_price}]` i resultat OG outbox · serverens svar i neste prompt |
@ -44,9 +44,10 @@ grønne fordi de bare krever `plan != ""`.
## 1. M1 — `--explore` etter `444fea7` ## 1. M1 — `--explore` etter `444fea7`
**Oppsett.** Fire baser: `shared/examples/{bygg-energi-mikro, veglys-fv-soer, tunnel-hauglia}` + **Oppsett.** Fire baser: `shared/examples/bygg-energi-mikro`, to energi-eksempelbaser som den gang
`~/repos/vegnormal-okf/build/B-n200-2024-gren-1-1-importert` (grenbasen katalogmålingen 26.08 brukte; lå i commons (her `energi-a` og `energi-b`; i dag erstattet av de fiktive `klientpark-energi` og
10 filer). Kontrakt: `max_rounds=6, max_tokens=100000, max_stall_count=2, max_reset_count=1, `driftssenter-kjoling` — tallene under er målt på de gamle) + én grenbase fra et kravkorpus brukt
under utviklingen (her `kravgren-1-1`, grenbasen katalogmålingen 26.08 brukte; 10 filer). Kontrakt: `max_rounds=6, max_tokens=100000, max_stall_count=2, max_reset_count=1,
max_plan_revisions=0, enable_plan_review=false`. Instrumentet (§ 8, vedlegg A) teller hvert max_plan_revisions=0, enable_plan_review=false`. Instrumentet (§ 8, vedlegg A) teller hvert
verktøykall ved å wrappe `FunctionTool.func` ETTER konstruksjon — verktøylisten agentene får er verktøykall ved å wrappe `FunctionTool.func` ETTER konstruksjon — verktøylisten agentene får er
uendret — og hvert modellkall per rolle. uendret — og hvert modellkall per rolle.
@ -57,9 +58,9 @@ streng per rolle, som er den ENESTE offline-formen CLI-en tilbyr):
| Base | rc | Modellkall (manager/proposer/checker) | Verktøykall | Artefakt: runder / `quick_validations` / `tokens_spent` | Notis | | Base | rc | Modellkall (manager/proposer/checker) | Verktøykall | Artefakt: runder / `quick_validations` / `tokens_spent` | Notis |
|---|---:|---|---:|---|---| |---|---:|---|---:|---|---|
| bygg-energi-mikro | 0 | 8 (4/3/1) | **0** | 1 / 0 / 32 | «concluded after 1 round(s); **0 approach(es)**» | | bygg-energi-mikro | 0 | 8 (4/3/1) | **0** | 1 / 0 / 32 | «concluded after 1 round(s); **0 approach(es)**» |
| veglys-fv-soer | 0 | 8 (4/3/1) | **0** | 1 / 0 / 32 | samme | | energi-a | 0 | 8 (4/3/1) | **0** | 1 / 0 / 32 | samme |
| tunnel-hauglia | 0 | 8 (4/3/1) | **0** | 1 / 0 / 32 | samme | | energi-b | 0 | 8 (4/3/1) | **0** | 1 / 0 / 32 | samme |
| B-n200-2024-gren-1-1 (PROJECT_ID=PROBE) | 1 | 4 (4/0/0) | **0** | 1 / 0 / 32 | utforskningen FULLFØRER, så `run refused: IR projection not found in bundle: 'validator-input.json'`; `m1a-exploration.json` skrevet fra `finally` ✅ | | kravgren-1-1 (PROJECT_ID=PROBE) | 1 | 4 (4/0/0) | **0** | 1 / 0 / 32 | utforskningen FULLFØRER, så `run refused: IR projection not found in bundle: 'validator-input.json'`; `m1a-exploration.json` skrevet fra `finally` ✅ |
To ting er målt her, og de er ulike: (i) `444fea7` lukket krasjen — rc er 0/1 med ren nekt, ingen To ting er målt her, og de er ulike: (i) `444fea7` lukket krasjen — rc er 0/1 med ren nekt, ingen
traceback (4/4); (ii) sløyfa er **vakuøs ved konstruksjon** på denne døra: en `ScriptedChatClient` traceback (4/4); (ii) sløyfa er **vakuøs ved konstruksjon** på denne døra: en `ScriptedChatClient`
@ -80,9 +81,9 @@ tre ledgere navigator→hypothesiser→satisfied):
| Base | Verktøykall (`list_bundles`/`read_bundle`/`read_file`/`quick_validate`) | Modellkall (mgr/nav/hyp) | Runder | Approaches (label, `bundle_id`) | `quick_validate`-dom | Navigatørens prompt-tokens per kall | | Base | Verktøykall (`list_bundles`/`read_bundle`/`read_file`/`quick_validate`) | Modellkall (mgr/nav/hyp) | Runder | Approaches (label, `bundle_id`) | `quick_validate`-dom | Navigatørens prompt-tokens per kall |
|---|---|---|---:|---|---|---| |---|---|---|---:|---|---|---|
| bygg | **1/1/1/1** | 12 (6/4/2) | 3 | 1 (`probe-direction-B`, `bygg-energi-mikro`) | validated, **anchored=false**, P10/P50/P90 68 543 / 95 444 / 121 057 | 3 → 144 → 4 031 → 4 783 | | bygg | **1/1/1/1** | 12 (6/4/2) | 3 | 1 (`probe-direction-B`, `bygg-energi-mikro`) | validated, **anchored=false**, P10/P50/P90 68 543 / 95 444 / 121 057 | 3 → 144 → 4 031 → 4 783 |
| veglys | **1/1/1/1** | 12 (6/4/2) | 3 | 1 (…, `veglys-fv-soer`) | validated, anchored=true | 3 → 122 → 10 553 → 11 869 | | energi-a | **1/1/1/1** | 12 (6/4/2) | 3 | 1 (…, `energi-a`) | validated, anchored=true | 3 → 122 → 10 553 → 11 869 |
| tunnel | **1/1/1/1** | 12 (6/4/2) | 3 | 1 (…, `tunnel-hauglia`) | validated, anchored=true | 3 → 148 → 12 767 → 14 429 | | energi-b | **1/1/1/1** | 12 (6/4/2) | 3 | 1 (…, `energi-b`) | validated, anchored=true | 3 → 148 → 12 767 → 14 429 |
| B-n200-gren-1-1 | **1/1/1/1** | 12 (6/4/2) | 3 | 1 (…, `B-n200-2024-gren-1-1-importert`) | validated, anchored=false, **P10 = P50 = P90 = 300** | 3 → 137 → 2 476 → 3 050 | | kravgren-1-1 | **1/1/1/1** | 12 (6/4/2) | 3 | 1 (…, `kravgren-1-1`) | validated, anchored=false, **P10 = P50 = P90 = 300** | 3 → 137 → 2 476 → 3 050 |
Katalogresultatet (`index_excerpt`) står i navigatørens NESTE prompt i 4/4 baser (målt på Katalogresultatet (`index_excerpt`) står i navigatørens NESTE prompt i 4/4 baser (målt på
innholdet, ikke på `.text`). Sømmen bærer altså et verktøykall ende til ende — også mot en innholdet, ikke på `.text`). Sømmen bærer altså et verktøykall ende til ende — også mot en
@ -190,23 +191,23 @@ eksempelbundlene; `bundle_context` re-målt i dag til nøyaktig de tallene).
| Base | Sum prompt-tok | debatt(proposer) ×2 | debatt(checker) ×1 | utforskning(manager) ×4 | generering ×1 | Kontekst re-sendt | | Base | Sum prompt-tok | debatt(proposer) ×2 | debatt(checker) ×1 | utforskning(manager) ×4 | generering ×1 | Kontekst re-sendt |
|---|---:|---:|---:|---:|---:|---| |---|---:|---:|---:|---:|---:|---|
| bygg (ctx 3 861) | 14 283 | 7 868 (55 %) | 3 986 (28 %) | 2 243 (16 %) | 186 (1 %) | 3× = **81 %** | | bygg (ctx 3 861) | 14 283 | 7 868 (55 %) | 3 986 (28 %) | 2 243 (16 %) | 186 (1 %) | 3× = **81 %** |
| veglys (ctx 10 406) | 34 001 | 20 984 (62 %) | 10 556 (31 %) | 2 243 (7 %) | 218 (1 %) | 3× = **92 %** | | energi-a (ctx 10 406) | 34 001 | 20 984 (62 %) | 10 556 (31 %) | 2 243 (7 %) | 218 (1 %) | 3× = **92 %** |
| tunnel (ctx 12 595) | 40 605 | 25 376 (62 %) | 12 759 (31 %) | 2 243 (6 %) | 227 (1 %) | 3× = **93 %** | | energi-b (ctx 12 595) | 40 605 | 25 376 (62 %) | 12 759 (31 %) | 2 243 (6 %) | 227 (1 %) | 3× = **93 %** |
**Utforskning med verktøykall (bibliotek-armen, M1 B):** **Utforskning med verktøykall (bibliotek-armen, M1 B):**
| Base | Sum prompt-tok | manager-ledger ×3 | hypothesiser ×2 | navigator ×4 | manager-final ×1 | plan+facts ×2 | Kontekst re-sendt | | Base | Sum prompt-tok | manager-ledger ×3 | hypothesiser ×2 | navigator ×4 | manager-final ×1 | plan+facts ×2 | Kontekst re-sendt |
|---|---:|---:|---:|---:|---:|---:|---| |---|---:|---:|---:|---:|---:|---:|---|
| bygg | 36 919 | 12 064 (33 %) | 9 786 (27 %) | 8 961 (24 %) | 5 292 (14 %) | 816 (2 %) | 7× = **73 %** | | bygg | 36 919 | 12 064 (33 %) | 9 786 (27 %) | 8 961 (24 %) | 5 292 (14 %) | 816 (2 %) | 7× = **73 %** |
| veglys | 86 013 | 26 262 (31 %) | 23 984 (28 %) | 22 547 (26 %) | 12 404 (14 %) | 816 (1 %) | 7× = **85 %** | | energi-a | 86 013 | 26 262 (31 %) | 23 984 (28 %) | 22 547 (26 %) | 12 404 (14 %) | 816 (1 %) | 7× = **85 %** |
| tunnel | 103 649 | 31 394 (30 %) | 29 116 (28 %) | 27 347 (26 %) | 14 976 (14 %) | 816 (1 %) | 7× = **85 %** | | energi-b | 103 649 | 31 394 (30 %) | 29 116 (28 %) | 27 347 (26 %) | 14 976 (14 %) | 816 (1 %) | 7× = **85 %** |
| B-n200-gren (ctx 2 307) | 24 761 | 8 532 (34 %) | 6 254 (25 %) | 5 666 (23 %) | 3 493 (14 %) | 816 (3 %) | 7× = **65 %** | | kravgren-1-1 (ctx 2 307) | 24 761 | 8 532 (34 %) | 6 254 (25 %) | 5 666 (23 %) | 3 493 (14 %) | 816 (3 %) | 7× = **65 %** |
«Kontekst re-sendt» = antall prompts som inneholder en 160-tegns skive fra midten av «Kontekst re-sendt» = antall prompts som inneholder en 160-tegns skive fra midten av
`okf.bundle_context(base)` × kontekstens tokens, delt på summen. Én `read_bundle` gjør hele `okf.bundle_context(base)` × kontekstens tokens, delt på summen. Én `read_bundle` gjør hele
konteksten til et `function_result`, og det resultatet rir med i **hver** senere prompt: navigatørens konteksten til et `function_result`, og det resultatet rir med i **hver** senere prompt: navigatørens
egne (2), begge hypotesiserens (2, fordi de deler samtalehistorikk), og alle managerens egne (2), begge hypotesiserens (2, fordi de deler samtalehistorikk), og alle managerens
ledger-/final-prompts (3). Hele utforskningen koster **8,2 × kontekststørrelsen** (tunnel: ledger-/final-prompts (3). Hele utforskningen koster **8,2 × kontekststørrelsen** (energi-b:
103 649 / 12 595), debatten **3,2 ×**. 103 649 / 12 595), debatten **3,2 ×**.
**De tre største postene, navngitt:** **De tre største postene, navngitt:**
@ -214,7 +215,7 @@ ledger-/final-prompts (3). Hele utforskningen koster **8,2 × kontekststørrelse
prompt-tokens (proposer ×2 + checker ×1). Kilde: `workflow.py` GroupChat, konteksten i prompt-tokens (proposer ×2 + checker ×1). Kilde: `workflow.py` GroupChat, konteksten i
deltakernes melding, ikke i `instructions` (målt `instructions_chars` 61/307). deltakernes melding, ikke i `instructions` (målt `instructions_chars` 61/307).
2. **Managerens progress-ledger-prompts bærer hele samtalen inkl. verktøyresultater** — 3 × 5 766 2. **Managerens progress-ledger-prompts bærer hele samtalen inkl. verktøyresultater** — 3 × 5 766
(bygg) / 3 × 15 450 (tunnel) tokens, 30–34 % av utforskningen. MAF-Magentic-atferd (bygg) / 3 × 15 450 (energi-b) tokens, 30–34 % av utforskningen. MAF-Magentic-atferd
(`_magentic.py`), ikke vår kode. (`_magentic.py`), ikke vår kode.
3. **Hypotesiseren arver navigatørens verktøyresultater** — 2 × 4 795 / 2 × 14 441 tokens, 25–28 %. 3. **Hypotesiseren arver navigatørens verktøyresultater** — 2 × 4 795 / 2 × 14 441 tokens, 25–28 %.
@ -222,20 +223,20 @@ ledger-/final-prompts (3). Hele utforskningen koster **8,2 × kontekststørrelse
| Par | Felles prefiks (tok) | Av | Lesning | | Par | Felles prefiks (tok) | Av | Lesning |
|---|---:|---|---| |---|---:|---|---|
| debatt proposer #1 vs #2 (bygg / tunnel) | **3 877 / 12 612** | 3 877 / 12 612 | hele første tur er et re-sendt prefiks (100 %) | | debatt proposer #1 vs #2 (bygg / energi-b) | **3 877 / 12 612** | 3 877 / 12 612 | hele første tur er et re-sendt prefiks (100 %) |
| debatt proposer #1 vs checker | 3 877 / 12 612 | 3 986 / 12 759 | 97–99 % delt | | debatt proposer #1 vs checker | 3 877 / 12 612 | 3 986 / 12 759 | 97–99 % delt |
| manager ledger #1 vs #2 | 215 | 751 / 5 547 (bygg) | før første verktøyresultat deles nesten ingenting | | manager ledger #1 vs #2 | 215 | 751 / 5 547 (bygg) | før første verktøyresultat deles nesten ingenting |
| manager ledger #2 vs #3 (bygg / tunnel) | **5 010 / 14 656** | 5 766 / 15 450 | 87–95 % delt | | manager ledger #2 vs #3 (bygg / energi-b) | **5 010 / 14 656** | 5 766 / 15 450 | 87–95 % delt |
| hypothesiser #1 vs #2 | 4 795 / 14 441 | 9 786 / 29 116 | 49–50 % | | hypothesiser #1 vs #2 | 4 795 / 14 441 | 9 786 / 29 116 | 49–50 % |
Anslag, per base tunnel: **leverandør-prefiks-caching** (uendret kode, bare byte-stabile prefikser, Anslag, for energi-b: **leverandør-prefiks-caching** (uendret kode, bare byte-stabile prefikser,
som allerede er tilfellet) kunne treffe ≈ 2 av 3 kontekstkopier i debatten (≈ 25 200 av 40 605 = som allerede er tilfellet) kunne treffe ≈ 2 av 3 kontekstkopier i debatten (≈ 25 200 av 40 605 =
62 %) og ≈ 2 av 3 ledger-kopier i utforskningen (≈ 29 300 av 103 649 = 28 %); hypotesiserens 62 %) og ≈ 2 av 3 ledger-kopier i utforskningen (≈ 29 300 av 103 649 = 28 %); hypotesiserens
andre kall ≈ 14 400 (14 %). Samlet ≈ **40 % av utforskningen og ≈ 60 % av debatten** er andre kall ≈ 14 400 (14 %). Samlet ≈ **40 % av utforskningen og ≈ 60 % av debatten** er
prefiks-cachebart i dag. **Kontekstbudsjett** virker på multiplikatorens grunntall: et tak på prefiks-cachebart i dag. **Kontekstbudsjett** virker på multiplikatorens grunntall: et tak på
`read_bundle` av samme form som katalogtaket (`_CATALOGUE_EXCERPT_CHARS`) — f.eks. konseptliste + `read_bundle` av samme form som katalogtaket (`_CATALOGUE_EXCERPT_CHARS`) — f.eks. konseptliste +
`read_file` per konsept i stedet for hele `bundle_context` — kutter alle 7 kopiene samtidig; ved `read_file` per konsept i stedet for hele `bundle_context` — kutter alle 7 kopiene samtidig; ved
tunnel er hver kopi 12 595 tokens. **Ikke målt her:** hva en levende manager faktisk kaller og hvor energi-b er hver kopi 12 595 tokens. **Ikke målt her:** hva en levende manager faktisk kaller og hvor
mange runder den bruker; tallene over er én navigatør-tur + én hypotesiser-tur. mange runder den bruker; tallene over er én navigatør-tur + én hypotesiser-tur.
## 5. M5 — klar for K2? ## 5. M5 — klar for K2?
@ -373,7 +374,7 @@ prefiks-cachebart som koden står, resten krever et kontekstbudsjett på `read_b
> med i; (2) bygg `read_bundle` om til samme form som katalogen: konseptliste (`name`, `type`, > med i; (2) bygg `read_bundle` om til samme form som katalogen: konseptliste (`name`, `type`,
> `title`, lengde) med `read_file` som neste trinn, tak i TESTEN ikke i koden > `title`, lengde) med `read_file` som neste trinn, tak i TESTEN ikke i koden
> (`test_catalogue_cost_loadbearing.py`-formen); (3) gate: `tests/test_read_bundle_cost_loadbearing.py` > (`test_catalogue_cost_loadbearing.py`-formen); (3) gate: `tests/test_read_bundle_cost_loadbearing.py`
> — `read_bundle` over tunnel-basen < 1 500 o200k-tokens, kontroll at én konseptfil alene sprenger > — `read_bundle` over energi-b-basen < 1 500 o200k-tokens, kontroll at én konseptfil alene sprenger
> taket, og at `read_file` fortsatt gir hele fila. **Ikke utløs:** `okf.bundle_context` selv (den > taket, og at `read_file` fortsatt gir hele fila. **Ikke utløs:** `okf.bundle_context` selv (den
> bærer debattens kontekst og goldenene), nav-goldenene, `run.py`. Debattens 3× er GroupChat-formen > bærer debattens kontekst og goldenene), nav-goldenene, `run.py`. Debattens 3× er GroupChat-formen
> og en egen beslutning (kontekst i `instructions` vs melding endrer ikke tokens, bare cache-treff). > og en egen beslutning (kontekst i `instructions` vs melding endrer ikke tokens, bare cache-treff).

View file

@ -13,7 +13,9 @@ ikke en ny produksjonsflate. Tokens telles med `tiktoken`s `o200k_base`, kjørt
`uv run --with tiktoken` (`tiktoken` er fortsatt IKKE en prosjekt-avhengighet). `uv run --with tiktoken` (`tiktoken` er fortsatt IKKE en prosjekt-avhengighet).
**Kjent-positiv kontroll FØR noe ble målt** (Verifiseringsloven ansikt 4 — en spørring skal bevises **Kjent-positiv kontroll FØR noe ble målt** (Verifiseringsloven ansikt 4 — en spørring skal bevises
å KUNNE finne): instrumentet ble kjørt mot de tre eksempelbundlene og reproduserte commons' egne å KUNNE finne): instrumentet ble kjørt mot de tre eksempelbundlene som den gang lå i commons (`bygg-energi-mikro`
og to energi-eksempler, her `energi-a` og `energi-b`; de to siste er i dag erstattet av de fiktive
`klientpark-energi` og `driftssenter-kjoling`, og tallene her er målt på de gamle) og reproduserte commons' egne
publiserte fasittall for `okf.bundle_context` **eksakt** — 3 861 / 10 406 / 12 595. Et instrument publiserte fasittall for `okf.bundle_context` **eksakt** — 3 861 / 10 406 / 12 595. Et instrument
som ikke treffer en kjent fasit måler seg selv. som ikke treffer en kjent fasit måler seg selv.
@ -41,10 +43,10 @@ Per base, én CLI-kjøring med `--explore`:
| Base | `read_bundle`-nyttelast | Prompts totalt | Prompt-tokens totalt | Utforsknings-kopier | Tokens i dem | Debatt-kopier | Tokens i dem | | Base | `read_bundle`-nyttelast | Prompts totalt | Prompt-tokens totalt | Utforsknings-kopier | Tokens i dem | Debatt-kopier | Tokens i dem |
|---|---:|---:|---:|---:|---:|---:|---:| |---|---:|---:|---:|---:|---:|---:|---:|
| `bygg-energi-mikro` | 12 005 tegn / **3 861 tok** | 15 | 35 773 | **5** | 22 013 | 3 | 11 738 | | `bygg-energi-mikro` | 12 005 tegn / **3 861 tok** | 15 | 35 773 | **5** | 22 013 | 3 | 11 738 |
| `veglys-fv-soer` | 32 201 tegn / **10 406 tok** | 19 | 88 881 | **5** | 54 623 | 3 | 31 376 | | `energi-a` | 32 201 tegn / **10 406 tok** | 19 | 88 881 | **5** | 54 623 | 3 | 31 376 |
| `tunnel-hauglia` | 39 583 tegn / **12 595 tok** | 19 | 106 519 | **5** | 65 698 | 3 | 37 943 | | `energi-b` | 39 583 tegn / **12 595 tok** | 19 | 106 519 | **5** | 65 698 | 3 | 37 943 |
**De fem utforsknings-kopiene, navngitt** (tunnel-tall): navigatørens egen prompt etter kallet **De fem utforsknings-kopiene, navngitt** (energi-b-tall): navigatørens egen prompt etter kallet
(12 770) · managerens tre progress-ledger-/final-prompts (13 527 / 13 549 / 13 075) · (12 770) · managerens tre progress-ledger-/final-prompts (13 527 / 13 549 / 13 075) ·
hypotesiserens ene tur (12 777). Ett `read_bundle`-kall gjør hele basen til et `function_result`, hypotesiserens ene tur (12 777). Ett `read_bundle`-kall gjør hele basen til et `function_result`,
og det resultatet rir med i **hver senere prompt i samme samtale** — deltakerne deler og det resultatet rir med i **hver senere prompt i samme samtale** — deltakerne deler
@ -67,11 +69,11 @@ den er antall prompts etter kallet, og den er ≥ 1 uansett.
| Base | Filer navigert | Konseptfiler | `type: verdict` | Rot-`index.md` (body) | | Base | Filer navigert | Konseptfiler | `type: verdict` | Rot-`index.md` (body) |
|---|---:|---:|---:|---:| |---|---:|---:|---:|---:|
| `bygg-energi-mikro` | 6 | 4 | 1 | 1 884 tegn | | `bygg-energi-mikro` | 6 | 4 | 1 | 1 884 tegn |
| `veglys-fv-soer` | 7 | 5 | 1 | 3 646 tegn | | `energi-a` | 7 | 5 | 1 | 3 646 tegn |
| `tunnel-hauglia` | 7 | 5 | 1 | 4 763 tegn | | `energi-b` | 7 | 5 | 1 | 4 763 tegn |
**Ett premiss felt før noe ble bygget på det:** «basens indeks er selve navigasjonsprosaen, så den **Ett premiss felt før noe ble bygget på det:** «basens indeks er selve navigasjonsprosaen, så den
hører hjemme i `read_bundle`». Tunnelbasens rot-indeks er alene **4 763 tegn ≈ 1 400 o200k-tokens** hører hjemme i `read_bundle`». Energi-b-basens rot-indeks er alene **4 763 tegn ≈ 1 400 o200k-tokens**
— altså nesten hele ordrens tak på 1 500 for HELE kallet. Å legge den inn ville brukt opp budsjettet — altså nesten hele ordrens tak på 1 500 for HELE kallet. Å legge den inn ville brukt opp budsjettet
på et felt katalogen alt gir et bundet utdrag av (`_CATALOGUE_EXCERPT_CHARS`), og som fortsatt er på et felt katalogen alt gir et bundet utdrag av (`_CATALOGUE_EXCERPT_CHARS`), og som fortsatt er
nøyaktig ett `read_file(id, "index.md")` unna. `read_bundle` bærer derfor konseptlista og ikke nøyaktig ett `read_file(id, "index.md")` unna. `read_bundle` bærer derfor konseptlista og ikke
@ -94,7 +96,7 @@ denne ordren gjør bare det siste.
| 2 | Kjøringen ÅPNET basen (ikke bare listet den) | `measure-exploration.json`s `tool_calls` → `list_bundles`, `read_bundle(<base>)` | | 2 | Kjøringen ÅPNET basen (ikke bare listet den) | `measure-exploration.json`s `tool_calls` → `list_bundles`, `read_bundle(<base>)` |
| 3 | Prompt-størrelse inkluderer verktøyresultatet | wrapper på `_inner_get_response` serialiserer `contents` (`function_call`/`function_result`), ikke `.text` | | 3 | Prompt-størrelse inkluderer verktøyresultatet | wrapper på `_inner_get_response` serialiserer `contents` (`function_call`/`function_result`), ikke `.text` |
| 4 | Nevner | 15 / 19 / 19 prompts per kjøring; 8 bærer konteksten i alle tre | | 4 | Nevner | 15 / 19 / 19 prompts per kjøring; 8 bærer konteksten i alle tre |
| 5 | Indeksbodyen alene sprenger nesten hele taket | `len(navigate_bundle(tunnel).index_summary)` → 4 763 tegn ≈ 1 400 tok | | 5 | Indeksbodyen alene sprenger nesten hele taket | `len(navigate_bundle(energi_b).index_summary)` → 4 763 tegn ≈ 1 400 tok |
--- ---
@ -106,10 +108,10 @@ denne ordren gjør bare det siste.
| Base | Nyttelast før → etter | Kopier i utforsknings-prompts | Nyttelast × kopier | Utforsknings-prompts totalt | Hele kjøringen | | Base | Nyttelast før → etter | Kopier i utforsknings-prompts | Nyttelast × kopier | Utforsknings-prompts totalt | Hele kjøringen |
|---|---|---:|---|---:|---| |---|---|---:|---|---:|---|
| `bygg-energi-mikro` | 3 861 → **163 tok** | 5 → 5 | 19 305 → **815** | 23 726 → **5 434** (−77 %) | 35 773 → 17 481 (−51 %) | | `bygg-energi-mikro` | 3 861 → **163 tok** | 5 → 5 | 19 305 → **815** | 23 726 → **5 434** (−77 %) | 35 773 → 17 481 (−51 %) |
| `veglys-fv-soer` | 10 406 → **237 tok** | 5 → 5 | 52 030 → **1 185** | 56 314 → **5 742** (−90 %) | 88 881 → 38 309 (−57 %) | | `energi-a` | 10 406 → **237 tok** | 5 → 5 | 52 030 → **1 185** | 56 314 → **5 742** (−90 %) | 88 881 → 38 309 (−57 %) |
| `tunnel-hauglia` | 12 595 → **259 tok** | 5 → 5 | 62 975 → **1 295** | 67 415 → **5 973** (−91 %) | 106 519 → 45 077 (−58 %) | | `energi-b` | 12 595 → **259 tok** | 5 → 5 | 62 975 → **1 295** | 67 415 → **5 973** (−91 %) | 106 519 → 45 077 (−58 %) |
**Ordrens eget kriterium, verifisert direkte:** `read_bundle` over tunnelbasen er **748 tegn / 259 **Ordrens eget kriterium, verifisert direkte:** `read_bundle` over energi-b-basen er **748 tegn / 259
o200k-tokens** — under taket på 1 500. Målt forhold 2,89 tegn/token for denne norske markdownen; o200k-tokens** — under taket på 1 500. Målt forhold 2,89 tegn/token for denne norske markdownen;
det er dét som lar gaten bounde TEGN uten å gjette (se testens docstring, som uttaler avviket). det er dét som lar gaten bounde TEGN uten å gjette (se testens docstring, som uttaler avviket).
@ -126,7 +128,7 @@ grønn (**1 195 passed / 5 skipped**, mot 1 189/5 før — supersett, 0 fjernet)
og golden `demo-transcript.stdout` UENDRET (`ea8c534773acdbe41ae68f2c55724d69aaf8be4f`). og golden `demo-transcript.stdout` UENDRET (`ea8c534773acdbe41ae68f2c55724d69aaf8be4f`).
**Et fravær som var et instrumentfeil, ikke et faktum (Verifiseringsloven ansikt 4).** Den første **Et fravær som var et instrumentfeil, ikke et faktum (Verifiseringsloven ansikt 4).** Den første
ETTER-kjøringen rapporterte **0 kopier** på veglys og tunnel. Det var ikke sant: sonden var en ETTER-kjøringen rapporterte **0 kopier** på energi-a og energi-b. Det var ikke sant: sonden var en
160-tegns skive fra MIDTEN av nyttelasten, og den nye nyttelasten er kort nok til at midten treffer 160-tegns skive fra MIDTEN av nyttelasten, og den nye nyttelasten er kort nok til at midten treffer
norske tegn, som prompten serialiserer escaped (`å`) mens sonden holdt dem rå. Sonden ble byttet norske tegn, som prompten serialiserer escaped (`å`) mens sonden holdt dem rå. Sonden ble byttet
til et konseptfilnavn (ASCII, ordrett i begge), og svaret ble 5 — samme tall som før endringen. til et konseptfilnavn (ASCII, ordrett i begge), og svaret ble 5 — samme tall som før endringen.
@ -137,7 +139,7 @@ instrument man selv har erklært ødelagt, er nøyaktig dét dette avsnittet adv
## 6. Ærlighetsgrenser, uttalt ## 6. Ærlighetsgrenser, uttalt
1. **Dette er ikke «−59 % kostnad».** Det målte utsagnet er at `read_bundle`s EGET bidrag faller fra 1. **Dette er ikke «−59 % kostnad».** Det målte utsagnet er at `read_bundle`s EGET bidrag faller fra
5 × 12 595 til 5 × 259 tokens på tunnelbasen. En navigatør som deretter åpner *k* dokumenter 5 × 12 595 til 5 × 259 tokens på energi-b-basen. En navigatør som deretter åpner *k* dokumenter
betaler *k* `read_file`-resultater, og en som åpner ALT betaler omtrent de samme bytene — bare betaler *k* `read_file`-resultater, og en som åpner ALT betaler omtrent de samme bytene — bare
per kall. Gevinsten er at den betaler for det den valgte, og at hvert resultat rir fra SITT eget per kall. Gevinsten er at den betaler for det den valgte, og at hvert resultat rir fra SITT eget
kall og framover i stedet for at alt rir fra det første. kall og framover i stedet for at alt rir fra det første.

View file

@ -32,7 +32,7 @@ Diagnosen stemmer derimot ikke. **Forslaget har aldri blitt lest fra den fila.**
tatt. tatt.
At `validator-input.json` er en komplett håndskrevet `SavingsProposal` (den er det — se At `validator-input.json` er en komplett håndskrevet `SavingsProposal` (den er det — se
`shared/examples/tunnel-hauglia/validator-input.json`) gjør den til en **fasit ved siden av** `src/portfolio_optimiser/data/bundles/driftssenter-kjoling/validator-input.json`) gjør den til en **fasit ved siden av**
kjørestien, ikke til kjørestiens inngang. Det som faktisk blokkerer, er at fila er en **påkrevd kjørestien, ikke til kjørestiens inngang. Det som faktisk blokkerer, er at fila er en **påkrevd
inngangsbetingelse for prosjekt-identiteten**. inngangsbetingelse for prosjekt-identiteten**.

View file

@ -80,7 +80,8 @@ setningen holder ikke, og begge er målt:**
1. **K2 kan ikke være en testavhengighet.** Basen ligger utenfor repoet (`~/corpora/`). MAJOR-3-gaten 1. **K2 kan ikke være en testavhengighet.** Basen ligger utenfor repoet (`~/corpora/`). MAJOR-3-gaten
hadde samme begrensning og løste den likt: gaten binder den største basen som faktisk shippes, hadde samme begrensning og løste den likt: gaten binder den største basen som faktisk shippes,
og K2-tallene bor i en rapport. Gaten her binder derfor `shared/examples/tunnel-hauglia` (flat), og K2-tallene bor i en rapport. Gaten her binder derfor den største flate eksempelbasen
(den gang et energi-eksempel i commons, i dag `driftssenter-kjoling`),
`shared/examples/nav-golden-hierarchy/bundle` (nestet) og en syntetisk base på 242 konsepter bak `shared/examples/nav-golden-hierarchy/bundle` (nestet) og en syntetisk base på 242 konsepter bak
20 kataloger — der den FLATE formen er over 5× taket den nivå-formen ligger godt innenfor. 20 kataloger — der den FLATE formen er over 5× taket den nivå-formen ligger godt innenfor.
2. **1 500 tegn er ikke oppnåelig for K2s rotnivå, og skal ikke være det.** Rota bærer **39 2. **1 500 tegn er ikke oppnåelig for K2s rotnivå, og skal ikke være det.** Rota bærer **39

View file

@ -19,10 +19,11 @@ prosjektavhengighet.
| Kjent-positiv sjekk | Publisert fasit | Målt nå | | Kjent-positiv sjekk | Publisert fasit | Målt nå |
|---|---|---| |---|---|---|
| `read_bundle` over `tunnel-hauglia` | 259 tok (`docs/2026-09-02-read-bundle-kontekstkostnad.md`) | **259 tok** | | `read_bundle` over `energi-b` | 259 tok (`docs/2026-09-02-read-bundle-kontekstkostnad.md`) | **259 tok** |
| `bundle_context` over `tunnel-hauglia` | 12 595 tok (samme) | **12 595 tok** | | `bundle_context` over `energi-b` | 12 595 tok (samme) | **12 595 tok** |
Instrumentet reproduserer begge eksakt. Først etter det er K2-tallene under lest som tall. `energi-b` er commons' daværende energi-eksempelbase (i dag erstattet av den fiktive
`driftssenter-kjoling`; tallene er målt på den gamle). Instrumentet reproduserer begge eksakt. Først etter det er K2-tallene under lest som tall.
**To instrumentfeil ble fanget underveis, og begge er skrevet ned fordi de er samme klasse som **To instrumentfeil ble fanget underveis, og begge er skrevet ned fordi de er samme klasse som
misjonsreview v2 rapporterte.** (i) Første prompt-sonde leste `repr(Content)` og fikk misjonsreview v2 rapporterte.** (i) Første prompt-sonde leste `repr(Content)` og fikk
@ -365,7 +366,7 @@ krever en levende manager, ikke en endring i noe av dette.
| # | Påstand | Kommando → resultat | | # | Påstand | Kommando → resultat |
|---|---|---| |---|---|---|
| 1 | Instrumentet reproduserer publisert fasit | `uv run --with tiktoken python scratchpad/s7a/m1_navigation.py` → tunnel `read_bundle` 259, `bundle_context` 12 595 | | 1 | Instrumentet reproduserer publisert fasit | `uv run --with tiktoken python scratchpad/s7a/m1_navigation.py` → energi-b `read_bundle` 259, `bundle_context` 12 595 |
| 2 | 39 konsepter, 0 `adjudication`, kjent-positiv 39 `type` | `grep -l '^adjudication:' *.md \| wc -l` → 0; `grep -l '^type:'` → 39 | | 2 | 39 konsepter, 0 `adjudication`, kjent-positiv 39 `type` | `grep -l '^adjudication:' *.md \| wc -l` → 0; `grep -l '^type:'` → 39 |
| 3 | `N=43` ikke verifiserbart | ingen `log.md` i bundelen (`Path(base)/'log.md').exists()` → `False`) | | 3 | `N=43` ikke verifiserbart | ingen `log.md` i bundelen (`Path(base)/'log.md').exists()` → `False`) |
| 4 | Navigasjonstall | samme skript → § 1-tabellen | | 4 | Navigasjonstall | samme skript → § 1-tabellen |

View file

@ -22,10 +22,11 @@ Samme protokoll som S7a: `tiktoken` `o200k_base` over den fulle prompt-blobben
| Kjent-positiv sjekk | Publisert fasit | Målt nå | | Kjent-positiv sjekk | Publisert fasit | Målt nå |
|---|---|---| |---|---|---|
| `read_bundle` over `tunnel-hauglia` | 259 tok | **259 tok** | | `read_bundle` over `energi-b` | 259 tok | **259 tok** |
| `bundle_context` over `tunnel-hauglia` | 12 595 tok | **12 595 tok** | | `bundle_context` over `energi-b` | 12 595 tok | **12 595 tok** |
Begge reproduseres eksakt. Først etter det er K2-tallene lest som tall. `energi-b` er commons' daværende energi-eksempelbase (i dag erstattet av den fiktive
`driftssenter-kjoling`; tallene er målt på den gamle). Begge reproduseres eksakt. Først etter det er K2-tallene lest som tall.
**S7as to instrumentfeller er arvet som rettelser, ikke gjenoppdaget:** sonden henter **S7as to instrumentfeller er arvet som rettelser, ikke gjenoppdaget:** sonden henter
`text`/`result`/`arguments` ut av hvert `Content` (aldri `repr`), og `Message.text` telles **ikke** `text`/`result`/`arguments` ut av hvert `Content` (aldri `repr`), og `Message.text` telles **ikke**

View file

@ -1,729 +0,0 @@
# N100, N200 og N500 re-målt i hypoteseform — det nye utdraget bærer nøkkelen, og po dropper den
> **Ordre `20260908T134013Z-2103630340-from-.claude` (P2).** P1 (`docs/2026-09-08-n-bundlene-konsum.md`,
> `e7ffe9e`+`a26e8f2`) fant at pre-passets utdrag leverte `text` uten `title`/`req_number`, så
> kravnummeret spørsmålet stiller fantes ikke noe sted i modellens kontekst. llm-ingestion-okf lukket
> det (`17c49fc`, `c95d189` — HEAD `171798e`, treet rent), vegnormal-okf re-bygde de tre bundlene med
> proveniens (`feae0c8`, V2-treet). Denne målingen re-kjører de tre armene mot det nye utdraget, med
> `--require-cost-baseline` (operatørvalg D-3), og dømmer etter den NYE «ferdig»-definisjonen
> (operatørvalg 08.09 12:20, D-1: **po er ikke et oppslagsverktøy**).
>
> **Utdraget bærer nå nøkkelen — 14 medlemmer, var 9. Modellen fikk den likevel ikke: po dropper
> feltene TO ganger, ved parsing og ved rendering. Funnet har byttet eier, fra okf til po.**
---
## 0. Hva som ER målt, og hva som IKKE er det
**MÅLT.** Tre nye tre-hasher reprodusert ordrett fra vegnormal-okfs V2-melding. `okf.navigate_bundle`
når 446 / 1 133 / 270 konsepter, 0 ufulgte lenker. Fasit-konseptet på **rang 1 av 8 på alle tre**, med
den flaggløse kommandoen. Utdragets medlemsliste: **14**, med `title`, `req_number`, `sources`,
`source_element_id`, `source_sha256`. Kontraktsjekk **exit 0 × 3**, 15 regler, 0 funn.
`prepass.admit_payload` OK × 3. Klientproben grønn. `--require-cost-baseline` nektet alle tre, rc 1,
**0 modellkall** — på full kjøresti, ikke bare dry-run. `--derive-cost-baseline` nektet også, alle tre.
Tre betalte armer uten flagget, rc 0 × 3, **NOK 0,22 av taket 5, 0 × 429**. Hva som nådde prompten,
felt for felt. Hvert modellsvar ordrett.
**IKKE MÅLT.** **Én kjøring er én kjøring** — tre armer, ett spørsmål hver, én modell
(`gpt-4-1-mini`), ett deployment, ingen gjentakelse. Ingenting her sier noe om varians, og P1 og P2
er to trekk fra samme modell på nesten samme input: at N100 gikk fra `rejected` (P1) til `validated`
(P2) og N200 motsatt vei er ikke tilskrevet noen årsak. At en ANNEN modell ville lest kuttet
annerledes er ikke målt. Ingen av bundlene er faglig gjennomgått — sammenlikningen er mot
konseptkroppen, ikke mot vegnormalen.
**IKKE RØRT.** Ingen fil under `src/`. Ingen Azure-konfig. Ingen fil i vegnormal-okf eller i noen
okf-checkout. Ingen push.
---
## 1. Kjent-positiv per bundle, FØR noe ble betalt
```bash
OKF=~/repos/llm-ingestion-okf # HEAD 171798e, `git status --short` TOMT
B=~/repos/vegnormal-okf/build/ferdig # feae0c8 (V2)
$OKF/.venv/bin/python3 $OKF/tools/okf_consume.py $B/n100-2023 \
--question "Hva krever Krav 3.3.1—13 i N100? Gjengi det sentrale vilkåret." --out payload-n100.json
$OKF/.venv/bin/python3 $OKF/tools/okf_skill.py $B/n100-2023 --out skill-n100
$OKF/.venv/bin/python3 $OKF/tools/okf_contract_check.py --skill skill-n100/SKILL.md --payload payload-n100.json
# ... tilsvarende for n200-2024 og n500-2024, DEFAULT-kommandoen, ingen flagg
```
| kjent-positiv | N100:2023 | N200:2024 | N500:2024 |
|---|---|---|---|
| `sha256-tree` reproduserer V2s ref | **JA** `da6b8204…` | **JA** `c64f36b7…` | **JA** `673a0c2c…` |
| `okf.navigate_bundle` → konsepter | **446** (fasit 446) | **1 133** (fasit 1 133) | **270** (fasit 270) |
| `Bundle.skipped` (ufulgte lenker) | 0 | 0 | 0 |
| considered / withheld / delivered | 446 / 438 / 8 | 1 133 / 1 125 / 8 | 270 / 262 / 8 |
| budsjett brukt av 120 000 | 11 868 | 23 961 | 11 941 |
| **fasit-konseptets rang** | **1 av 8** | **1 av 8** | **1 av 8** |
| `okf_contract_check` | **exit 0**, 15 regler, 0 funn | **exit 0**, 15 regler, 0 funn | **exit 0**, 15 regler, 0 funn |
| `prepass.admit_payload` mot montert base | **OK** | **OK** | **OK** |
| klientprobe `test_foundry_profile_live` | **1 passed** — kjørt FØR armene | (samme kjøring) | (samme kjøring) |
Budsjettforbruket er høyere enn P1s (11 868 mot 8 939 · 23 961 mot 21 102 · 11 941 mot 9 085) —
forventet, og det er selve endringen: hvert utdrag bærer nå fem felt til. okfs egen melding målte
+450,5 B per utdrag på et annet korpus; her er differansen +2 929 / +2 859 / +2 856 B over åtte
utdrag, altså **+366 / +357 / +357 B per utdrag**.
**Budsjett-instrumentets egen kjent-positiv flyttet, som okf varslet:** `expected` 12 563 /
`raw_bytes` 12 227 / `encoding_delta` 336 (var 10 349 / 10 060 / 289), og `measured == expected` på
alle tre. En stale verdi ville fått pre-passet til å nekte — den høylytte feilen, ikke en defekt.
**Utdragets medlemmer: 14, var 9.** Ordren forventet ≥ 12.
```
adjudication · bundle_id · bundle_id_inherited · concept_id · rank · req_number · sha256
· source_element_id · source_sha256 · sources · text · text_sha256 · title · trust_tier
```
De fem nye er `title`, `req_number`, `sources`, `source_element_id`, `source_sha256`.
(okfs melding målte 9 → 17 på K2; differansen er at K2 bærer fem `source_*`-lokatorer der
N-bundlene bærer to — prefiks-regelen, ikke en allowlist.)
For N100s fasit-konsept:
```yaml
title: Krav 3.3.1—13 H1 – Nasjonal hovedveg, ÅDT < 6 000 og fartsgrense 80 km/t
req_number: Krav 3.3.1—13
sources: [{resource: https://viewers.vegnorm.vegvesen.no/api/nisosts/859984?languageCode=nb,
title: N100:2023}]
source_element_id: id-4b61eee9-a149-42b3-863d-293b8320c15a
source_sha256: c58e8bbc5fa9a5400c111e51b04c05f2cfd9edabd884ef5352a486fdab2cb5ab
```
**Alt P1 etterlyste er der.** Det er derfor resten av dette dokumentet handler om po.
---
## 2. `--require-cost-baseline` NEKTET alle tre — og nekten er gratis
Operatørvalg D-3. Flagget ble kjørt på **full kjøresti**, ikke bare dry-run, og nekten er ordrett:
```
run refused: this run was required to be anchored, but the knowledge base offers no cost baseline:
without one the validator's stage 0 (reconciling each proposed cost line against the project's own)
is skipped and nothing ties a proposed cost line to this project. Ship a cost-baseline.json, or pass
--derive-cost-baseline when the base carries a priced schedule
```
| | N100 | N200 | N500 |
|---|---|---|---|
| `--require-cost-baseline`, full kjøring | **rc 1** | **rc 1** | **rc 1** |
| modellkall før nekten | **0** | **0** | **0** |
| kontroll: samme argv UTEN flagget | **rc 0** | **rc 0** | **rc 0** |
| `cost-baseline.json` i basen | fraværende | fraværende | fraværende |
| `--derive-cost-baseline` (den andre døra nekten navngir) | **rc 1** | **rc 1** | **rc 1** |
Antallet modellkall asserteres, ikke exit-koden alene: **en nekt etter forbruket ser identisk ut ved
exit-koden** (økt 57s regel). rc-0-kontrollen er der fordi en arm som bare kan bli rød ikke beviser
noe.
Den andre dørens nekt er like presis, og den forklarer hvorfor det ikke finnes en tredje vei:
```
no cost table found in bundle '…/n100-2023': no concept file carries a markdown table whose header
names all three of ['code', 'quantity', 'unit_cost']
```
**Målt konsekvens: det finnes ingen forankret form av en N-bundle-kjøring.** En vegnormal er et
kravkorpus, ikke et anbudskorpus — den bærer ingen kostlinjer i det hele tatt, og ingen av po sine
tre baseline-projeksjoner (håndskrevet fil · `baseline_from_project` · `derive_cost_baseline`) kan
lage én av den. F4-flagget gjør altså nøyaktig det det ble bygget for, og svaret er at N-bundlene
ikke er kostnadskjøringer.
**Derfor er de tre betalte armene kjørt UTEN flagget, og det er et uttalt avvik fra ordrens
bokstav.** Ordren ba om tre betalte armer MED flagget; med flagget koster de ingenting og produserer
ingen modellsvar, altså heller ingen (a), (b′), (c) eller (e). Nekten er rapportert som resultatet
D-3 ba om (over), og armene som faktisk måler det nye utdraget er kjørt uten det. Det som ellers ville
stått igjen, var en tom måling av den eneste tingen P2 finnes for.
---
## 3. Tre betalte armer
```bash
export PORTFOLIO_FOUNDRY_PROJECT_ENDPOINT=… # utledet INLINE fra `az`, aldri i fil
export PORTFOLIO_FOUNDRY_DEPLOYMENT=gpt-4-1-mini
export PORTFOLIO_MODEL_MAP=scratchpad/major2-live/model_map.json
export PACE_SECONDS=2
uv run --with tiktoken python scratchpad/nbundler-p2/live_nbundler_p2.py {n100|n200|n500}
# argv: <N100|N200|N500> --profile azure --docs-dir <base> --bundle-dir <base>
# --proposal-review --outbox-dir … --run-id p2-<tag>-free --prepass-payload payload-<tag>.json
```
Innsprutspunktet er `run._default_factory` (Fase 4e). Alle roller LEVENDE.
Deployment urørt (`az … deployment show`): `GlobalStandard`, `capacity` **100**, `gpt-4.1-mini`
`2025-04-14`, `Succeeded`.
| | **N100** | **N200** | **N500** |
|---|---:|---:|---:|
| rc | 0 | 0 | 0 |
| prompter totalt | **6** | **7** | **5** |
| proposer / checker | 5 / 1 | 6 / 1 | 4 / 1 |
| prompt-tokens (o200k) | 7 748 | 16 764 | 6 092 |
| leverandør-input | 12 217 | 25 877 | 9 619 |
| leverandør-output | 824 | 1 248 | 611 |
| **429-svar** | **0** | **0** | **0** |
| parse-feil-artefakt | fraværende | **TIL STEDE (1)** | fraværende |
| verktøykall i debatten | **0** (A-form: verktøyene trukket) | **0** | **0** |
| validator-dom | **validated** | rejected (stage 4, P90) | **validated** |
| dom-nøkkel | `c195117970ce86ef` | `70f3eb3540cd88f7` | `c9740904b1303f65` |
| Steg 5 (reason matet tilbake) | 2 prompter | 2 prompter | 1 prompt |
**Én parse-feil, på N200 — og filens tilstedeværelse ER signalet** (økt 35). Den er ikke en
formatfeil: modellen leverte gyldig JSON i riktig form, og det som falt var en DOMENE-invariant i
`SavingsProposal`:
> `Value error, claimed saving 1500000.0 exceeds affected items' total 900000.0`
Den avledede grammatikken (Fase 1b funn 1b) holdt altså på tre ferske korpora igjen; det som fanget
denne var pydantics egen `claimed <= total`. Funn-1-fangsten (kaller-eid sink, `finally`) leverte
teksten VERBATIM, som er hele grunnen til at setningen over kan siteres i det hele tatt.
---
## 4. Hovedfunnet: nøkkelen finnes nå, og po dropper den to ganger
P1s funn 1 var okfs. **Det er lukket.** Det som står igjen er po sitt, og det er to uavhengige tap
på samme sti.
**Tap 1 — ved PARSING.** `prepass.PrepassExcerpt` arver `_Permissive`, som er
`ConfigDict(extra="ignore")`. Payloadens 14 medlemmer blir til 7 i objektet po bygger:
```python
>>> sorted(gold.model_dump())
['adjudication', 'bundle_id', 'concept_id', 'sha256', 'text', 'text_sha256', 'trust_tier']
```
`title`, `req_number`, `sources`, `source_element_id` og `source_sha256` finnes ikke lenger.
**Tap 2 — ved RENDERING.** `prepass._data_blocks` renderer fire ting per utdrag: `concept_id`,
`adjudication`, `trust_tier` og `text`. Selv om feltene hadde overlevd parsing, ville de ikke nådd
prompten. DATA-blokka for fasit-konseptet, ORDRETT:
```
--- BEGIN DATA krav/N100/id-4b61eee9-a149-42b3-863d-293b8320c15a (adjudication: unknown, trust_tier: unverified) ---
## Krav
Eventuell kryssing mellom gang- og sykkelveg og veg skal være planskilt ved ÅDT > 4 000.
--- END DATA krav/N100/id-4b61eee9-a149-42b3-863d-293b8320c15a ---
```
**En første måling av dette var KONFUNDERT, og ble felt før noe ble bygget på den.** Et naivt
delstreng-søk fant `req_number` i 2 av 6 prompter og `source_element_id` i 2 av 6 — begge falske:
strengen `Krav 3.3.1—13` står i prompten fordi den er en del av SPØRSMÅLET
(«cut computed for: Hva krever Krav 3.3.1—13 i N100?»), og `id-4b61eee9-…` fordi den er en delstreng
av `concept_id`. Ingen av dem kommer fra utdragets nye felt. Det er repoets egen regel om at en
assert aldri skal stå på en delstreng to grener deler, anvendt på en måling i stedet for en test.
| når prompten faktisk? | N100 | N200 | N500 |
|---|---|---|---|
| utdragets `text` | **JA** | **JA** | **JA** |
| `concept_id` (UUID-formet) | **JA** | **JA** | **JA** |
| `req_number` som FELT | **NEI** | **NEI** | **NEI** |
| `title` | **NEI** | **NEI** | **NEI** |
| `sources` / `resource`-URL | **NEI** | **NEI** | **NEI** |
| `source_element_id` som FELT | **NEI** | **NEI** | **NEI** |
| `source_sha256` | **NEI** | **NEI** | **NEI** |
**Konsekvensen er direkte observerbar i modellens egne ord.** N200s proposer skriver:
> «…to optimize the geotechnical investigations and ground assessments already in the regulatory
> planning phase (**as per the krav with ID 03418c46-ad07-4678-bae1-08f441f38903**)»
Modellen siterer en UUID fordi UUID-en er den eneste identifikatoren den kan se — den står i
DATA-avgrenseren. Kravnummeret, som er det et menneske ville sitert, er i payloaden og når aldri
fram. **Rangeringen finner riktig dokument, produsenten leverer nå nøkkelen, og konsumenten kaster
den.**
---
## 5. Målene (a)–(e) + (b′), med nevner
### (a) Svarte modellen med det sentrale vilkåret i fasit-kravet?
| | N100 | N200 | N500 |
|---|---|---|---|
| nøkkelord fra fasit-kroppen | **3 av 3** (`planskilt` 13×, `ÅDT` 14×, `4 000` 3×) | **0 av 6** | **0 av 5** |
| fasit-setningen ORDRETT | **3 ganger** | 0 | 0 |
| **(a)** | **JA** | **NEI** | **NEI** |
Uendret fra P1, og mekanismen er den samme, målt og ikke gjettet: modellen ble bedt om et
kostnadsbesparende tiltak. På N100 ER fasit-kravet kostnadsformet (en terskel som lar deg sløyfe en
dyr konstruksjon), så «svar på oppgaven» og «svar på spørsmålet» sammenfaller. På N200 og N500 gjør
de det ikke, og modellen fulgte oppgaven den fikk — den resonnerte om grunnundersøkelser og om
fjernstyrte bommer, begge fra ANDRE leverte konsepter i kuttet.
### (b′) Navnga modellen konseptet den bygde på?
Ordrens nye mål, og det som P2 finnes for.
| | N100 | N200 | N500 |
|---|---|---|---|
| `req_number` ordrett i svaret | **0** | **0** | **0** |
| `title` ordrett | 0 | 0 | 0 |
| fasit-`concept_id` ordrett | 0 | 0 | 0 |
| `source_element_id` som EGET felt | 0 | 0 | 0 |
| `resource`-URL / `nisosts` | 0 | 0 | 0 |
| leverte konsepter navngitt i det hele tatt | **0 av 8** | **1 av 8** (ikke fasit) | **1 av 8** (ikke fasit) |
| **(b′)** | **NEI** | **NEI** | **NEI** |
**`req_number` er sitert 0 ganger i 18 modellsvar** — og det kan den ikke være, siden feltet aldri
nådde prompten (§ 4). (b′) måler derfor po sin renderer, ikke modellens vilje: ingen implementasjon
av modellen kunne bestått denne raden slik koden står.
**Proveniens sitert: nei × 3.** Verken `source_element_id` eller `resource`-URL-en forekommer i noe
svar. Det er samme årsak.
### (c) Hallusinerte den et kravnummer eller en verdi?
| | N100 | N200 | N500 |
|---|---|---|---|
| kravnummer-formede tokens i svaret | **0** | **0** | **0** |
| tall i prosa som ikke er i delivered | **0** | **0** | **0** |
| konsept-id-referanser som ikke er levert | **0** | **0** | **0** |
| **(c)** | **0** | **0** | **0** |
Ett treff undersøkt og forkastet som instrumentfeil: N100s `4,000` er modellens engelske tusenskille
av det leverte `4 000`. N200s og N500s tall-fragmenter (`03418`, `4678`, `0000`, `4715`, …) er biter
av ekte, leverte konsept-UUID-er som modellen skrev i prosa.
**Kjent-negativ for instrumentet:** første tall-regex fant 0 tokens i N200s og N500s prosa, altså
kunne den ikke ha funnet en hallusinasjon heller. Den ble skjerpet til den fant de ekte tallene
(N100 `4 000` fra kroppen), og målingen over står på den skjerpede.
**Det strukturerte FORSLAGET dikter fortsatt opp kostkoder**, som i P1, og av samme strukturelle
grunn (ingen baseline, og oppgaven krever et tall):
| | kode | finnes i basen |
|---|---|---|
| N100 | `planskilt_kryssing` | **0 av 450 filer** |
| N200 | `03418c46-ad07-4678-bae1-08f441f38903` | 2 av 1 137 — en EKTE konsept-id brukt som kostkode |
| N500 | `RCB01` | **0 av 274 filer** |
**Forskjellen fra P1 er verdt å si:** P1s `03423b12` var en oppdiktet identifikator formet som en
ekte, og den ble VALIDERT. Denne gangen er ingen oppdiktet kode identifikator-formet — de to
oppdiktede er generiske kostlinje-etiketter. Det er ikke en forbedring noen bygget; det er et annet
trekk fra samme modell. **Begge kjøringene ville vært nektet av `--require-cost-baseline`** (§ 2),
og det er den delen som ikke er et sammentreff.
### (d) Kostnad
Listepris, Azure Retail Prices API (`api-version=2023-01-01-preview`, `currencyCode='NOK'`,
`armRegionName=eastus`), **hentet på nytt 08.09**, metere `gpt 4.1 mini Inp/Outp glbl Tokens`:
input NOK 0,003734 / 1K, output NOK 0,014936 / 1K.
| Kjøring | Prompter | Input | Output | NOK |
|---|---:|---:|---:|---:|
| N100 | 6 | 12 217 | 824 | 0,058 |
| N200 | 7 | 25 877 | 1 248 | 0,115 |
| N500 | 5 | 9 619 | 611 | 0,045 |
| klientprobe | 1 | ~20 | ~5 | ~0,000 |
| **SUM** | **19** | **47 733** | **2 688** | **NOK 0,22** |
**NOK 0,22 — 4,4 % av taket på NOK 5, og under ordrens tak på NOK 1. 429-svar: 0.**
De tre nekt-armene kostet **NOK 0,00** (0 modellkall).
### (e) Nevner-vokabularet
**Ikke brukt i noen arm.** `[sourced-not-sufficient]`, `[unread]`, `[sourced]`, `[inferred]` og
`[unsupported]` står **0 ganger i 18 modellsvar**. Som i P1: alle tre armene FANT noe å foreslå i
kuttet, så ingen var i posisjonen markøren finnes for. Det er en ubesvart nevner, ikke et bevis for
at markøren ikke virker.
---
## 6. «Ferdig»-dommen, etter den NYE definisjonen
Operatørvalg 08.09 12:20 (D-1). **Oppslagsspørsmålet «Hva krever Krav X?» er IKKE lenger po sitt
kriterium** — oppslag er Claude Code sin jobb via okf sin C1-oppskrift
(`llm-ingestion-okf/docs/2026-09-08-claude-code-skill-vilkaarlig-bundle.md`, som måler fire spørsmål
over to bundler med fire pass og null oppfunne tall). po sitt kriterium er hypoteseformen:
**ferdig i po = (a) modellen bygger hypotesen på riktig fasit-konsept ELLER nekter forankret
· (b′) navngir konseptet · (c) 0 hallusinasjoner.**
| | (a) | (b′) | (c) | **ferdig** |
|---|---|---|---|---|
| **N100:2023** | **JA** (fasit-setningen ordrett ×3) | **NEI** | **0** | **NEI** |
| **N200:2024** | NEI | **NEI** | **0** | **NEI** |
| **N500:2024** | NEI | **NEI** | **0** | **NEI** |
**0 av 3 — og den bindende raden er nå (b′), på alle tre.**
Det er en annen situasjon enn P1s 0 av 3, og forskjellen er hele poenget:
- **P1: nøkkelen fantes ikke.** For to av tre bundler var kravnummeret fraværende fra modellens
kontekst i det hele tatt. Ingen konsument kunne gjort noe.
- **P2: nøkkelen finnes, og po kaster den.** Alle tre payloadene bærer `req_number`, `title` og en
hentbar `resource`-adresse. `prepass.PrepassExcerpt` ignorerer dem, og `prepass._data_blocks`
renderer dem ikke. **(b′) er derfor ikke en modell-dom — det er en po-dom**, og den kan ikke bli
ja for noen modell før de to linjene endres.
Den andre halvdelen av (a) — «ELLER nekter forankret» — er verdt å lese nøyaktig: med
`--require-cost-baseline` nekter alle tre (§ 2), men da finnes det ingen hypotese å navngi et
konsept for, så (b′) er umålbar og dommen ville vært ufullstendig snarere enn ja. **En N-bundle kan
ikke samtidig være forankret og produsere en hypotese**, fordi et kravkorpus ikke bærer kostlinjer.
Det er ikke en defekt i noen av de tre repoene; det er hva slags korpus en vegnormal er.
### Hvem eier hva som mangler
**llm-ingestion-okf eier ingenting her lenger.** P1s funn 1 er lukket og verifisert i denne
målingen: utdraget bærer `title`, `req_number`, `sources`, `source_element_id` og `source_sha256`,
rangeringen står på rang 1 × 3, kontraktsjekken går exit 0 med 15 regler, og budsjett-instrumentets
egen kjent-positiv er oppdatert i takt med at § 8 flyttet.
**vegnormal-okf eier ingenting.** V2-treet reproduserer sine egne hasher, nevnerne lukker, 0 ufulgte
lenker × 3, og hvert konsept bærer en adresse okf kan bære videre.
**portfolio-optimiser eier begge de gjenstående postene.** (1) `prepass`-sømmen dropper fem felt to
ganger. (2) Kjøreformen: A-formens oppgave er fortsatt hardkodet (`run.py:1243`) og `--explore` er
fortsatt nektet med `--prepass-payload` — men etter D-1 er dét ikke lenger en mangel, det er en
avgrensning operatøren har tatt stilling til.
### Anbefaling til operatøren (beslutningen er din)
1. **La po bære utdragets nye felt gjennom til prompten.** To linjer: navngi feltene på
`PrepassExcerpt` (eller les dem via `model_extra`), og la `_data_blocks` sette `req_number` og
`title` i DATA-avgrenseren ved siden av `concept_id`. Kostnaden er målt: **+366 B per utdrag**
er allerede betalt i payloaden, og rendringen legger til titalls tegn per utdrag. Dette er det
ENESTE som kan gjøre (b′) nåbar, og det er en rød test og en søm — ikke en beslutning.
2. **Ikke gjør N-bundlene til kostnadskjøringer.** `--require-cost-baseline` og
`--derive-cost-baseline` nekter begge, målt, og grunnen er strukturell. Hvis en N-kjøring skal
forankres, må baselinen komme fra prosjektet — ikke fra normalen.
3. **`--require-cost-baseline` bør fortsatt brukes på N-kjøringer der et TALL skal telle.** Den
nektet tre kjøringer som ellers ville stemplet `validated` over kostkoder som ikke finnes i noen
base (§ 5c). Prisen er null.
4. **Etter (1) er kriteriet på nytt målbart for under NOK 0,25.** Samme tre armer, samme spørsmål.
---
## 7. Funn — rapportert, ikke fikset
1. **po dropper utdragets nye felt to ganger** (§ 4). Eier: po. `PrepassExcerpt` er
`extra="ignore"`; `_data_blocks` renderer fire ting. Nevner: 5 av 14 medlemmer når aldri
prompten, og `req_number` er sitert 0 ganger i 18 svar. **Dette er (b′)s eneste årsak.**
2. **Ingen forankret form finnes for en N-bundle** (§ 2). Eier: ingen — det er en egenskap ved
korpustypen. Begge dørene nekter, målt, med rc-0-kontroll.
3. **To av tre validerte forslag bruker oppdiktede kostkoder** (§ 5c). Eier: po (bruksmåte).
`planskilt_kryssing` 0 av 450, `RCB01` 0 av 274. F4-flagget nekter dem, gratis.
4. **Én parse-feil på N200** (§ 3). Ikke en formatfeil — en domene-invariant i `SavingsProposal`
(`claimed 1 500 000 > total 900 000`). Rapportert fordi P1 målte null på alle tre, og fordi
artefaktets tilstedeværelse er signalet.
5. **Nevner-vokabularet ble ikke brukt i noen arm** (§ 5e). Uendret fra P1, samme ubesvarte nevner.
6. **En delstreng-måling av «nådde feltet prompten» er konfundert** (§ 4). Både `req_number` og
`source_element_id` finnes i prompten av HELT andre grunner (spørsmålslinja og `concept_id`).
Nevnt fordi neste måling vil gjøre samme feil om den ikke er skrevet ned.
---
## 8. Ærlighets-grenser, uttalt
- **Én kjøring er én kjøring.** Tre armer, ett spørsmål hver, ingen gjentakelse, én modell.
P1 og P2 er to trekk fra samme modell på nesten samme input, og de er UENIGE om utfallet på to av
tre armer (N100 rejected → validated, N200 validated → rejected). Ingen årsak er tilskrevet; det
er nettopp dét varians ser ut som når nevneren er 1.
- **(b′) måler po, ikke modellen.** Feltet når aldri prompten, så ingen modell kunne bestått raden.
Å skåre den som en modell-svakhet ville vært å bruke definisjonen som gjemmeplass.
- **(a) på N100 er ikke bevis for at kjeden svarer på oppslag.** Det er bevis for at fasit-teksten
nådde modellen og ble brukt, og på N100 sammenfaller de to spørsmålene ved et sammentreff i
kravets innhold. Sammentreffet er identifisert, ikke skjult — og etter D-1 er oppslag uansett
ikke po sitt kriterium.
- **(c) = 0 gjelder svaret.** Det strukturerte forslaget dikter opp kostkoder, som er strukturelt
påkrevd i en uforankret kjøring. Skillet er uttalt i § 5c, ikke skjult av definisjonen.
- **AVVIK fra ordren, uttalt:** de tre betalte armene er kjørt UTEN `--require-cost-baseline`.
Med flagget koster de null og måler null (§ 2). Nekten er rapportert som D-3s resultat, og armene
som måler det nye utdraget er kjørt uten det.
- **`--cost-vocabulary`, `--k` og `--rarity-weight` ble IKKE brukt.** Fasit sto på rang 1 med
default-kommandoen på alle tre, så betingelsen for opt-in-flaggene inntraff aldri.
- **Kuttet er verifisert mot den monterte basen** (`admit_payload` × 3), så payloaden kan ikke ha
levert bytes basen ikke holder. Det er en gate, ikke en tillitserklæring til produsenten.
- **Ingen av bundlene er faglig gjennomgått.** Sammenlikningen i (a) er mot konseptkroppen slik den
står, ikke mot vegnormalen.
- **`sources`-adressen er ikke hentet av po.** vegnormal-okf målte at URL-en returnerer bytes som
er bytelike med `source_sha256`; den målingen er deres, ikke gjentatt her.
---
## 9. Verifiseringslogg
| # | Påstand | Slik den ble verifisert |
|---|---|---|
| 1 | okf HEAD `171798e`, treet rent | `git -C ~/repos/llm-ingestion-okf log --oneline -1` + `status --short` (tom) |
| 2 | Tre V2-hasher reproduserer | `payload.bundle.ref` mot vegnormal-okfs melding, 3 av 3 ordrett |
| 3 | 446 / 1 133 / 270 konsepter, 0 skipped | `okf.navigate_bundle(...).context_files`, po sin egen kode |
| 4 | Fasit på rang 1 × 3 | indeks av fasit-id-en i `payload.excerpts`, flaggløs kommando |
| 5 | Utdraget har 14 medlemmer | `sorted(payload['excerpts'][0].keys())`, 3 av 3 |
| 6 | Kontraktsjekk exit 0 × 3 | `okf_contract_check.py --skill … --payload …`, 15 regler, 0 funn |
| 7 | `admit_payload` OK × 3 | `prepass.admit_payload(p, bundle_dir=…, resolved_id=…)` |
| 8 | Klientproben grønn | `uv run pytest tests/test_foundry_profile_live.py -q` → 1 passed |
| 9 | Deployment urørt | `az … deployment show` → GlobalStandard, 100, `2025-04-14` |
| 10 | Flagget nekter × 3, 0 modellkall | full kjøring, `assert not records` i måleskriptet, rc 1 |
| 11 | rc-0-kontroll uten flagget | samme argv uten `--require-cost-baseline` → rc 0, 3 av 3 |
| 12 | `--derive-cost-baseline` nekter × 3 | rc 1 + nekt-teksten navngir de tre påkrevde kolonnene |
| 13 | po dropper feltene ved parsing | `sorted(excerpt.model_dump())` → 7 nøkler; `_Permissive` er `extra="ignore"` |
| 14 | po dropper dem ved rendering | `prepass.render_context(...)`, DATA-blokka sitert ordrett |
| 15 | Delstreng-målingen var konfundert | de to «treffene» lokalisert til spørsmålslinja og `concept_id` |
| 16 | (a) N100 ja | fasit-setningen ORDRETT 3 ganger; `planskilt`/`ÅDT`/`4 000` alle til stede |
| 17 | (a) N200/N500 nei | 0 av 6 / 0 av 5 nøkkelord fra hver fasit-kropp i noe svar |
| 18 | (b′) nei × 3 | `req_number`/`title`/`concept_id`/`element_id`/URL: 0 treff i 18 svar |
| 19 | (c) = 0 | kravnummer-formede tokens: 0; tall i prosa mot delivered, ett treff forkastet |
| 20 | Instrumentet for (c) virker | skjerpet til det fant `4 000` fra kroppen; første form fant 0 av 0 |
| 21 | Kostkodene | `grep -rl` i hver base med nevner; fasit-id-en som kjent-positiv (2 treff) |
| 22 | Parse-feilen på N200 | `p2-n200-free-parse-failures.json` finnes; feilteksten sitert ordrett |
| 23 | Prisene | Azure Retail Prices API hentet på nytt 08.09 |
| 24 | 0 × 429 | `retries`-telleren i alle 18 poster |
---
## 10. P3 (ordre `20260908T141941Z`) — po bærer utdragets nye felt gjennom til prompten
PM-valg 08.09 14:20Z, på denne målingens egen anbefaling (§ 6): **ja, to linjer, rød test først.**
### 10.1 Reproduksjon (steg 1, uendret fra § 4)
```python
>>> sorted(json.load(open("scratchpad/nbundler-p2/payload-n500.json"))["excerpts"][0].keys())
['adjudication', 'bundle_id', 'bundle_id_inherited', 'concept_id', 'rank', 'req_number',
'sha256', 'source_element_id', 'source_sha256', 'sources', 'text', 'text_sha256', 'title',
'trust_tier'] # 14 medlemmer, i payloaden
>>> sorted(prepass.load_prepass_payload("scratchpad/nbundler-p2/payload-n500.json").excerpts[0]
... .model_dump().keys())
['adjudication', 'bundle_id', 'concept_id', 'sha256', 'text', 'text_sha256', 'trust_tier']
# 7 medlemmer, etter parsing
>>> "req_number" in prepass.render_context(...)
False # aldri i prompten (§ 4)
```
### 10.2 Feltform, valgt med måling
Spørsmålet ordren stiller: hvilken form overlever en ukjent `source_foo`-nøkkel fra en FRAMTIDIG
produsent, uten kodeendring her? okfs egen melding (§ 4, sitert) kaller `source_*` en **prefiks-
regel, ikke en allowlist** — K2 bærer fem slike lokatorer der N-bundlene bærer to. To former ble
sammenliknet mot nøyaktig det spørsmålet:
- **Navngitte felt** (`source_element_id: str | None`, `source_sha256: str | None`, …): en tredje
`source_*`-nøkkel krever et NYTT felt på `PrepassExcerpt` og en ny rendringslinje — en
kodeendring, nøyaktig det prefiksregelen sier man IKKE skal måtte gjøre.
- **`model_extra`** (`extra="allow"` på `PrepassExcerpt` ALENE, lest tilbake via et
`source_`-prefikssøk i `source_locators()`): en ny `source_*`-nøkkel havner unavngitt i
`model_extra` og plukkes opp av SAMME søk, null kodeendring.
**Valgt: `model_extra` for `source_*`-familien.** `title`, `req_number` og `sources` er derimot
navngitte felt — de er SS-8-deklarerte, entallige medlemmer (aldri en voksende familie), og en
`model_extra`-lesning av dem ville kastet bort pydantics egen validering for ingen gevinst.
Testen `test_a_future_source_star_key_survives_without_a_code_change`
(`tests/test_prepass_excerpt_fields_loadbearing.py`) legger til en TREDJE, aldri navngitt
`source_page_number`-nøkkel og viser at den når `source_locators()` uendret.
### 10.3 Fiksen: to steder, ikke to linjer
Anbefalingen i § 6 anslo "to linjer"; målt var det to STEDER (parsing + rendering), hver med mer
enn én linje fordi `sources` trengte en egen typet klasse (`PrepassSource`, med `resource` og en
valgfri `title`) og `source_locators()` trengte en egen metode:
1. **Parsing** (`PrepassExcerpt`): `title: str | None`, `req_number: str | None`,
`sources: tuple[PrepassSource, ...] | None`, alle default `None` (de fleste payloader i dette
repoet er fra FØR P2s produsent-endring og bærer ingen av dem — et påkrevd felt ville refusert
hver payload skrevet før i dag). `model_config = ConfigDict(extra="allow")` — kun på DENNE
klassen, `_Permissive`s `extra="ignore"` står urørt for `PrepassBundle`/`PrepassBudget`/osv.
2. **Rendering** (`_excerpt_header`, ny funksjon `_data_blocks` nå kaller): `req_number` og
`title` legges til i parentesen ved siden av `adjudication`/`trust_tier` NÅR de finnes;
`sources[0].resource` (adressen) og hver `source_*`-lokator (generisk, via
`source_locators()`) legges til deretter. **Kjent-negativ:** når ingen av de nye feltene
finnes, er sløyfa en no-op og headeren er BYTE-IDENTISK med før P3
(`test_a_p1_form_payload_renders_the_header_exactly_as_before`,
`test_a_p1_form_payload_render_seed_also_unchanged`) — pinnet LITERALT, ikke med en
delstreng-assert, fordi en lekkende tom-klausul (f.eks. en etterlatt `, `) ikke ville vist seg
i et substring-søk.
`render_seed` deler `_data_blocks` med `render_context` (ko-(p)); en fiks som bare traff den ene
armen ville latt utforsknings-døra ligge stille bak debatt-døra —
`test_render_seed_also_carries_the_new_fields` gater begge.
### 10.4 Rødt → grønt → mutasjon rød
Ny fil: `tests/test_prepass_excerpt_fields_loadbearing.py`, 10 arm. FØR fiksen: **8 røde, 2
grønne** (de to kjent-negative armene er trivielt grønne før fiksen òg — de er kontroller, ikke
mål). ETTER fiksen: **10 av 10 grønne.**
**Mutasjon** (revert `_excerpt_header`/`_data_blocks` til § 4s form, midlertidig, aldri committet):
3 av 10 røde — nøyaktig de tre rendrings-armene
(`test_the_data_block_renders_req_number_title_and_the_address`,
`test_the_data_block_renders_a_future_locator_too`, `test_render_seed_also_carries_the_new_fields`)
— parsing-armene forblir grønne (mutasjonen rørte ikke `PrepassExcerpt`), som beviser at de to
halvdelene av fiksen har HVER SIN uavhengige gate. Fiksen restaurert fra en kopi (`shasum -a 1`
verifisert identisk før/etter), aldri `git checkout`.
### 10.5 Suite, golden, lint
**1521 passed, 5 skipped** (var 1511 — 10 nye), `pytest -q`, 272 s. `ruff check src tests` og
`ruff format --check src tests`: rene (94 funn i `scratchpad/` er FØR-eksisterende, utracket,
utenfor scope — samme avgrensning som K3-ordrenes `ruff check src tests tools`). `mypy src`: 0
funn, 37 filer. Golden `demo-transcript.stdout` UENDRET: `shasum -a 1` = `ea8c534…` — forventet,
`simulation.py` importerer ikke `prepass` i det hele tatt.
### 10.6 Én betalt N100-kjøring med `--require-cost-baseline` — og (b′) er STRUKTURELT ikke dekket
Samme oppsett som § 3 (`live_nbundler_p2.py n100 --require-cost-baseline`), med den FIKSEDE koden.
```
KJENT-POSITIV n100: navigate_bundle=446 (fasit 446) OK
run refused: this run was required to be anchored, but the knowledge base offers no cost baseline: …
===== ARM n100 [req] (rc=1) =====
MODELLKALL: 0 (gaten MAA fyre foer foerste kall)
```
**rc=1, 0 modellkall, NOK 0,00.** Dette REPRODUSERER § 2s funn ordrett — `--require-cost-baseline`
nekter N100 FØR første modellkall, fordi en vegnormal ikke bærer kostlinjer (strukturelt, uendret
av denne fiksen). **Konsekvens: (b′) kan IKKE måles i denne armen** — det oppstår aldri en
hypotese å navngi et konsept for, uansett hvor godt utdraget bærer `req_number`/`title`. Dette er
IKKE en regresjon fra fiksen; det er § 2s "en N-bundle kan ikke samtidig være forankret og
produsere en hypotese" gjentatt på den fiksede koden.
**(b′) er derfor "ikke dekket" i DENNE armen — strukturelt, ikke som en feil ved fiksen.**
Fiksens EGEN korrekthet er verifisert der den kan verifiseres gratis: offline, mot payloadens
faktiske felt (§ 10.1–10.4). En live gjenmåling av (b′) på den FRIE armen (uten flagget, slik § 3
kjørte N100) ville kostet et nytt betalt kall utenfor denne ordrens scope (ordren navngir
eksplisitt `--require-cost-baseline`-armen som den ENE betalte kjøringen); det er ikke gjort her.
### 10.7 Funn — rapportert, ikke fikset
7. **(b′) er umålbar under `--require-cost-baseline` for enhver N-bundle** (§ 10.6). Samme
struktur som funn 2 i § 7, nå bekreftet på den fiksede koden. Ingen handling — F4 gjør nøyaktig
det den ble bygget for.
8. **En live gjenmåling av (b′) på den frie N100-armen (uten flagget) er ikke gjort** her — ordren
navngir `--require-cost-baseline` som den ene betalte kjøringen, og en fri kjøring ville vært
en ny, ubedt betalt handling.
## 11. P4 (ordre `20260908T151403Z`) — den frie N100-armen, ETTER fiksen: (b′) = JA
PM-valg 08.09 15:14Z, på § 6 punkt 4 og § 10.7 funn 8s egen anbefaling: funn 8s hull lukkes med
ÉN betalt, flaggfri N100-kjøring. Ingen `src/`-endring; ny kjørefil KUN i `scratchpad/`
(`live_p4_free.py`, en kopi av `live_nbundler_p2.py`s frie arm med eget run-id/filnavn, for å ikke
overskrive P2s eksisterende `n100-free-*`-evidens).
### 11.1 Repro (steg 1, gratis)
```python
>>> len(json.load(open("scratchpad/nbundler-p2/payload-n100.json"))["excerpts"])
14 # medlemmer i excerpts[0], uendret
```
```
--- BEGIN DATA krav/N100/id-4b61eee9-…-c15a (adjudication: unknown, trust_tier: unverified,
req_number: Krav 3.3.1—13, title: Krav 3.3.1—13 H1 – Nasjonal hovedveg, ÅDT < 6 000 og
fartsgrense 80 km/t, source: https://viewers.vegnorm.vegvesen.no/api/nisosts/859984?…
```
`req_number` og `title` står ORDRETT på `BEGIN DATA`-avgrenserens EGEN linje (§ 7 funn 6: målt her
på avgrenseren, ikke på hele prompten, som § 7 selv advarte var konfundert).
**Klientprobe FØR armen, begge env-variabler satt:** `PORTFOLIO_FOUNDRY_PROJECT_ENDPOINT` (utledet
INLINE fra `az cognitiveservices account show --name po-foundry-ktg --resource-group
portfolio-optimiser-rg`, PROSJEKT-formen `…/api/projects/po-project`) + `PORTFOLIO_FOUNDRY_DEPLOYMENT`
= `gpt-4-1-mini`. `uv run pytest tests/test_foundry_profile_live.py -q` → **1 passed** (ikke skippet).
### 11.2 Den betalte kjøringen
`uv run --with tiktoken python scratchpad/nbundler-p2/live_p4_free.py`, profil `azure`,
`gpt-4-1-mini`, `PACE_SECONDS=2`, `PORTFOLIO_MODEL_MAP=scratchpad/major2-live/model_map.json`,
`--run-id p4-n100-free`, UTEN `--require-cost-baseline` og UTEN `--derive-cost-baseline`. Rå
resultat:
```
KJENT-POSITIV n100: navigate_bundle=446 (fasit 446) OK
rang 1 = krav/N100/id-4b61eee9-…-c15a req_number='Krav 3.3.1—13'
===== ARM p4-n100-free (rc=0) =====
N100: Rejection (no expert verdict given; verdict key=ca7cd8e99c6cc2d9)
proposal review offered, never consulted (no candidate validated)
--- ledger ---
checker prompter=1 in=4693 out=99
proposer prompter=5 in=10716 out=648
prompter totalt: 6 429-retries: 0
```
**rc=0, 6 modellkall (5 proposer, 1 checker), ingen 429.** `--proposal-review` var satt (skriptet
manus svarer `approve` på hvert spørsmål, uten at noe forslag noensinne når validatoren — checkeren
avviste ikke, men ingen kandidat ble VALIDERT, se § 11.4).
### 11.3 Modellsvarene, ordrett (utdrag)
Proposer, forsøk 1:
> A concrete cost-saving measure for the N100 project could be: **Avoid planskilt (grade-separated)
> crossings between pedestrian/cycle paths and roads when the ÅDT (Average Daily Traffic) is 4,000
> or less.** Rationale based on **Krav 3.3.1—13** from the N100 knowledge base: The requirement
> states that "Eventuell kryssing mellom gang- og sykkelveg og veg skal være planskilt ved
> ÅDT > 4 000." …
Checker:
> The N100 excerpt **Krav 3.3.1—13** clearly states that crossing between pedestrian/bicycle paths
> and roads must be grade separated (planskilt) only if ÅDT > 4,000. … VERDICT: APPROVE
### 11.4 (b′)/(a)/(c) — med nevner, samme rader som § 5
| | N100 (P4, fri, ETTER fiksen) |
|---|---|
| `req_number` (`Krav 3.3.1—13`) ordrett | **6 av 6 svar** |
| `title` ordrett (hele strengen) | 0 |
| fasit-`concept_id` ordrett | 0 |
| `source_element_id` som eget felt | 0 |
| `resource`-URL/`nisosts` | 0 |
| leverte konsepter navngitt i det hele tatt | **1 av 8 — OG DET ER FASIT-KONSEPTET** (ulikt P2s N200/N500, hvor det navngitte konseptet IKKE var fasit) |
| **(b′)** | **JA** |
| | N100 (P4) |
|---|---|
| fasit-setningen ORDRETT (`"Eventuell kryssing … ÅDT > 4 000."`) | **1 gang** (proposer, forsøk 1) |
| `planskilt` / `ÅDT` / `4 000`-formet tall | 10 / 10 / 11 |
| **(a)** | **JA** |
| | N100 (P4) |
|---|---|
| kravnummer-formede tokens i svaret UTOVER `Krav 3.3.1—13` | **0** (regex `Krav\s+[\d.]+—\d+` finner ETT distinkt token på tvers av alle 6 svar) |
| tall i prosa som ikke er i det leverte | 0 (`4,000`/`4 000` er samme fasit-tall, engelsk tusenskille — § 5c-instrumentfeilen gjentatt, ikke en ny hallusinasjon) |
| konsept-id-referanser som ikke er levert | 0 |
| **(c)** | **0** |
**Kjent-negativ for instrumentet, kjørt FØR dommen:** samme spørring (`"Krav 3.3.1—13"` i teksten)
mot P2s `n100-free-records.json` (FØR fiksen, 6 svar) gir **0** — spørringen er ikke konfundert.
**Det strukturerte forslaget dikter fortsatt opp kostkoder** (`CS-GS1`, `planSkiltCrossing`,
`GRD-CRS-01` — ingen finnes i basen), av samme strukturelle grunn som § 5c: ingen baseline, og
`--derive-cost-baseline` ble bevisst IKKE brukt her (ordrens scope). Uendret funn, ikke en ny defekt.
**«Ferdig»-raden for N100, etter § 6-definisjonen:** (a) JA · (b′) **JA** (var NEI i P1/P2) ·
(c) 0. **N100 = FERDIG.** Første gang en av de tre N-bundlene når «ferdig» i po sin hypoteseform —
P3s fiks var det som gjorde det nåbart, og denne kjøringen er beviset på at det ER nådd, ikke bare
at koden er riktig offline.
**Er (b′) MODELL-dom, ikke po-dom:** feltet sto i DATA-avgrenseren (§ 11.1); at modellen faktisk
brukte det — siterte `req_number` ordrett i 6 av 6 svar, og bygde hypotesen på nøyaktig det
kravet fasiten peker på — er noe modellen gjorde, ikke noe po garanterte. Oppgaven prompten stiller
er fortsatt A-formens hardkodede `"Find a cost-saving measure for {project.id}."`
(`run.py:1243`, § 6 «Hvem eier hva») — ikke et spørsmål om Krav 3.3.1—13 spesifikt; at modellen
likevel fant og navnga akkurat det kravet, i et kutt på 8 av 446, er dømmekraft utenfor det po kan
ta æren for.
### 11.5 Kostnad
Samme listepris som § 5d (input NOK 0,003734/1K, output NOK 0,014936/1K):
| Kjøring | Prompter | Input | Output | NOK |
|---|---:|---:|---:|---:|
| N100 (P4, fri) | 6 | 15 409 | 747 | 0,069 |
| klientprobe | 1 | ~20 | ~5 | ~0,000 |
| **SUM** | **7** | **15 429** | **752** | **NOK 0,07** |
**NOK 0,07 — under ordrens tak på NOK 0,25. 429-svar: 0.**
### 11.6 Funn — rapportert, ikke fikset
9. **Ingen nytt funn som okf eier.** Utdraget bar begge feltene korrekt (§ 11.1); at modellen brukte
dem er en modell-egenskap, ikke en utdragsform-defekt. Ingen `coord-send` til llm-ingestion-okf.
10. **`--proposal-review` fikk ingen kandidat å vise** — checkeren avviste aldri, men heller ingen av
de fem proposer-forsøkene ble VALIDERT av den deterministiske validatoren (ingen baseline, se
§ 11.4s kostkode-funn), så reviewer-døra sto åpen uten noe å spørre om. Uendret struktur fra
P1/P2, ikke en ny defekt.

View file

@ -1,416 +0,0 @@
# Konsum-målingen på N100, N200 og N500 — hentet, resonnert og sitert, mot en LEVENDE modell
> **Ordre `20260908T113200Z-46497613-from-.claude`.** «Ferdig» skulle bety at portfolio-optimiser
> henter, resonnerer og siterer riktig fra hver av de tre bundlene med en levende modell.
> Tre betalte armer i A-formen (`--prepass-payload`), én per bundle, samme oppsett som S7c.
> Underlag: vegnormal-okf `docs/2026-09-08-n100-n200-n500-ferdig.md` (`6d953f8`),
> llm-ingestion-okf worktree `llm-ingestion-okf-wt-o2c` @ `a37d5ce`.
>
> **Svaret er nei på alle tre — og de tre grunnene er ulike, målt, og eies av hvert sitt repo.**
---
## 0. Hva som ER målt, og hva som IKKE er det
**MÅLT.** De tre tre-hashene reprodusert ordrett fra vegnormal-okfs melding. `okf.navigate_bundle`
når 446 / 1 133 / 270 konsepter — po sin egen kode, mot V1s tall. Pre-passet leverer fasit-konseptet
på **rang 1 av 8 på alle tre**, med default-kommandoen, ingen flagg. Kontraktsjekk exit 0 × 3.
Klientproben grønn. Tre betalte kjøringer, rc 0 × 3, **NOK 0,21 av taket 5, 0 × 429**. Hver rolles
prompter, prompt-tokens og leverandør-tokens. Hvert modellsvar ordrett. Nekten `--explore` +
`--prepass-payload` (rc 1, null modellkall). Hvilke felt pre-passets utdrag faktisk bærer.
**IKKE MÅLT.** **Én kjøring er én kjøring** — tre armer, ett spørsmål hver, én modell
(`gpt-4-1-mini`), ett deployment. Ingen arm er gjentatt, så ingenting her sier noe om varians.
At en ANNEN modell ville lest det samme kuttet annerledes er ikke målt. Om rangeringen holder for
andre spørsmål enn V1s tre er ikke målt her (okf måler det). Ingen av de tre bundlene er
faglig gjennomgått av meg — jeg sammenlikner mot konseptkroppen, ikke mot vegnormalen.
**IKKE RØRT.** Ingen fil under `src/`. Ingen Azure-konfig. Ingen fil i vegnormal-okf eller i noen
okf-checkout. Ingen push.
---
## 1. Kjent-positiv per bundle, FØR noe ble betalt
Måleprotokollens stige: alt gratis først, så en feil i en betalt arm er attribuerbar.
```bash
OKF=~/repos/llm-ingestion-okf-wt-o2c # ren worktree, a37d5ce, egen venv
B=~/repos/vegnormal-okf/build/ferdig
$OKF/.venv/bin/python3 $OKF/tools/okf_consume.py $B/n100-2023 \
--question "Hva krever Krav 3.3.1—13 i N100? Gjengi det sentrale vilkåret." --out payload-n100.json
# ... tilsvarende for n200-2024 og n500-2024, DEFAULT-kommandoen, ingen flagg
```
| kjent-positiv | N100:2023 | N200:2024 | N500:2024 |
|---|---|---|---|
| `sha256-tree` reproduserer V1s ref | **JA** `5f288f2c…` | **JA** `c420b754…` | **JA** `eb74e987…` |
| `okf.navigate_bundle` → konsepter | **446** (fasit 446) | **1 133** (fasit 1 133) | **270** (fasit 270) |
| `Bundle.skipped` (ufulgte lenker) | 0 | 0 | 0 |
| considered / withheld / delivered | 446 / 438 / 8 | 1 133 / 1 125 / 8 | 270 / 262 / 8 |
| budsjett brukt av 120 000 | 8 939 | 21 102 | 9 085 |
| **fasit-konseptets rang** | **1 av 8** | **1 av 8** | **1 av 8** |
| `okf_contract_check` | **exit 0**, 14 regler, 0 funn | **exit 0**, 14 regler, 0 funn | **exit 0**, 14 regler, 0 funn |
| `prepass.admit_payload` mot montert base | **OK** | **OK** | **OK** |
| klientprobe `test_foundry_profile_live` | **1 passed** — kjørt FØR armene | (samme kjøring) | (samme kjøring) |
`a37d5ce` gjør det den sier: fasit-konseptet står på **rang 1 på 3 av 3** med den flaggløse
kommandoen. Nevnerne lukker mot V1 til konseptet. **Budsjettforbruket avviker fra V1s tall**
(8 939 mot 8 977 · 21 102 mot 17 818 · 9 085 mot 10 517) — forventet, og det er selve fiksen:
oppslags-omordningen leverer et ANNET sett med åtte utdrag enn den V1 målte.
Deployment urørt, målt med `az cognitiveservices account deployment show`: `GlobalStandard`,
`capacity` **100**, versjon `2025-04-14`, `Succeeded`.
### 1.1 En egenskap S7a-3 kjøpte, som først nå ble betalt for
Alle tre basene erklærer `bundle_id` i rot-`index.md` (`vegnormal-n100-2023`) mens de er montert som
`n100-2023`. Til 03.09 NEKTET `reconcile_bundle_id` nøyaktig det. Uten slakke-beslutningen (økt 81)
kunne **ingen av de tre bundlene åpnes slik de er levert** — botemiddelet ville vært å montere dem
på nytt for hånd, én gang per leveranse. Kjøringen sier avviket høyt i stedet:
```
Knowledge base: declares bundle_id 'vegnormal-n100-2023' (source: declared-index) but is mounted
as 'n100-2023' — the DECLARED id is the identity, so every artefact this run stamps names
'vegnormal-n100-2023'
```
---
## 2. Kjøreformen: po har INGEN oppslags-dør i A-form, og nekten er målt
Ordren ba meg velge kjøreform og begrunne, og sa at en nekt er et funn. Den er det.
**Debattens oppgave er hardkodet.** `run.py:1243`, ett eneste forekomststed:
```python
result = await debate.run(f"Find a cost-saving measure for {project.id}.\nContext:\n{context}")
```
Ingen av `run_project`s **26 parametre** bærer et spørsmål, en prompt, et objective eller en task
(målt med `inspect.signature`). Operatørens spørsmål når modellen KUN som pre-passets erklærte
`question`-linje inne i `context`.
**Den ene døra som bærer en operatør-prompt er `--explore`, og den er NEKTET med `--prepass-payload`:**
```
$ python -m portfolio_optimiser.run N100 --profile local --docs-dir B --bundle-dir B \
--explore "Hva krever Krav 3.3.1—13 i N100? …" --explore-config /dev/null \
--prepass-payload payload-n100.json
run refused: --prepass-payload and --explore cannot be combined (the exploration navigates the
whole knowledge base with the same four tools the payload withdraws, so the run as a whole would
read far outside the cut it declares) # rc 1, null modellkall
```
**Valget, og begrunnelsen:** jeg kjørte A-formen som ordren spesifiserer, fordi det er den eneste
formen som finnes for et deklarert kutt, og fordi den andre armen (`--prepass-seed`, som VILLE båret
spørsmålet som `explore()`s prompt) er utenfor denne ordrens gjerde. Konsekvensen er målt og
uttalt, ikke bortforklart:
| | N100 | N200 | N500 |
|---|---|---|---|
| oppgavelinja modellen fikk | `Find a cost-saving measure for N100.` | `… for N200.` | `… for N500.` |
| V1s spørsmål ORDRETT i prompten | ja, i **2 av 6** prompter | ja, i **2 av 5** | ja, i **2 av 6** |
Modellen fikk altså både spørsmålet og fasit-teksten — men ble ALDRI bedt om å svare på spørsmålet.
Det er ikke en feilkonfigurasjon; det er formen po har.
---
## 3. Tre betalte armer
```bash
export PORTFOLIO_FOUNDRY_PROJECT_ENDPOINT=… # utledet INLINE fra `az`, aldri i fil
export PORTFOLIO_MODEL_MAP=scratchpad/major2-live/model_map.json
export PACE_SECONDS=2
uv run --with tiktoken python scratchpad/nbundler/live_nbundler.py {n100|n200|n500}
# argv: <N100|N200|N500> --profile azure --docs-dir <base> --bundle-dir <base>
# --proposal-review --outbox-dir … --run-id nb-<tag> --prepass-payload payload-<tag>.json
```
Innsprutspunktet er `run._default_factory` (Fase 4e). Alle roller LEVENDE.
| | **N100** | **N200** | **N500** |
|---|---:|---:|---:|
| rc | 0 | 0 | 0 |
| prompter totalt | **6** | **5** | **6** |
| — debatt / generering | 3 / 3 | 3 / 2 | 3 / 3 |
| proposer / checker | 5 / 1 | 4 / 1 | 5 / 1 |
| prompt-tokens (o200k), debatt | 6 826 | 15 595 | 5 553 |
| prompt-tokens (o200k), generering | 800 | 483 | 782 |
| leverandør-input | 12 057 | 24 582 | 10 115 |
| leverandør-output | 718 | 822 | 817 |
| **429-svar** | **0** | **0** | **0** |
| parse-feil-artefakt | **fraværende** | **fraværende** | **fraværende** |
| verktøykall i debatten | **0** (A-form: verktøyene trukket) | **0** | **0** |
| validator-dom | rejected (stage 4, P90) | **validated** | rejected (stage 4, P90) |
| dom-nøkkel | `d7eeac666dbe88dc` | `7326ad0ec7353039` | `1059ab348ea87f48` |
**Null parse-feil på tre ferske korpora.** `{run_id}-parse-failures.json` finnes ikke i noen av de
tre utboksene, og filens tilstedeværelse ER signalet (økt 35). Den avledede grammatikken (Fase 1b
funn 1b) ble akseptert av det levende endepunktet på tre korpora den aldri er testet mot — det
lukker en av den radens uttalte ærlighets-grenser.
**Steg 5 fyrer LEVENDE på begge de avviste armene.** Forsøk 2 og 3 bærer ordrett «your previous
proposal was REJECTED by the deterministic validator», og påstanden faller monotont:
| | forsøk 1 | forsøk 2 | forsøk 3 |
|---|---:|---:|---:|
| N100 `claimed_saving_nok` | 3 000 000 | 1 500 000 | 600 000 (P90 = 530 585) |
| N500 `claimed_saving_nok` | 350 000 | 210 000 | 90 000 (P90 = 81 323) |
---
## 4. Målene (a)–(e), med nevner
### (a) Svarte modellen med det sentrale vilkåret i fasit-kravet?
Ordrett sammenlikning mot konseptkroppen.
**N100 — JA.** Fasit-kroppen er én setning, og modellen gjenga den **ORDRETT**:
> «Eventuell kryssing mellom gang- og sykkelveg og veg skal være planskilt ved ÅDT > 4 000.»
og resonnerte riktig om den (ÅDT ≤ 4 000 ⇒ planfri kryssing ikke påkrevd). Nøkkelordene
`planskilt`, `ÅDT` og `4 000` står alle i svaret.
**N200 — NEI.** Fasit er Krav 2.9.2—12, filterkriterier for friksjonsjordarter, med tabell
2.9.2—1. Nevner: **0 av 6** nøkkelord fra kroppen (`filterkriteri`, `friksjonsjordart`, `2.9.2`,
`O90`, `D90`, `filter`) står i noe av modellens svar. Modellen resonnerte i stedet om
grunnundersøkelser, fra et ANNET levert konsept.
**N500 — NEI.** Fasit er Krav 10.2—2, tekniske bygg som egne brannceller. Nevner: **0 av 6**
nøkkelord (`branncelle`, `brannsikker`, `teknisk(e) bygg`, `10.2`, `byggteknisk`). Modellen
resonnerte om tunnelportaler, fra et annet levert konsept.
**Mekanismen er målt, ikke gjettet:** modellen ble bedt om et kostnadsbesparende tiltak. På N100
ER fasit-kravet kostnadsformet (en terskel som lar deg sløyfe en dyr konstruksjon), så det å svare
på oppgaven og å svare på spørsmålet SAMMENFALLER. På N200 og N500 gjør de det ikke, og modellen
fulgte oppgaven den fikk.
### (b) Siterte den riktig konsept-id?
Ordren gir to instrumenter. Begge rapporteres, og bare det ene er bærende.
| | N100 | N200 | N500 |
|---|---|---|---|
| stempelets siteringer | 8 | 8 | 8 |
| stempel ∩ delivered | **8 av 8** | **8 av 8** | **8 av 8** |
| fasit-id i stempelet | **JA** | **JA** | **JA** |
| **modellen navnga fasit-id-en** | **NEI** (navnga ingen id) | **NEI** (navnga `id-03418c46…`) | **NEI** (navnga `id-05152184…`) |
**Stempel-lesningen kan ikke diskriminere, og er derfor ikke bærende.** Stempelet siterer de leverte
konseptene MEKANISK, uavhengig av hva modellen leste — det ville vært `8 av 8` også for en kjøring
der modellen leste ingenting. Det er repoets egen vakuøs-gate-klasse. Den bærende lesningen er hva
modellen faktisk refererte, og der er svaret **nei på alle tre**. De to id-ene modellen DID navngi
er begge ekte, leverte konsepter — bare ikke fasit.
Konsept-id-en er tilgjengelig for modellen: `render_context` avgrenser hvert utdrag med
`--- BEGIN DATA <concept_id> ---`. På N200 og N500 brukte modellen den; på N100 lot den være.
### (c) Hallusinerte den et kravnummer eller en verdi som ikke står i kroppen?
Kjent-negativ: tall i modellens PROSA som ikke finnes i delivered-mengden, etter at UUID-fragmenter
er fjernet (ellers teller instrumentet biter av ekte id-er som oppdiktede tall).
| | N100 | N200 | N500 |
|---|---|---|---|
| kravnummer-formede tokens i prosa | ingen | ingen | ingen |
| tall i prosa ikke i delivered | **0** | **0** | **0** |
| konsept-id-referanser som ikke er levert | **0** | **0** | **0** |
**(c) = 0 på alle tre i prosaen.** Modellen siterte kun det den hadde. To treff ble undersøkt og
forkastet som instrumentfeil: N200s `03418` er et fragment av den ekte, leverte id-en
`id-03418c46-…` (modellen skrev den uten `id-`-prefikset), og N500s `500` er normalens eget navn.
**Men det strukturerte FORSLAGET er en annen sak, og der er det ett ekte funn.** N200s validerte
forslag bruker to kostkoder:
```json
"affected_items":[{"code":"03418c46","quantity":1,"unit_cost":15000000},
{"code":"03423b12","quantity":500,"unit_cost":4500}]
```
`03418c46` er et **kravnummer brukt som kostkode** — en ekte konsept-id, men ikke en kostlinje.
`03423b12` finnes **0 ganger blant basens 1 137 filer** (målt). Det er en oppdiktet identifikator,
formet som en ekte. Begge ble **VALIDERT**, fordi kjøringen er UFORANKRET og validatorens stage 0
derfor hoppes over — nøyaktig den defekten `--require-cost-baseline` (F4, økt 100) ble bygget for,
og det første LEVENDE tilfellet av den. Kjøringen sa det selv, i klartekst:
> `Cost baseline: NONE in the bundle — this run is un-anchored: the validator's stage 0 … is SKIPPED`
Dette er ikke telt under (c) etter ordrens definisjon (tall i SVARET mot delivered-mengden), men å
la det stå urapportert ville vært å bruke definisjonen som en gjemmeplass.
### (d) Kostnad
Listepris, Azure Retail Prices API (`api-version=2023-01-01-preview`, `currencyCode='NOK'`,
`armRegionName=eastus`), **hentet på nytt 08.09**, metere `gpt 4.1 mini Inp/Outp glbl Tokens`:
input NOK 0,003734 / 1K, output NOK 0,014936 / 1K.
| Kjøring | Prompter | Input | Output | NOK |
|---|---:|---:|---:|---:|
| N100 | 6 | 12 057 | 718 | 0,056 |
| N200 | 5 | 24 582 | 822 | 0,104 |
| N500 | 6 | 10 115 | 817 | 0,050 |
| klientprobe | 1 | ~20 | ~5 | ~0,000 |
| **SUM** | **18** | **46 754** | **2 357** | **NOK 0,21** |
**NOK 0,21 — 4,2 % av taket på NOK 5. 429-svar: 0.** Takene (`max_rounds=3`, `max_tokens`,
`max_attempts`) er URØRT, og ingen kjøring traff dem.
### (e) Nevner-vokabularet
**Ikke brukt i noen av de tre armene.** `[sourced-not-sufficient]` og de øvrige markørene står
0 ganger i 17 modellsvar. Det nærmeste er N500s checker: «No other specific cost-saving tradeoffs
were found within the given excerpt for N500» — riktig innhold, feil vokabular.
Det er ærlig nok her: alle tre armene FANT noe å foreslå i kuttet, så ingen av dem var i posisjonen
markøren finnes for. Kontrasten er S7 arm A, som brukte `[sourced-not-sufficient]` ordrett fordi
kuttet der ikke bar svaret.
---
## 5. Hovedfunnet: kuttet leverer riktig dokument og fjerner nøkkelen spørsmålet stiller
Dette er det som avgjør «ferdig»-dommen, og det ble målt fordi (a) falt på to av tre.
Fasit-konseptets fil i basen bærer kravnummeret i frontmatter:
```yaml
title: Krav 3.3.1—13 H1 – Nasjonal hovedveg, ÅDT < 6 000 og fartsgrense 80 km/t
req_number: Krav 3.3.1—13
description: Eventuell kryssing mellom gang- og sykkelveg og veg skal være planskilt ved ÅDT > 4 000.
```
Pre-passets utdrag bærer feltene `bundle_id · concept_id · sha256 · adjudication · trust_tier ·
bundle_id_inherited · text_sha256 · text · rank` — **ingen `title`, ingen `req_number`**.
| kravnummeret i det modellen faktisk fikk | N100 `3.3.1` | N200 `2.9.2` | N500 `10.2` |
|---|---|---|---|
| i fasit-utdragets `text` | **NEI** | ja (kroppen navngir «Tabell 2.9.2—1») | **NEI** |
| noe sted i hele det leverte kuttet | **NEI** | ja | **NEI** |
Til sammenlikning, po sin EGEN `read_file` på samme fil returnerer hele fila — 774 tegn, inkludert
`title` og `req_number`. Pre-passets utdrag er 98 tegn kropp.
**Pre-passet RANGERER på kravnummeret** — det er hele `a37d5ce`s mekanisme, og den virker: rang 1
av 446. **Og så leverer det utdraget uten den nøkkelen.** Modellen får et avsnitt om planfrie
kryssinger uten noe som helst som knytter det til «Krav 3.3.1—13». For to av tre bundler er
spørsmålets identifikator dermed ikke til stede i modellens kontekst i det hele tatt.
Dette er den strukturelle tvillingen til S7c-funnet: **der kjøpte låsene BYTENE, ikke STRUKTUREN;
her kjøper rangeringen RIKTIG DOKUMENT, ikke NØKKELEN.**
---
## 6. «Ferdig»-dommen, ærlig, per bundle
Kriteriet er ordrens: ferdig = (a) ja OG (b) ja OG (c) 0.
| | (a) | (b) modell | (c) | **ferdig** |
|---|---|---|---|---|
| **N100:2023** | **JA** (ordrett) | NEI | **0** | **NEI** |
| **N200:2024** | NEI | NEI | **0** | **NEI** |
| **N500:2024** | NEI | NEI | **0** | **NEI** |
**0 av 3.** N100 er nærmest: den gjenga fasit-vilkåret ordrett og hallusinerte ingenting; den
navnga bare ikke konseptet den siterte.
### Hvem eier hva som mangler
**llm-ingestion-okf eier én ting, og den er skarp.** Utdragsformen i `okf-consumption/1` bærer
`text` uten `title`/`req_number`. Rangeringen er FIKSET og god (rang 1 × 3 med flaggløs kommando) —
det er leveransen som mister nøkkelen. Et spørsmål stilt på en identifikator kan ikke besvares på en
identifikator som ikke ble levert. **Dette er ikke en ny nekt eller et flagg: det er ett felt til
per utdrag.**
**vegnormal-okf eier ingenting her.** Bundlene bærer `title`, `req_number`, `description` og riktig
kropp; indekstreet lukker (0 ufulgte lenker × 3); tre-hashene reproduserer; nevnerne stemmer.
Kroppene er de riktige kroppene. Det er ingen innholdsmangel i noen av de tre.
**portfolio-optimiser eier kjøreformen, og det er den største posten.** A-formens oppgave er
hardkodet til et kostnads-mandat, og den eneste døra som bærer en operatør-prompt (`--explore`) er
nektet med `--prepass-payload`. **po kan ikke STILLE et oppslagsspørsmål i A-form.** Det som ble
målt over er derfor det nærmeste A-formen kommer: fikk modellen fasit-teksten, og brukte den den?
Ja på N100, nei på to — og på begge de to fulgte modellen oppgaven den faktisk fikk.
### Anbefaling til operatøren (beslutningen er din)
1. **Meld feltet til llm-ingestion-okf: la utdraget bære `title` (eller `req_number`).** Billigst,
mest presist, og det eneste som gjør et identifikator-spørsmål besvarbart i det hele tatt.
Kostnaden er noen titalls tegn per utdrag mot dagens 8 utdrag.
2. **Avgjør om po skal ha en oppslags-dør.** I dag finnes den ikke i A-form. Tre veier, ikke
bygget her: (i) la `--prepass-payload` ta et operatør-spørsmål som debattens oppgave;
(ii) løsne `--explore`-nekten under et deklarert kutt (Q5=B-armen `--prepass-seed` er allerede
den formen, med verktøyene BEHOLDT); (iii) erklær at po ikke er et oppslagsverktøy og la
«ferdig» bety noe annet enn V1s K0-spørsmål. **Dette er en beslutning, ikke en fiks.**
3. **Kjør N-bundlene med `--require-cost-baseline` neste gang det er en kostnadskjøring.** N200
validerte et forslag med en oppdiktet kostkode fordi kjøringen var uforankret. F4-flagget finnes
allerede og ville nektet den.
4. **De tre bundlene kan IKKE kalles fullført på dette grunnlaget** — men det som mangler er ikke
bundlene. Etter (1) er kriteriet på nytt målbart for under NOK 0,25.
---
## 7. Funn — rapportert, ikke fikset
1. **Pre-passets utdrag mister kravnummeret** (§ 5). Eier: llm-ingestion-okf. Nevner: 2 av 3
bundler har ikke identifikatoren noe sted i modellens kontekst.
2. **po har ingen oppslags-dør i A-form** (§ 2). Eier: po. `run.py:1243` er hardkodet; ingen av
`run_project`s 26 parametre bærer et spørsmål; `--explore` nektes med `--prepass-payload`.
3. **En uforankret kjøring validerte en oppdiktet kostkode** (§ 4c). Eier: po (bruksmåte, ikke
kode — F4-flagget finnes). `03423b12` finnes 0 av 1 137 filer.
4. **Nevner-vokabularet ble ikke brukt i noen arm** (§ 4e). Eier: uavklart. Ingen av armene var i
posisjonen markøren finnes for, så dette er ikke bevis for at det ikke virker — det er en
ubesvart nevner.
5. **Budsjettforbruket avviker fra V1s tall** på alle tre (§ 1). Forventet konsekvens av `a37d5ce`,
ikke en defekt — nevnt fordi V1s tabell ellers ville lest som stale.
---
## 8. Ærlighets-grenser, uttalt
- **Én kjøring er én kjøring.** Tre armer, ett spørsmål hver, ingen gjentakelse. Ingenting her
måler varians, og et annet trekk fra samme modell kunne gitt et annet utfall på (a).
- **(a) på N100 er ikke bevis for at kjeden svarer på oppslag.** Det er bevis for at fasit-teksten
nådde modellen og ble brukt — og på N100 sammenfaller «svar på oppgaven» og «svar på spørsmålet»
ved et sammentreff i kravets innhold. Sammentreffet er identifisert, ikke skjult.
- **(b) er skåret på modellens referanse, ikke på stempelet**, fordi stempelet ikke kan
diskriminere. Det er en skjerping av ordrens kriterium, ikke en oppmykning, og den er begrunnet.
- **(c) = 0 gjelder PROSAEN.** Det strukturerte forslaget dikter opp kostkoder, som er strukturelt
påkrevd (ingen baseline, og oppgaven krever et tall). Skillet er uttalt i § 4c.
- **`--cost-vocabulary`, `--k` og `--rarity-weight` ble IKKE brukt.** Fasit sto på rang 1 med
default-kommandoen på alle tre, så opt-in-flaggene var ikke nødvendige — ordrens betingelse for
å bruke dem (`below_k`) inntraff aldri.
- **Kuttet er verifisert mot den monterte basen** (`admit_payload` × 3), så payloaden kan ikke ha
levert bytes basen ikke holder. Det er en gate, ikke en tillitserklæring til produsenten.
- **Ingen av de tre bundlene er faglig gjennomgått.** Sammenlikningen i (a) er mot konseptkroppen
slik den står, ikke mot vegnormalen.
---
## 9. Verifiseringslogg
| # | Påstand | Slik den ble verifisert |
|---|---|---|
| 1 | Tre tre-hasher reproduserer | `payload.bundle.ref` mot vegnormal-okfs melding, 3 av 3 ordrett |
| 2 | 446 / 1 133 / 270 konsepter | `okf.navigate_bundle(...).context_files`, po sin egen kode |
| 3 | Fasit på rang 1 × 3 | indeks av fasit-id-en i `payload.excerpts`, default-kommando |
| 4 | Kontraktsjekk exit 0 × 3 | `okf_contract_check.py`, 14 regler, 0 funn |
| 5 | Klientproben grønn | `uv run pytest tests/test_foundry_profile_live.py -q` → 1 passed |
| 6 | Deployment urørt | `az … deployment show` → GlobalStandard, 100, `2025-04-14` |
| 7 | Debattens oppgave er hardkodet | `grep -n 'Find a cost-saving measure' src/` → ett treff, `run.py:1243` |
| 8 | Ingen spørsmåls-parameter | `inspect.signature(run_project)`, 26 navn, 0 treff |
| 9 | Nekten er ekte | argv kjørt → rc 1, meldingen ordrett, null modellkall |
| 10 | Spørsmålet nådde modellen | ordrett delstreng i 2 av 6 / 2 av 5 / 2 av 6 prompter |
| 11 | (a) N100 ja | fasit-setningen ORDRETT i svaret; `planskilt`/`ÅDT`/`4 000` alle til stede |
| 12 | (a) N200/N500 nei | 0 av 6 nøkkelord fra hver fasit-kropp i noe svar |
| 13 | (c) = 0 | tall i prosa (UUID-fragmenter fjernet) mot delivered-teksten; 2 treff undersøkt og forkastet som instrumentfeil |
| 14 | `03423b12` er oppdiktet | 0 treff blant basens 1 137 filer |
| 15 | Utdraget mangler `title` | feltlista i `payload.excerpts[0]`; kravnummeret ikke i `text` for N100/N500 |
| 16 | po sin `read_file` bærer det | `navigator_tools(...)` → 774 tegn inkl. `req_number` |
| 17 | Null parse-feil | `{run_id}-parse-failures.json` fraværende i alle tre utboksene |
| 18 | Steg 5 fyrer levende | «REJECTED by the deterministic validator» i forsøk 2 og 3, begge armer |
| 19 | Prisene | Azure Retail Prices API hentet på nytt 08.09 |
| 20 | 0 × 429 | `retries`-telleren i alle 17 poster |

View file

@ -30,7 +30,7 @@ trengte.
| (ii) To utrackede: presentasjons-HTML (parallell økt) og `scratchpad/` | `git status --short` | **BEKREFTET.** Begge forblir utrackede | | (ii) To utrackede: presentasjons-HTML (parallell økt) og `scratchpad/` | `git status --short` | **BEKREFTET.** Begge forblir utrackede |
| (iii) Armen er S7c `Aopen`, og bare den | S7c § 5 lest; `Adef` ikke kjørt | **BEKREFTET** | | (iii) Armen er S7c `Aopen`, og bare den | S7c § 5 lest; `Adef` ikke kjørt | **BEKREFTET** |
| (iv) `payload-open.json` = 240 021 B, sha256 `f802809e…b995d3`, 12 utdrag, prisskjemaet på rang 10 med 67 245 tegn | `ls -l`, `shasum -a 256`, parsing av fila | **BEKREFTET, alle fem tallene** | | (iv) `payload-open.json` = 240 021 B, sha256 `f802809e…b995d3`, 12 utdrag, prisskjemaet på rang 10 med 67 245 tegn | `ls -l`, `shasum -a 256`, parsing av fila | **BEKREFTET, alle fem tallene** |
| (v) Payloaden gjenbrukes BYTE-IDENTISK; den har 9 medlemmer per utdrag | `sorted(excerpts[0].keys())` | **BEKREFTET:** `adjudication`, `bundle_id`, `bundle_id_inherited`, `concept_id`, `rank`, `sha256`, `text`, `text_sha256`, `trust_tier` — **9**, altså eldre enn P3/okf-feltfiksen (14 på N-bundlene). Det er ønsket her: A/B-en isolerer kollapsen alene | | (v) Payloaden gjenbrukes BYTE-IDENTISK; den har 9 medlemmer per utdrag | `sorted(excerpts[0].keys())` | **BEKREFTET:** `adjudication`, `bundle_id`, `bundle_id_inherited`, `concept_id`, `rank`, `sha256`, `text`, `text_sha256`, `trust_tier` — **9**, altså eldre enn P3/okf-feltfiksen (14 på kravbasene). Det er ønsket her: A/B-en isolerer kollapsen alene |
| (vi) Kollapsen gir 67 245 → 18 531 tegn (−72,4 %), 104 linjer, lengste løp 887 → 0 | pre-flight § 2 | **BEKREFTET, alle fire** | | (vi) Kollapsen gir 67 245 → 18 531 tegn (−72,4 %), 104 linjer, lengste løp 887 → 0 | pre-flight § 2 | **BEKREFTET, alle fire** |
| (vii) Azure uendret; klientproben krever BEGGE env-vars | `uv run pytest tests/test_foundry_profile_live.py -q` med begge satt | **BEKREFTET: `1 passed`** (ikke skipped) | | (vii) Azure uendret; klientproben krever BEGGE env-vars | `uv run pytest tests/test_foundry_profile_live.py -q` med begge satt | **BEKREFTET: `1 passed`** (ikke skipped) |
@ -218,7 +218,7 @@ LESNINGEN (funn 5) til **VALGET** — hvilket dokument modellen bestemmer seg fo
ingen varians målt. At samme argv kjørt om igjen ville gitt samme null, er ikke vist — og P6 vs ingen varians målt. At samme argv kjørt om igjen ville gitt samme null, er ikke vist — og P6 vs
S7c viser nettopp at BANEN varierer (5 vs 11 prompter, `ValidatedProposal` vs `Rejection`) selv S7c viser nettopp at BANEN varierer (5 vs 11 prompter, `ValidatedProposal` vs `Rejection`) selv
når svaret på (a) ikke gjør det. når svaret på (a) ikke gjør det.
* **Payloaden har 9 medlemmer per utdrag** og er eldre enn P3/okf-feltfiksen (14 på N-bundlene). * **Payloaden har 9 medlemmer per utdrag** og er eldre enn P3/okf-feltfiksen (14 på kravbasene).
Det er bevisst — den skal være byte-identisk med S7cs — men det betyr at `title` / `req_number` / Det er bevisst — den skal være byte-identisk med S7cs — men det betyr at `title` / `req_number` /
`sources` IKKE nådde prompten her. Om de nye feltene ville flyttet modellens VALG er ikke målt. `sources` IKKE nådde prompten her. Om de nye feltene ville flyttet modellens VALG er ikke målt.
* **At en annen modell ville lest kollapsen annerledes er ikke målt.** Særlig: en større modell kan * **At en annen modell ville lest kollapsen annerledes er ikke målt.** Særlig: en større modell kan

View file

@ -34,7 +34,7 @@ i det hele tatt.
| (iv) `_reconcile_against_baseline` bak `if baseline is not None` | Kjørt PMs egen kontroll på P6-forslaget | **BEKREFTET ORDRETT.** `baseline=None` → `ValidatedProposal`; ikke-tom baseline → `Rejection: unknown cost code 'M-04-01' … 'M-04-03'` | | (iv) `_reconcile_against_baseline` bak `if baseline is not None` | Kjørt PMs egen kontroll på P6-forslaget | **BEKREFTET ORDRETT.** `baseline=None` → `ValidatedProposal`; ikke-tom baseline → `Rejection: unknown cost code 'M-04-01' … 'M-04-03'` |
| (v) P6 `validated`/`5fd6272e3725fe68`/`approve`; S7c `rejected` på MAGNITUDE | Lest begge `*-outcome.json` + begge `*-proposal.json` | **BEKREFTET.** S7cs grunn er «claimed saving 1400000 exceeds P90 feasible 879107» — stage 4, ikke fabrikasjon | | (v) P6 `validated`/`5fd6272e3725fe68`/`approve`; S7c `rejected` på MAGNITUDE | Lest begge `*-outcome.json` + begge `*-proposal.json` | **BEKREFTET.** S7cs grunn er «claimed saving 1400000 exceeds P90 feasible 879107» — stage 4, ikke fabrikasjon |
| (vi) ugrunnede identifikatorer per opptak med PMs grove mønster | Re-målt, se § 3 | **BEKREFTET** (2/2 · 4/4 · 1/2), og mønsteret er erstattet — se § 2 | | (vi) ugrunnede identifikatorer per opptak med PMs grove mønster | Re-målt, se § 3 | **BEKREFTET** (2/2 · 4/4 · 1/2), og mønsteret er erstattet — se § 2 |
| (vii) `Krav 3.3.1—13` i 6/6 prompter OG 6/6 svar, EM-DASH | Re-målt på `p4-n100-free-records.json` | **BEKREFTET.** Bindestrek-varianten: 0 i begge | | (vii) `Krav 3.3.1—13` i 6/6 prompter OG 6/6 svar, EM-DASH | Re-målt på P4-opptaket (kravbase A) | **BEKREFTET.** Bindestrek-varianten: 0 i begge |
| (viii) sømmen er `generate.py` (`_fetch_parsed` → `validate_proposal`); fem kallsteder | Lest alle: `validator.py:292` (`self_repair`), `run.py:450` (mandat, ingen modell), `explore.py:1266` (`quick_validate`, rådgivende), `generate.py` | **BEKREFTET.** Regelen wires i `generate.py`; de tre andre er uendret, med grunn i § 6 | | (viii) sømmen er `generate.py` (`_fetch_parsed` → `validate_proposal`); fem kallsteder | Lest alle: `validator.py:292` (`self_repair`), `run.py:450` (mandat, ingen modell), `explore.py:1266` (`quick_validate`, rådgivende), `generate.py` | **BEKREFTET.** Regelen wires i `generate.py`; de tre andre er uendret, med grunn i § 6 |
--- ---
@ -42,10 +42,11 @@ i det hele tatt.
## 2. Mønsteret, målt fra korpuset — og hvorfor regelen ikke har noe mønster ## 2. Mønsteret, målt fra korpuset — og hvorfor regelen ikke har noe mønster
Målt over det som faktisk ble levert: K2-basen (`scratchpad/s7c/k2-bundle-s7c/`, **1 108 `.md`, Målt over det som faktisk ble levert: K2-basen (`scratchpad/s7c/k2-bundle-s7c/`, **1 108 `.md`,
2 005 561 tegn**) og de tre N-payloadene (`scratchpad/nbundler-p2/payload-n{100,200,500}.json`, 2 005 561 tegn**) og de tre kravbase-payloadene (kravbase A/B/C fra et kravkorpus brukt under utviklingen, lokale
scratchpad-filer,
**8 leverte utdrag hver**). **8 leverte utdrag hver**).
| Form | K2 (treff / unike) | N100 | N200 | N500 | Tas inn i regelen? | | Form | K2 (treff / unike) | Kravbase A | Kravbase B | Kravbase C | Tas inn i regelen? |
|---|---|---|---|---|---| |---|---|---|---|---|---|
| `Krav X.Y.Z—N` (em-dash) i KROPPEN | 0 / 0 | 0 | 0 | 0 | — se raden under | | `Krav X.Y.Z—N` (em-dash) i KROPPEN | 0 / 0 | 0 | 0 | 0 | — se raden under |
| `Krav X.Y.Z—N` i `req_number` / `title` | — | 8/8 | 8/8 | 8/8 | **JA** (den bor i frontmatter, ikke i kroppen — derfor leser grunnlaget også `frontmatter`) | | `Krav X.Y.Z—N` i `req_number` / `title` | — | 8/8 | 8/8 | 8/8 | **JA** (den bor i frontmatter, ikke i kroppen — derfor leser grunnlaget også `frontmatter`) |
@ -78,7 +79,7 @@ grense i § 6, ikke som en påstand om dekning.
|---|---|---| |---|---|---|
| P6 `scratchpad/s7c/p6-Aopen-records.json` (5 records) | **2 av 2 ugrunnet** — `M-04-01`, `M-04-03` | **4 av 4** — `MER-001`, `MER-002`, `M-04-01`, `M-04-03` | | P6 `scratchpad/s7c/p6-Aopen-records.json` (5 records) | **2 av 2 ugrunnet** — `M-04-01`, `M-04-03` | **4 av 4** — `MER-001`, `MER-002`, `M-04-01`, `M-04-03` |
| S7c `scratchpad/s7c/Aopen-records.json` (11 records) | **2 av 2** — `PRD-001`, `PRD-002` | **4 av 4** — `MEETINGS-05`, `LOGGING-02`, `PRD-001`, `PRD-002` | | S7c `scratchpad/s7c/Aopen-records.json` (11 records) | **2 av 2** — `PRD-001`, `PRD-002` | **4 av 4** — `MEETINGS-05`, `LOGGING-02`, `PRD-001`, `PRD-002` |
| P4 `scratchpad/nbundler-p2/p4-n100-free-records.json` (6 records) | **nevner 0** — opptaket har intet lagret forslag (fri kjøring) | **1 av 2** — `CRS-01` ugrunnet, **`Krav 3.3.1—13` GRUNNET** (6/6 prompter) | | P4, fri kjøring på kravbase A (6 records) | **nevner 0** — opptaket har intet lagret forslag (fri kjøring) | **1 av 2** — `CRS-01` ugrunnet, **`Krav 3.3.1—13` GRUNNET** (6/6 prompter) |
**VALGT: variant A. Prosa-skanningen er IKKE bygget.** Begrunnelsen er tallene over, ikke smak: **VALGT: variant A. Prosa-skanningen er IKKE bygget.** Begrunnelsen er tallene over, ikke smak:
@ -156,7 +157,7 @@ som korrekt skal passere pre-P7) før én linje av regelen fantes.
`tests/fixtures/p7-grounding/` bærer de tre genererings-promptene ORDRETT (1 788 / 1 397 / 985 `tests/fixtures/p7-grounding/` bærer de tre genererings-promptene ORDRETT (1 788 / 1 397 / 985
tegn) — hele inputen proposeren så på forsøket som produserte kandidaten. Ingen test skipper. tegn) — hele inputen proposeren så på forsøket som produserte kandidaten. Ingen test skipper.
**Kjent-positiven, bindende:** `Krav 3.3.1—13` (EM-DASH, U+2014) står i N100-genererings-prompten og **Kjent-positiven, bindende:** `Krav 3.3.1—13` (EM-DASH, U+2014) står i genererings-prompten fra kravbase A og
flagges IKKE. **KONTROLLEN på samme opptak:** `CRS-01` — det ene identifikator-formede tokenet den flagges IKKE. **KONTROLLEN på samme opptak:** `CRS-01` — det ene identifikator-formede tokenet den
kjøringen produserte som ingen prompt bærer — FELLES. Uten den kontrollen ville en grønn kjøringen produserte som ingen prompt bærer — FELLES. Uten den kontrollen ville en grønn
kjent-positiv ikke bevist noe. kjent-positiv ikke bevist noe.
@ -201,7 +202,7 @@ Grønn kontroll **1558 passed / 5 skipped**, golden BYTE-UENDRET, restaurert fra
* **Den hostede flaten er urørt** — `grounding` er ikke i noen av hostings tre sett, så den * **Den hostede flaten er urørt** — `grounding` er ikke i noen av hostings tre sett, så den
generiske 400-en svarer og Fase 4es to halvdeler står (MAJOR-4/S7bs eget valg gjentatt). generiske 400-en svarer og Fase 4es to halvdeler står (MAJOR-4/S7bs eget valg gjentatt).
* **`--require-cost-baseline` er IKKE gjort til default** (F4 valgte den opt-in, D-3 låste bruken på * **`--require-cost-baseline` er IKKE gjort til default** (F4 valgte den opt-in, D-3 låste bruken på
N-kjøringer). P7 gjør den mindre nødvendig, ikke overflødig. kravbase-kjøringer). P7 gjør den mindre nødvendig, ikke overflødig.
* **Tre eksisterende fixturer ble endret, ingen gate svekket:** `test_structured_output_loadbearing` * **Tre eksisterende fixturer ble endret, ingen gate svekket:** `test_structured_output_loadbearing`
gir sin syntetiske kontekst linja forslaget gjenforteller; `test_dimension_loadbearing`s gir sin syntetiske kontekst linja forslaget gjenforteller; `test_dimension_loadbearing`s
fremmed-dimensjon-arm bytter `SENTINEL-FOREIGN` mot en kode basen NAVNER (armen ville ellers ridd fremmed-dimensjon-arm bytter `SENTINEL-FOREIGN` mot en kode basen NAVNER (armen ville ellers ridd

View file

@ -1,227 +0,0 @@
# P8 — den leverte inputen bærer ingen kostlinjer, så ingen forankret proposal kan oppstå
Ordre `20260909T142951Z-365478643-from-.claude`, økt 110. HEAD ved start: `277bb95`.
Alt i dette dokumentet er målt **gratis** — null modellkall, **NOK 0,00**.
## § 0 Hva som ER målt og hva som IKKE er det
**Målt:** hva de tre gratis opptakene faktisk leverte og hvor mange av kodene P7-gaten feller;
hva den LEVERTE inputen i hvert korpus po har liggende kan tilby som lovlig `affected_item`-kode;
og at rapporten som nå bygges sier null der tilbudet er null og positivt der det er positivt.
**Ikke målt:** at rapporten endrer noe LEVENDE. Ingen betalt kjøring er gjort, og ingen er
bestilt. At en modell som får se linja oppfører seg annerledes er ikke vist — samme klasse som
structured-output-grensen. Rapporten er dessuten skrevet på grunnlag av tre opptak fra **ÉN**
modell og **ETT** deployment.
**Ikke bygget, med vilje:** prosa-skanningen (operatørbeslutning A) og
`--require-cost-baseline` som default (operatørbeslutning, F4/D-3). Begge står uendret.
## § 1 Premissene (i)–(viii) — hver verifisert selv
| # | Premiss (PM) | Mitt utfall |
|---|---|---|
| (i) | HEAD `277bb95`, `git ls-remote origin main` = `eb41374`, upushet 2 | **BEKREFTET** ordrett |
| (ii) | To utrackede: presentasjons-HTML (parallell økt) + `scratchpad/` | **BEKREFTET**, intet tredje |
| (iii) | 1558 passed / 5 skipped, ruff rent, mypy rent (37 filer), golden `ea8c534…` | **BEKREFTET** alle fire |
| (iv) | 13 forslag / 29 koder / 29 ugrunnet | **BEKREFTET på PMs nevner, med et avvik — se § 2** |
| (v) | K2 leverer 9/2 distinkte kodeformede (`SHA-01`/`SHA-10`, dokumentnumre); N100 0 kodeformede, 17/8 kravnumre | **BEKREFTET ordrett** |
| (vi) | Ved uttømte forsøk returneres siste `Rejection`, ingen krasj | **BEKREFTET**: `generate.py` returnerer `GenerationResult(outcome=last_ruling, …)` etter forsøksløkka |
| (vii) | `--require-cost-baseline` er opt-in og skal ikke bli default | **RESPEKTERT**, urørt |
| (viii) | (A) og (B) er operatørens | **RESPEKTERT**, ikke avgjort her |
## § 2 Konsekvensen — og AVVIKET mot PMs tall
PMs tall er reprodusert nøyaktig **på PMs nevner**, men den nevneren teller blobs som aldri
NÅR P7-gaten. Begge tall er sanne om hver sin ting, og begge gir 100 %:
| Opptak | Rå JSON-blobs | Koder | Ugrunnet | Parsebare til IR | Koder | Ugrunnet |
|---|---|---|---|---|---|---|
| `p6-Aopen-records.json` | 2 | 4 | **4** | 2 | 4 | **4** |
| `Aopen-records.json` | 8 | 22 | **22** | 3 | 7 | **7** |
| `p4-n100-free-records.json` | 3 | 3 | **3** | 3 | 3 | **3** |
| **Totalt** | **13** | **29** | **29 (100 %)** | **8** | **14** | **14 (100 %)** |
De fem blobene som skiller tallene avvises av en **annen og TIDLIGERE falsifiserer** —
pydantics `claimed_saving_nok <= affected items' total` — og når derfor aldri stadium 0b.
Det er ikke en svakhet i PMs måling; det er to nevnere om to ulike hendelser. Grunnlaget er
det **mest sjenerøse** som finnes: unionen av ALLE prompter i opptaket. Selv der er ingen kode
grunnet.
Et instrument-forbehold, skrevet ned fordi det først ga feil svar: en rå
`SavingsProposal.model_validate_json` avviser **alle 13**, fordi svarene bærer WIRE-formen av
`assumptions` (et array) som `_normalise_assumptions` folder tilbake. En måling som stoppet der
ville rapportert 0 forslag — null fordi instrumentet ikke kunne lese formen, ikke fordi formen
manglet.
## § 3 Årsaken — prompten ber om noe inputen ikke kan levere
`generate._build_messages` sier ordrett: «Each entry in `affected_items` must restate a cost line
as the project's price schedule already carries it». Målt på record 0 (turen som bar bundelen):
* **K2** (P6: 96 567 tegn; S7c: 145 281 tegn) → **9 forekomster / 2 distinkte** kodeformede, og
begge er `SHA-01` / `SHA-10`. Konteksten er entydig: `Oppdragsnr.: 52308329 Dokumentnr.: SHA-01
Versjon: 01` — en sidefot i en SHA-plan — og `Dokumentnavn: SHA-10 - … Restrisikorapport`.
**Dokumentnumre, ikke kostlinjer.**
* **N100** (10 569 tegn) → **0** kodeformede, men **17 forekomster / 8 distinkte** kravnumre på
`Krav X.Y.Z—N`-form (em-dash U+2014).
Modellen ble bedt om å gjengi en kostlinje som ikke finnes, og fabrikkerte den. Dette rimer med
funn 4 (økt 107): K2s prisskjema bærer ingen mengdefortegnelse, 0 av 92 rader navngir
kode/mengde/enhetspris.
## § 4 Tilbudet per korpus — målt med den SHIPPEDE funksjonen
Teksten er komponert nøyaktig som `run_project` komponerer P7s grunnlag
(`"\n".join([context, bundle_grounding])`, deretter gjennom `generate._grounding_text`).
«Kostlinje» er po sin EGEN definisjon (`okf.derive_cost_baseline`, MAJOR-4) — ikke en ny
heuristikk oppfunnet her.
| Korpus | Konsepter | Grunnlag (tegn) | Distinkte identifikatorer | Kostlinjer | `derive_cost_baseline` |
|---|---|---|---|---|---|
| K2 s7c + `payload-open` | 630 | 1 991 597 | 50 | **0** | REFUSED |
| K2 s7c + `payload-default` | 630 | 1 970 415 | 50 | **0** | REFUSED |
| K2 s7a2 (peker-armen, P6) | 629 | 1 894 500 | 50 | **0** | REFUSED |
| N100 `n100-2023` | 446 | 462 041 | 435 | **0** | REFUSED |
| N200 `n200-2024` | 1 133 | 1 500 962 | 982 | **0** | REFUSED |
| N500 `n500-2024` | 270 | 408 220 | 272 | **0** | REFUSED |
| `shared/veglys-fv-soer` | 5 | 30 038 | 3 | **1** | REFUSED (men fila finnes) |
| `shared/tunnel-hauglia` | 5 | 36 546 | 2 | **1** | REFUSED (men fila finnes) |
| `shared/bygg-energi-mikro` | 4 | 11 019 | 2 | **0** | REFUSED |
**Klassene, målt.** K2s 50 distinkte kodeformede er tegningsnumre (`B-20-00-00`, `F-20-00-01`,
`V-73-20-01-01`), dokumentnumre (`SHA-01`, `RIM-02`, `RIA-01`, `NOT-01`), stoff- og
standardreferanser (`PCB-7`, `PAH-16`, `DALI-2`) og forskriftsnumre
(`FOR-2011-12-06-1357`). **Ingen av dem er en kostkode.** N-korpusene bærer kravnumre og UUID-er
(N200: 2 290 distinkte UUID-er), ingen kostkoder.
**Er et forankret forslag i det hele tatt MULIG?** For **K2: NEI**, og tallet er 0 —
`derive_cost_baseline` nekter basen, og ingen av de 50 identifikatorene er en kostlinje. Bare
**2 av 50** når i det hele tatt prompten. For **N-korpusene: NEI** på kostlinje — en vegnormal
bærer ingen — men **JA** på identifikator: 435 / 982 / 272 distinkte kravnumre er sitérbare, og
8 av dem sto i N100-prompten.
**Rene tall er farligst og bæres videre uendret:** 46 394 forekomster / 2 117 distinkte i K2
(P7 § 2). Regelen er inert mot dem og feiler ÅPENT. Ikke bygget om på.
**Gammel default.** Alt over er målt på `K2-bundle-20260903`. okf melder (innboks
`20260909T134141Z`, `reply-expected: no`, lukket med `coord-done`) at deres default-bygg sluttet å
emittere den 2026-09-08, at gjeldende default er 436 konsepter / 832 filer, og at de leser
formfunnet som fortsatt stående fordi endringen treffer FORMEN, ikke innholdet. Ingen ny bundle er
bygget eller konsumert her — deres endring er ikke pushet, og en re-måling på den nye defaulten er
en senere, separat ordre.
## § 5 Hva som er bygget — en RAPPORT, ikke en gate
`generate.GroundingOffer(chars, identifiers, cost_lines)` + `generate.grounding_offer(...)`,
kalt fra `run.py`, rendret av `run.grounding_offer_notice`.
**Valget av kallsted, med grunnen (ordrens eget krav).** Begge kandidater ble lest først.
`generate.py` komponerer grunnlaget **per forsøk, ETTER `await _fetch_parsed(messages)`** — en
rapport derfra kan først tale når ett forsøk allerede er betalt, altså nøyaktig det ordren ber
den om å komme foran. `run.py` binder begge halvdeler ved `run.py:1228`, **over
`--live-dry-run`-kuttet og før første `debate.run`**. Valgt: **`run.py`**. Tellefunksjonen bor
likevel i `generate.py`, ved siden av `_grounding_text` den måler — å skille dem ville gitt to
steder å bli uenige på.
**De fire kravene:**
1. **BLOKKERER ikke.** En kjøring med null tilbud kjører som før. Et blokkerende krav ER
`--require-cost-baseline`, som premiss (vii) fredet.
2. **Måler den EKSAKTE teksten.** `grounding_offer` komponerer GJENNOM `_grounding_text` —
samme funksjon P7s gate bruker — og `run.py` binder `delivered` **én gang** og gir samme
variabel til både rapporten og `_evaluate`. To komposisjoner av én tekst er fri til å være
uenige (kø-(p)); her er de identiske ved konstruksjon.
3. **Når utfallet operatøren leser.** `RunResult.grounding_offer`, `DryRunReport.grounding_offer`
og én linje på stdout i begge CLI-armene.
4. **Gjenbruker P7s sømmer.** Ingen ny domstype ved siden av `Rejection`/`ValidatedProposal`.
`_ground_against_input` er **URØRT**.
**Identifikator-formene er TRANSKRIBERT fra målingen i § 4**, ikke valgt: `[A-ZÆØÅ]{1,8}[-_]\d…`
(K2s 50) og `Krav X.Y.Z—N` (N-korpusenes dominerende form). At et mønster er tillatt HER og ikke i
`_ground_against_input` er selve skillet mellom en rapport og en gate: en form rapporten ikke
kjenner er et token den unnlater å telle, altså en **under-telling** — aldri en falsk avvisning.
**Rene tall er BEVISST utelatt, med tallet** (46 394 / 2 117): å telle dem ville gjort hver rapport
positiv og målingen inert — repoets kardinalklasse, en gate som bare kan bli grønn.
Rendereren er **ÉN**, og den tier når kjøringen KAN forankre en kostlinje — omisjon, aldri en tom
rad (`cost_baseline_notice`s regel). Den bærer **begge tall**, fordi paret er diagnosen:
«0 kostlinjer» alene leses som en gjentakelse av `cost_baseline_notice`, «50 identifikatorer» alene
leses som gode nyheter.
## § 6 Kjent-positiv og kontroll
**Kjent-positiven (bindende):** N100s input bærer `Krav 3.3.1—13` (em-dash U+2014; P7 premiss
(vii): 6 av 6 prompter). Rapporten sier **positivt tilbud** der, og armen er paret med en kontroll
på at strengen faktisk STÅR i fixturen — en rapport som fant null fordi den lette etter ingenting
ville ellers bestått.
**Kontrollen på samme materiale:** K2s to genererings-prompter, der tilbudet er **null i begge
tall**. Uten den beviser en grønn kjent-positiv ingenting.
**Fixturvalget, uttalt.** Testene leser P7s egne SPORede fixturer under
`tests/fixtures/p7-grounding/`. De leverte KUTTENE (10 kB N100, 96 kB K2) er ikke sporet: å
committe verbatim anbudstekst og standardtekst inn i et repo som publiseres på `open/` er en
publiseringsbeslutning som tilhører operatøren, ikke denne ordren. 8-/435-distinkt-tallene står
derfor i § 4 som MÅLINGER; det testene asserterer er EGENSKAPEN, på tekst repoet allerede
shipper.
## § 7 Mutasjonstabellen
Grønn kontroll **1570 passed / 5 skipped** (fra 1558/5; **+12 node-ider, 0 fjernet**, målt med
`comm` mot en liste bygget fra HEAD). Golden `demo-transcript.stdout` BYTE-UENDRET,
`shasum -a 1` av INNHOLDET = `ea8c534773acdbe41ae68f2c55724d69aaf8be4f` (ALDRI git-blob-id-en).
Hver mutasjon kjørt mot HELE suiten, én per kall, restaurert fra `scratchpad/p8/mut-backup/`
med `shasum -c`, aldri `git checkout`.
| # | Mutasjon | Røde |
|---|---|---|
| M1 | rapporten teller alltid null | **2** (kjent-positiven blant dem) |
| M2 | rapporten teller alltid positivt | **7** (K2-kontrollen først) |
| M3 | rapporten bygges fra `delivered` alene, ikke gjennom `_grounding_text` | **1** (krav (2)-armen alene) |
| M4 | rapporten når aldri `RunResult` | **3** |
| M5 | rapporten når aldri `DryRunReport` | **3** |
| M6 | rendereren skriver alltid linja | **2** (omisjonen er selv gatet, på begge flater) |
| M7 | detach dry-run-utskriften | **1** |
| M8 | detach fullkjørings-utskriften | **1** |
| M9 | rene tall telles som tilbud | **2** |
Ni mutasjoner, **alle røde**. Ingen grønn mutasjon, altså ingen uvitnet søm i denne leveransen.
## § 8 Honesty limits
* **Alt er målt OFFLINE** på tre opptak fra **ÉN modell** og **ETT deployment**. Ingen betalt
kjøring bekrefter at rapporten endrer noe levende, og at en modell som ser linja velger
annerledes er **ikke vist** (structured-output-grensens klasse).
* **Rapporten BLOKKERER ikke.** En kjøring med null tilbud kan fortsatt brenne tre forsøk på et
forslag som ikke kan bli forankret. Det er **VALGT**, ikke oversett: et blokkerende krav er
`--require-cost-baseline`, og F4/D-3 la den beslutningen hos operatøren.
* **Identifikator-formene er et mønster**, og et mønster kan mangle en form. Retningen er
under-telling, aldri falsk avvisning — men et korpus med en tredje form vil rapportere lavere
enn det tilbyr, til noen måler den formen.
* **`cost_lines` er `len(baseline.items)`**, lest av SAMME `baseline`-binding `_grounding_text`
tar som sin tredje kilde. Den gjentar altså forankringen som et ANTALL. Den står her fordi
paret er diagnosen, ikke fordi antallet er en ny kjensgjerning.
* **Portefølje-armen er BEVISST ikke wiret.** `run_portfolio` skriver ingen slik linje;
`bundle_id_notice`s avgjørelse, ikke `cost_baseline_notice`s. Asymmetrien står her fordi
stillhet om den er det eneste gale svaret.
* **Den hostede flaten er urørt.** Feltet er i ingen av hostings tre sett, så Fase 4es to
halvdeler står uendret.
* **Tilbudsmålingen er gjort på okf sin GAMLE default-bundle** (`K2-bundle-20260903`). En
re-måling på den nye (436 konsepter / 832 filer) er en senere, separat ordre.
* **(A) prosa-skanningen og (B) blindsone-valget forblir OPERATØRENS.** Målingen her informerer
(B) — den handler om **TILBUDET** i inputen, ikke om hvilket utdrag modellen **VELGER** — men
den avgjør den ikke.
## § 9 Reproduksjon
uv run pytest -q tests/test_grounding_offer_loadbearing.py
uv run python -m portfolio_optimiser.run BYGG-KONTOR-NORD \
--docs-dir shared/examples/bygg-energi-mikro \
--bundle-dir shared/examples/bygg-energi-mikro --live-dry-run
uv run python -m portfolio_optimiser.run VEGLYS-FV-SOER \
--docs-dir shared/examples/veglys-fv-soer \
--bundle-dir shared/examples/veglys-fv-soer --live-dry-run
Den første basen har ingen kostbaseline og skriver tilbudslinja; den andre har én og tier.
Måleskriptene ligger i `scratchpad/p8/` (utracket).

View file

@ -162,8 +162,8 @@ i en kopi av `38104b7` gir den gamle payloaden byte-identisk tilbake. Default-ar
(72 535 → 47 679) skjedde i to trinn: `38104b7` (→ 63 644) og deretter tie-effekten ved `a364ef4` (72 535 → 47 679) skjedde i to trinn: `38104b7` (→ 63 644) og deretter tie-effekten ved `a364ef4`
(→ 47 679). `known_positive` 10 349 → 12 563 er en versjonsmarkør: tallet flyttet seg ved `17c49fc` (→ 47 679). `known_positive` 10 349 → 12 563 er en versjonsmarkør: tallet flyttet seg ved `17c49fc`
og `c95d189` mens den leverte lista sto. På okf 0.8.1 er prisskjemaet ikke lenger `below_k`, men og `c95d189` mens den leverte lista sto. På okf 0.8.1 er prisskjemaet ikke lenger `below_k`, men
`over_budget_after_knapsack`. Målt med `scratchpad/p11/bisect/` og `scratchpad/p11/exponent/`; tall `over_budget_after_knapsack`. Målt med `scratchpad/p11/bisect/` og `scratchpad/p11/exponent/`; P11-rapporten
og kommandoer står i `docs/2026-09-11-p11-okf-081.md` § 4 og § 9. med tall og kommandoer er fjernet, invarianten står i `docs/invarianter.md`.
Dette er hele grunnen til at en re-spilt payload er en NY måling og ikke en reparert gammel. Dette er hele grunnen til at en re-spilt payload er en NY måling og ikke en reparert gammel.
@ -215,7 +215,7 @@ uten et endret tall er støy.
Prisskjemaet ble rangert ut av okf `38104b7`, som endret dokument-prioren Prisskjemaet ble rangert ut av okf `38104b7`, som endret dokument-prioren
(`DOCUMENT_PRIOR_EXPONENT` 1.0 → 0.5). Når bare den konstanten settes tilbake i en kopi av (`DOCUMENT_PRIOR_EXPONENT` 1.0 → 0.5). Når bare den konstanten settes tilbake i en kopi av
commiten, kommer den gamle payloaden tilbake byte-identisk. Se § 4 over og commiten, kommer den gamle payloaden tilbake byte-identisk. Se § 4 over og
`docs/2026-09-11-p11-okf-081.md` § 4. `docs/invarianter.md`.
* **(A), (B) og (C) forblir operatørens** og ble ikke flyttet av denne økta. * **(A), (B) og (C) forblir operatørens** og ble ikke flyttet av denne økta.
* PATH-okf 0.7.0 er ikke bevist å være samme commit som PMs frosne `958e9bc`; versjonsstrengen er * PATH-okf 0.7.0 er ikke bevist å være samme commit som PMs frosne `958e9bc`; versjonsstrengen er
det eneste som er målt, og de to sprikte i flaggliste. det eneste som er målt, og de to sprikte i flaggliste.

View file

@ -1,227 +0,0 @@
# P9 — po på okf v0.7.0-bundler: omdøpingen koster ingenting, innholdet gir 15 identifikatorer
Ordre `20260910T040954Z-7380215721-from-.claude`, økt 111. HEAD ved start: `455d611`.
Alt i dette dokumentet er målt **gratis** — null modellkall, **NOK 0,00**. Ingen push.
## § 0 Hva som ER målt og hva som IKKE er det
**Målt:** at `ruff format`-driften P7/P8 etterlot er tre rene linjeombrekk uten
oppførselsendring; at P8s tilbudsmåling reproduserer **eksakt** på de tre N-bundlene etter V3s
rebygg; at K2 på okf `v0.7.0` gir **65** distinkte identifikatorer mot P8s **50**, at ingen av de
50 er tapt, og at differansen bæres av **innhold** og ikke av konsept-omdøpingen — med tallet
`0 av 50` og `0 av 65` fra konseptnavn i hver sin bundle; at `okf check` kan feile (kjent-negativ,
9 funn); og at `ark1.md` ligger i en annen katalog enn FYI-meldingen oppga.
**Ikke målt:** at noe av dette endrer noe LEVENDE. Ingen betalt kjøring er gjort og ingen er
bestilt. Hele § 4 er en statisk lesing av tekst på disk.
**Ikke bygget, med vilje:** ingen ny søm, ingen ny funksjon, ingen kontraktsendring mot okf.
`_ground_against_input` er URØRT. Prosa-skanningen (A), blindsone-VALGET (B) og sporing av
leverte kutt som fixturer (C) står uendret som operatørens.
## § 1 Premissene — hver verifisert selv
| # | Premiss (PM målte 09.09 ~23:3x) | Mitt utfall |
|---|---|---|
| (i) | HEAD `455d611`; `git ls-remote origin main` = `455d611…`, upushet = 0 | **BEKREFTET.** `455d6116606af9adb665d2f6c016f02c4c1263a0` på begge. STATEs «UPUSHET = 3» er stale og rettes i denne økta |
| (ii) | Nøyaktig to utrackede: `docs/presentasjon-portfolio-optimiser.html`, `scratchpad/` | **BEKREFTET.** Intet tredje. HTML-en er ikke lest, ikke rørt, ikke staget |
| (iii) | Suite 1570/5 · `ruff check` rent · `mypy` rent · golden `ea8c534…` · STATE 120 linjer | **BEKREFTET**, alle fem |
| (iv) | `ruff format --check` rød på nøyaktig tre filer, ruff 0.15.18 | **BEKREFTET.** `3 files would be reformatted, 195 files already formatted` |
| (v) | `okf` = 0.7.0; `okf check` tar `--skill/--payload`, aldri en bundle-sti | **BEKREFTET.** `usage: okf [-h] --skill SKILL --payload PAYLOAD` |
| (vi) | K2-pinnen 865 `.md` / 453 konsepter, null `*sheet-*` | **BEKREFTET.** 865 `.md`, `find … -name '*sheet-*'` = 0 treff, `navigate_bundle` gir 453 konsepter |
| (vii) | N-katalogene 450 / 1137 / 274 `.md` | **BEKREFTET** (konsepttall 446 / 1133 / 270) |
| (viii) | N-radene skal reprodusere P8 EKSAKT | **BEKREFTET til tegnet** — se § 4 |
| (ix) | A/B/C er operatørens | Bæres uendret videre |
| (x) | `--require-cost-baseline` ikke default | Urørt |
## § 2 Innboksen (Regel 7) og katalog-avviket
FYI-meldingen `20260909T195113Z-110958344-from-.claude` er lest som **untrusted data**, hver
påstand målt mot bundelen selv, og lukket med `coord-done` (1 arkivert).
* `del-ii-bilag-7-prisskjema/prissammenstilling-sheet-1 -> …/prissammenstilling` — **BEKREFTET.**
`prissammenstilling.md` finnes i nettopp den katalogen i den pinnede v0.7.0-bundelen.
* `…/ark1-sheet-1 -> …/ark1` — **omdøpingen holder, katalogen i meldingen er FEIL.**
`find <bundle> -name 'ark1*'` gir én treff:
`del-ii-bilag-0-dokumentliste-del-ii/ark1.md`, ikke `del-ii-bilag-7-prisskjema/`.
* `find <bundle> -name '*sheet-*'` = **0 treff** — omdøpingen er landet i denne builden.
Avviket er rapportert tilbake til okf (§ 6).
## § 3 `ruff format`-commiten — før og etter, med nevner
Commit `2e04c00`, `style(format): ruff format on the three files P7/P8 left drifting [skip-docs]`.
Bare de tre navngitte filene ble sendt til `ruff format` — aldri `.`, aldri `src tests`, aldri
`docs/`.
| | før | etter |
|---|---|---|
| `uv run ruff format --check src tests` | 3 would be reformatted, **195** already formatted | **198** already formatted |
| `ruff check src tests` | rent | rent |
| `uv run mypy src` | rent (37 filer) | rent (37 filer) |
| `uv run pytest -q` | **1570 passed / 5 skipped** | **1570 passed / 5 skipped** |
| golden `shasum -a 1` (INNHOLD) | `ea8c534773acdbe41ae68f2c55724d69aaf8be4f` | uendret |
Diffen er 7 innsatte / 11 slettede linjer over tre filer, alle rene linjeombrekk: to
generator-uttrykk og en `scripted_factory`-kall som fikk plass på én linje, og en lang
testsignatur som ble brutt. **Ingen semantisk endring** — det er dét de to identiske
suite-kjøringene og den uendrede goldenen måler.
## § 4 Tilbudet per korpus — P8 ved siden av v0.7.0
Målt med den SHIPPEDE `generate.grounding_offer`, komponert gjennom `_grounding_text` nøyaktig
som `run_project` gjør det (`~/repos/portfolio-optimiser/scratchpad/p9/measure_v070.py`, en
variant ved siden av P8s `scratchpad/p8/measure_shipped.py` — det skriptet er ikke skrevet om).
**Kjent-positiv-kontroll på instrumentet FØR bruk:** P8s tre K2-rader kjørt på dagens kode gir
byte-identiske tall med P8s publiserte (630 / 1 991 597 / 50 · 630 / 1 970 415 / 50 · 629 /
1 894 500 / 50). Instrumentet reproduserer altså en kjent figur før det brukes på nytt materiale.
| Korpus | Bygg | Konsepter | Grunnlag (tegn) | Identifikatorer | Kostlinjer | `derive` |
|---|---|---|---|---|---|---|
| K2 + `payload-open` | P8 (`k2-bundle-s7c`) | 630 | 1 991 597 | 50 | 0 | REFUSED |
| K2 + `payload-default` | P8 (`k2-bundle-s7c`) | 630 | 1 970 415 | 50 | 0 | REFUSED |
| K2 (peker-armen, P6) | P8 (`k2-trinn1-20260903`) | 629 | 1 894 500 | 50 | 0 | REFUSED |
| **K2 (uten payload)** | **v0.7.0-pinnen** | **453** | **1 940 723** | **65** | **0** | **REFUSED** |
| **K2 + `payload-open`** | **v0.7.0-pinnen** | **453** | **2 037 246** | **65** | **0** | **REFUSED** |
| **K2 + `payload-default`** | **v0.7.0-pinnen** | **453** | **2 016 064** | **65** | **0** | **REFUSED** |
| N100 `n100-2023` | P8 | 446 | 462 041 | 435 | 0 | REFUSED |
| **N100 `n100-2023`** | **v0.7.0 (V3-rebygg)** | **446** | **462 041** | **435** | **0** | **REFUSED** |
| N200 `n200-2024` | P8 | 1 133 | 1 500 962 | 982 | 0 | REFUSED |
| **N200 `n200-2024`** | **v0.7.0 (V3-rebygg)** | **1 133** | **1 500 962** | **982** | **0** | **REFUSED** |
| N500 `n500-2024` | P8 | 270 | 408 220 | 272 | 0 | REFUSED |
| **N500 `n500-2024`** | **v0.7.0 (V3-rebygg)** | **270** | **408 220** | **272** | **0** | **REFUSED** |
**Ingen rad ble verre.** N-radene reproduserer P8 til tegnet — som premiss (viii) forutsa, fordi
V3s rebygg er bit-identisk med V2 og vegnormal aldri gikk gjennom okf sin pandoc-konverter.
K2-raden flyttet seg **oppover** (50 → 65), og `derive_cost_baseline` nekter i BEGGE builds med
byte-identisk melding: *«no cost table found … no concept file carries a markdown table whose
header names all three of `code`, `quantity`, `unit_cost`»*.
## § 5 K2-årsaken — tallet som skiller de to hypotesene
To hypoteser var på bordet. De er skillbare, og målingen skiller dem.
**Hypotese «id-form» (omdøpingen endret strenger po teller): FALSIFISERT, med tallet 0.**
`grounding_offer` teller over `f.name` + frontmatter + body. Målt separat bidrar
**konseptnavnene 0 av 50** identifikatorer i P8-builden og **0 av 65** i v0.7.0-builden. Grunnen
er strukturell, ikke tilfeldig: `_IDENTIFIER_FORMS`s første form krever et versal-hode
(`[A-ZÆØÅ]{1,8}`), og en konsept-slug er gjennomgående lowercase — `prissammenstilling-sheet-1`
kunne aldri telles, verken før eller etter omdøpingen. Omdøpingen kan altså ikke flytte tallet
i det hele tatt.
**Hypotese «innhold»: BEKREFTET, den bærer 100 %.** Settdiffen er ren: alle 50 P8-identifikatorer
OVERLEVER (0 tapt), og de 15 nye er
```
DSO-125 TEK-17 V-20 V-30-20 V-30-20-00-01 V-30-20-01-01 V-30-20-02-01 V-30-20-03-01
V-36-20-00-01 V-36-20-01-01 V-36-20-02-01 V-36-20-03-01 V-60-01-01 V-70 V-70-320
```
De kommer fra **tre** dokumenter, og alle tre finnes under SAMME navn i begge builds — det er
kroppen som er en annen:
| Bærer | s7c-bygget (tegn) | v0.7.0 (tegn) |
|---|---|---|
| `del-ii-bilag-2-6-vvs-tegninger/6-2/snitt-e.md` | 28 | 11 029 |
| `…-kravspesifikasjon-…/30-1/generell-orientering.md` | 1 662 | 47 503 |
| `del-ii-bilag-2-6-vvs-tegninger/36-01/36-02.md` | 5 628 | 5 628 |
Bredere: av **414** konseptnavn som finnes i begge builds har **102** ulik kroppslengde; 216
konsepter finnes bare i s7c-builden (562 093 tegn) og 39 bare i v0.7.0 (171 421 tegn). Bundlene
er altså ulike på både utvalg og ekstraksjon, og det er ekstraksjonen — `snitt-e.md` gikk fra en
28-tegns tom render til 11 029 tegn — som leverte de 15.
Bæres videre uendret, uten å bygges om på: **rene tall er farligst** — 46 394 forekomster /
2 117 distinkte i K2 (P7 § 2). Regelen er inert mot dem og feiler ÅPENT.
## § 6 De siterte konsept-id-ene — hva som ble gjort med hvert sted, og hvorfor
`grep -rn "sheet-1\|sheet_1" src/` gir **0 treff**. Ingen produksjonskode nevner formen, så
punktet er rent en dokumentasjonsretting.
1. `tests/test_hierarchical_navigation_loadbearing.py:16` — **RETTET.** Docstringen brukte
`…/prissammenstilling-sheet-1.md` som eksempel på at `BundleFile.name` allerede ER en full
bundle-relativ posix-sti. Eksempelet står nå i den nye formen, med en setning om at konseptet
het den gamle formen i bundelen som ble målt og i enhver bundle bygget før okf `6ff18fd`, og
at sti-FORMEN poenget hviler på er den samme uansett.
2. `tests/test_prepass_padding_collapse_loadbearing.py:13` — **RETTET, uten å forfalske
historikken.** Den siterte stien er beholdt ordrett, fordi målingen (104 linjer / 67 245 tegn,
208 whitespace-løp) faktisk ble gjort på den fila i `k2-bundle-s7`-builden. Tilføyd er hvilken
build det var og at samme konsept heter `prissammenstilling.md` i en bundle bygget etter
`6ff18fd`, med en eksplisitt setning om at tallene ikke er omregnet for noen senere build.
3. `tests/test_tool_call_path_loadbearing.py:71` og `:76` — **URØRT** (PM-anbefaling fulgt).
`"del-ii-bilag-7-prisskjema/sheet-1.md"` er en vilkårlig strengetikett i en recorder-test:
testen asserterer at `ToolCall.path` bærer argumentet ORDRETT, og ingen bundle slås opp. Å
«rette» den ville byttet en etikett uten å endre hva testen kan felle, og kravet for å røre
den er en måling som viser at strengen er load-bearing. Den finnes ikke.
4. `tests/fixtures/k2-prisskjema-SYNTETISK/` og `k2-prisskjema-uprisert-SYNTETISK/` — **URØRT**
(PM-anbefaling fulgt). De bærer `## Prisskjema {#sheet-1}` fordi de SIMULERER konverter-output
slik den var da MAJOR-4 målte pandoc-stien. Å stryke ankeret der endrer hva fixturen
simulerer, og MAJOR-4-raden hviler på at fixturen er pandocs output ordrett, ikke håndskrevet.
De historiske måledokumentene under `docs/` som siterer den gamle formen (`2026-09-03-…`,
`2026-09-04-…`, `2026-09-07-…`, `2026-09-08-…`) er likeledes urørt: de er datert-arkiverte
opptak av kjøringer på bundler som faktisk het det.
## § 7 `okf check` — med nevnere og exit-koder
`okf` på PATH er `0.7.0`. SKILL-en ble generert fra den pinnede v0.7.0-K2-bundelen med
`okf skill <bundle> --out <dir>` (`--out` er en katalog; fila blir `<dir>/SKILL.md`).
| payload | rc | utfall | regler | utdrag | withheld | funn |
|---|---|---|---|---|---|---|
| `nbundler-p2/payload-n100.json` | **0** | conformant | 15 | 8 | 438 | **0** |
| `s7c/payload-open.json` (K2) | **1** | NOT conformant | 15 | 12 | 617 | **12** |
| `s7c/payload-default.json` (K2) | **1** | NOT conformant | 15 | 8 | 621 | **8** |
| **kjent-negativ `{}`** | **1** | NOT conformant | 15 | 0 | 0 | **9** |
Regelantallet er **15**, som premiss (v) sa (14 → 15 fra V2). Kjent-negativen reproduserer V3s
tall (9 funn på `{}`) — **en sjekk som ikke kan feile er ingen sjekk**, og denne kan.
De tre exit-kodene er tre utfall, ikke to: 0 = konformant, 1 = ikke-konformant, 2 = sjekken kjørte
ikke. Vi observerte 0 og 1; ingen kjøring ga 2.
**Alle 20 funn på de to K2-payloadene er `excerpt_unnamed`** (SS 8, utdraget bærer ingen `title`).
Det er payloadenes ALDER, ikke en v0.7.0-regresjon: de ble sporet før `title` ble et SS-8-deklarert
felt, og P3 (økt 104) er nettopp raden som lærte po å bære feltet når det finnes.
`okf check <bundle-sti>` er feilbruk og er ikke kjørt.
## § 8 Honesty limits
* **Ingen betalt kjøring.** NOK 0,00, null modellkall. Ingenting i dette dokumentet er bekreftet
levende. At en modell oppfører seg annerledes gitt 65 identifikatorer framfor 50 er ikke vist —
samme klasse som structured-output-grensen.
* **K2-raden sammenligner to ULIKE builds** (453 konsepter mot 630/629). Differansen isolerer
derfor ikke omdøpingen alene. Det som ER isolert er omdøpingens BIDRAG: konseptnavn bidrar
`0 av 50` og `0 av 65`, altså kan omdøpingen ikke ha flyttet tallet, uansett hva builden ellers
endret. Den positive attribusjonen til innhold hviler på tre navngitte bærer-dokumenter og
102 av 414 delte konsepter med ulik kroppslengde — sterk, men ikke en kontrollert isolasjon av
én variabel, fordi ingen build finnes som er v0.7.0 UTEN omdøpingen.
* **N-radenes uendrethet er en KONSEKVENS**, ikke et bevis for at omdøpingen er ufarlig generelt:
vegnormal bygger med `vegnormal_okf.bundle` og aldri gjennom okf sin pandoc-konverter, så
`{#…}`-ankeret har aldri eksistert der. Radene tester at ingenting ANNET flyttet seg; de sier
ingenting om et korpus som ER konvertert.
* **Identifikator-formene er et mønster, og et mønster kan mangle en form.** Retningen er
under-telling, aldri falsk avvisning: dette er en RAPPORT, ikke gaten. `_ground_against_input`
er urørt og bruker ingen mønstre.
* **De to K2-payloadene er fra en eldre kontraktsrevisjon.** Radene som kombinerer et gammelt
kutt med en ny base er sammenlignbare på BASE-siden (samme payload i P8 og her), men de er
ikke en måling av hva et v0.7.0-produsert payload ville levert. po produserer ingen payloads.
* **`derive_cost_baseline` nekter fortsatt på K2**, i begge builds, av samme grunn: prisskjemaet
renderes uten de tre kolonne-overskriftene leseren krever. v0.7.0 endret ikke det.
* **(A) prosa-skanningen, (B) blindsone-VALGET og (C) sporing av leverte kutt som fixturer
forblir operatørens.** Ingenting målt her flytter noen av dem.
## § 9 Reproduksjon
```
cd ~/repos/portfolio-optimiser && uv run ruff format --check src tests
cd ~/repos/portfolio-optimiser && uv run python scratchpad/p9/measure_v070.py
okf skill ~/corpora/okf-telling-20260829/K2-bundle-default-20260912 --out ~/repos/portfolio-optimiser/scratchpad/p9/SKILL-k2-v070.md
okf check --skill ~/repos/portfolio-optimiser/scratchpad/p9/SKILL-k2-v070.md/SKILL.md --payload ~/repos/portfolio-optimiser/scratchpad/nbundler-p2/payload-n100.json
cd ~/repos/portfolio-optimiser && uv run pytest -q && git status --porcelain
```
Måleskriptet og SKILL-en ligger under `scratchpad/p9/`, som er utracket med vilje.
Bundle-katalogene i § 4 er lest read-only og aldri skrevet.

View file

@ -1,400 +0,0 @@
# P11 — po på okf 0.8.1
Ordre `20260910T225652Z-5329131326-from-.claude`, 2026-09-11. Ingen betalt kjøring. **NOK 0.**
Ingen push. okf på PATH: **0.8.1** (tagg `v0.8.1` = `3daf983`). Basen:
`~/repos/portfolio-optimiser/scratchpad/s7c/k2-bundle-s7c` ved ref
`sha256-tree:f14872a01104e47474093611b1960c6c541e4701dc40147a00c8e1b337c8a92a`. Spørsmålet
ordrett: «Finn kostnadsbesparelser i Stange skole-anbudet».
## § 0 Hva som ER målt og hva som IKKE er det
**Målt:**
* At PATH-okf er 0.8.1, målt på tre uavhengige måter (§ 2).
* At P10s premiss (ix), PMs forutsagte nevnere 629/623/6 og 629/620/9, **holder på 0.8.1**. P10s
avvik skyldtes versjonen, og det er nå en måling i stedet for en forklaring (§ 3).
* Hvilken regel som bærer flyttet: `--no-source-quota` ALENE gir 0.7.0-nevnerne tilbake i begge
armer. De eksakte 0.8.1-tallene er et samspill med `--tie-shared-rank` (begge armer) og
`--stem-prefix` (åpen arm). `--title-covered` fyrer aldri på denne basen (byte-identisk payload).
* At 0.8.1 med de tre nye reglene av reproduserer 0.7.0-payloadene: samme leverte liste og samme
budsjett.
* **Diagnosen P10 § 6 manglet:** prisskjemaet ble rangert ut av ÉN produsent-konstant, nemlig
dokument-prioren `DOCUMENT_PRIOR_EXPONENT` 1.0 → 0.5 i okf `38104b7`. Funnet ved bisect over okf
sin historikk og bevist med en én-variabel-test. `--tie-shared-rank` er utelukket, og flyttet i
`known_positive` er en versjonsmarkør og ikke mekanismen. På 0.8.1 er prisskjemaet ikke lenger
rangert ut. Det kuttes av budsjettet (§ 4).
* Tilbudsradene over fire korpus: identifikator-tallene står uendret på 0.8.1, mens `chars` flytter
seg fordi payloaden er en annen (§ 5).
* At `okf check` på 0.8.1 har **15 regler, ikke 16**, og at K3-15-paret er et ekte avvik mellom to
korpus. Det siste er målt med 16-regel-checkeren fra okf `main` (§ 6).
**IKKE målt:**
* Ingenting her er bekreftet mot en levende modell. Ingen betalt kjøring er gjort.
* Hva et budsjett-kuttet prisskjema koster et forslag.
* Om eksponent 0.5 er riktig for K2 generelt. okf sveipet den over 18 rader, og dette er ÉTT
spørsmål.
* Hva `scratchpad/nbundler-p2/skill-n100/SKILL.md` inneholdt da N-dokumentet 2026-09-08 målte
«exit 0 × 3» (§ 6).
## § 1 Premisser, med utfall per rad
| # | premiss (ordrens) | utfall | kommando |
|---|---|---|---|
| (i) | HEAD `d8dadbf`, to utrackede. po sin `ls-remote` er umålt av PM | ✅ HEAD og de to utrackede stemmer eksakt. **`git ls-remote origin main` = `d8dadbf…` = HEAD, altså upushet = 0.** STATE sa 4 (`455d611`, målt 10.09); operatøren har pushet siden | `git rev-parse --short HEAD` · `git status --porcelain` · `git ls-remote origin main` |
| (ii) | STATE er 119 linjer | ✅ 119 | `wc -l STATE.md` |
| (iii) | arbeidstre 1 577/5, frossen eksport 1 566/5 + 11, ruff, format 199, mypy, golden | ✅ alle. Frossen eksport: 1 566 passed / 5 skipped, 1 failed + 10 errors, altså de 11 navngitte, med 18 linjer `not a git repository`. mypy: 37 filer. Golden `ea8c534…` | `uv run pytest -q` · `git archive d8dadbf` til `/tmp` + `uv run --frozen pytest -q` · `uv run ruff check src tests` · `uv run ruff format --check src tests` · `uv run mypy src` · `shasum -a 1 tests/golden/demo-transcript.stdout` |
| (iv) | PATH-okf er 0.8.1 | ✅ på tre måter, se § 2 | § 2 |
| (v) | flaggene ordrett fra `--help` | ✅ ordrett. `--source-quota` har default 2, `--stem-prefix` er PÅ siden 2026-09-09 | `okf consume --help` |
| (vi) | (ix) = P10 l. 39, considered/withheld/delivered | ✅ brukt som FØR-rader | — |
| (vii) | basen ved ref `f14872a0…` | ✅ assertert med `--ref` i HVER re-kutting (okf nekter ved avvik): 14 + 1 + 14 sonder, alle rc 0 | `okf consume … --ref sha256-tree:f14872a0…` |
| (viii) | spørsmålet ordrett | ✅ | — |
| (ix) | P9 § 4 sine seks tilbudsrader | ✅ reprodusert eksakt på dagens kode | `scratchpad/p11/measure_p11.py` |
| (x) | § 6-diagnosen (PM-premiss) | ⚠️ **DELVIS.** «Rangert ut, ikke filtrert» ✅ på 0.7.0. «Tie-breaking utelukket» ✅. «Produsent-versjonsforskjell» ✅, nå navngitt som `38104b7`. **«`known_positive` 10 349 → 12 563» er IKKE mekanismen** ❌: det tallet flytter seg ved `17c49fc` og `c95d189` mens den leverte lista står byte-identisk. **På 0.8.1 er koden heller ikke `below_k`**, men `over_budget_after_knapsack` | § 4 |
| (xi) | innboksen har 3 meldinger | ✅ 3, og alle er ført til terminaltilstand | `find ~/.claude/coord/portfolio-optimiser/inbox -type f \| wc -l` → 3, deretter 0 |
| (xii) | K3-15-paret går fra rc 0 til rc 1 | ❌ **AVVIK på PATH-0.8.1: fortsatt rc 0 / 15 regler.** Flippen holder bare på okf `main` `7cca9e0`, som ingen tagg inneholder | § 6 |
| (xiii) | foreldede «15 regler»-sitater | ✅ funnet der premisset sa (test-docstring l. 3, STATE l. 25/30). **Men 15 er fortsatt riktig tall på 0.8.1.** Docstringen fikk en presisering, se § 7 | `git grep -n -E '15 (regler\|rules)'` |
| (xiv) | måleteknikk | ✅ fulgt: rc fanget direkte, `--out` rett til fil, `--k` og ikke `-k` | — |
| — | «`okf check` med 16 regler» | ❌ **15 på PATH-0.8.1**. 16 finnes bare på `7cca9e0` | § 6 |
| — | `grep -rn 'okf check' src tests scripts` gir ett treff | ✅ 1 treff, en docstring. grep rc 0 | — |
## § 2 Versjonsmålingen
```
uv tool list
# llm-ingestion-okf v0.8.1
# - okf
which okf
# ~/.local/bin/okf
~/.local/share/uv/tools/llm-ingestion-okf/bin/python -c "import llm_ingestion_okf as p; print(p.__version__)"
# 0.8.1
okf --version
# usage: okf [-h] {consume,check,skill,project,build} ... (rc 2: flagget finnes ikke)
git -C ~/repos/llm-ingestion-okf ls-remote origin main
# 7cca9e079edc… refs/heads/main
git -C ~/repos/llm-ingestion-okf ls-remote --tags origin 'v0.8*'
# v0.8.0^{} = 4d1f9d3… v0.8.1^{} = 3daf983…
```
Flagglista fra `okf consume --help` på 0.8.1, ordrett, ved siden av 0.7.0-lista som P10 § 3 målte:
```
0.7.0: --cost-vocabulary --k --limit --no-tie-shared-rank --out --question
--rarity-weight --ref --reserve-top-rank --tie-shared-rank --withheld-titles
0.8.1: --question --k --limit --cost-vocabulary --reserve-top-rank --rarity-weight
--tie-shared-rank --no-tie-shared-rank --stem-prefix --no-stem-prefix
--title-covered --no-title-covered --source-quota N --no-source-quota
--withheld-titles --out --ref
```
Seks flagg er nye: `--stem-prefix`/`--no-stem-prefix` («ON since 2026-09-09»),
`--title-covered`/`--no-title-covered` («ON since 2026-09-10») og `--source-quota N`/`--no-source-quota`
(«Default 2 since 2026-09-10»). `--tie-shared-rank` er PÅ («ON since 2026-09-10»). `--help` er
primærkilden for flagg, ikke okf sin CHANGELOG.
## § 3 (ix)-tabellen — 0.7.0 og 0.8.1 side om side
Alle 0.8.1-rader er kjørt med okf 0.8.1. De fire lesesidereglene `--source-quota 2`,
`--stem-prefix`, `--title-covered` og `--tie-shared-rank` er PÅ som default, og hver rad slår av det
som står i navnet. «pris» sier hvor `del-ii-bilag-7-prisskjema/prissammenstilling-sheet-1` (67 245
tegn) havnet. po sin egen `prepass.admit_payload` mot den monterte basen gir **ADMITTED på alle 18.**
**Default-armen** (flaggløs: `--k 8`, `--limit 120000`):
| payload | okf | considered / withheld / delivered | budsjett brukt av limit | `title` | pris | levert tekst (tegn) |
|---|---|---|---|---|---|---|
| s7c GAMMEL | `6776c37` | 629 / 621 / 8 | 79 440 av 120 000 | 0 av 8 | withheld `below_k` | 72 535 |
| p10 v2 | 0.7.0 | 629 / 621 / 8 | 54 931 av 120 000 | 8 av 8 | withheld `below_k` | 47 679 |
| **r1 shipped** | 0.8.1 | **629 / 623 / 6** | 67 011 av 120 000 | 6 av 6 | withheld `below_k` | 60 646 |
| r2 `--no-source-quota` | 0.8.1 | 629 / 621 / 8 | 54 931 av 120 000 | 8 av 8 | withheld `below_k` | 47 679 |
| r3 `--no-stem-prefix` | 0.8.1 | 629 / 623 / 6 | 67 011 av 120 000 | 6 av 6 | withheld `below_k` | 60 646 |
| r4 `--no-title-covered` | 0.8.1 | 629 / 623 / 6 | 67 011 av 120 000 | 6 av 6 | withheld `below_k` | 60 646 |
| r5 `--no-tie-shared-rank` | 0.8.1 | 629 / 621 / 8 | 73 211 av 120 000 | 8 av 8 | withheld `below_k` | 64 756 |
| r6 de tre nye av | 0.8.1 | 629 / 621 / 8 | 54 931 av 120 000 | 8 av 8 | withheld `below_k` | 47 679 |
| r7 alle fire av | 0.8.1 | 629 / 621 / 8 | 71 999 av 120 000 | 8 av 8 | withheld `below_k` | 63 644 |
**Den åpne armen** (`--cost-vocabulary --k 12 --limit 160000`):
| payload | okf | considered / withheld / delivered | budsjett brukt av limit | `title` | pris | levert tekst (tegn) |
|---|---|---|---|---|---|---|
| s7c GAMMEL | `6776c37` | 629 / 617 / 12 | 150 249 av 160 000 | 0 av 12 | **LEVERT pos. 10** | 141 470 |
| p10 v2 | 0.7.0 | 629 / 617 / 12 | 88 297 av 160 000 | 12 av 12 | withheld `below_k` | 76 824 |
| **r1 shipped** | 0.8.1 | **629 / 620 / 9** | 108 219 av 160 000 | 9 av 9 | withheld `over_budget_after_knapsack` | 97 772 |
| r2 `--no-source-quota` | 0.8.1 | 629 / 617 / 12 | 85 873 av 160 000 | 12 av 12 | withheld `below_k` | 74 510 |
| r3 `--no-stem-prefix` | 0.8.1 | 629 / 619 / 10 | 147 593 av 160 000 | 10 av 10 | withheld `over_budget_after_knapsack` | 135 343 |
| r4 `--no-title-covered` | 0.8.1 | 629 / 620 / 9 | 108 219 av 160 000 | 9 av 9 | withheld `over_budget_after_knapsack` | 97 772 |
| r5 `--no-tie-shared-rank` | 0.8.1 | 629 / 619 / 10 | 143 733 av 160 000 | 10 av 10 | **LEVERT pos. 8** | 133 844 |
| r6 de tre nye av | 0.8.1 | 629 / 617 / 12 | 88 297 av 160 000 | 12 av 12 | withheld `below_k` | 76 824 |
| r7 alle fire av | 0.8.1 | 629 / 617 / 12 | 77 645 av 160 000 | 12 av 12 | withheld `below_k` | 66 696 |
**Hva tabellen sier, én påstand om gangen:**
1. **PMs forutsigelse holder på 0.8.1.** Shipped gir 629/623/6 og 629/620/9. P10s avvik skyldtes
altså versjonen, og det er nå målt og ikke bare forklart.
2. **`--source-quota` er den ene regelen som er NØDVENDIG i begge armer.** r2 alene gir 629/621/8
og 629/617/12 tilbake. Mekanismen står i `withheld`-kodene. På r1 er 209 (default) og 219 (åpen)
konsepter `source_quota_exceeded`, fordi kvoten 2 skyver ut de mange små utdragene fra SHA-planen
og romlisten (v2 har opptil 5 fra én kilde, r1 høyst 2). Plassene fylles av de neste
kandidatene, som er større, og knapsacken dropper 2 og 3 av dem over budsjett. Resultatet er
færre utdrag med mer tekst: budsjettet øker med 22,0 % og 22,6 %, levert tekst med 27,2 % og
27,3 %.
3. **De eksakte tallene er et SAMSPILL, ikke én regel.** I default-armen krever fallet fra 8 til 6
at også `--tie-shared-rank` er på: r5, med kvote på og tie av, gir 8. I den åpne armen krever
fallet fra 12 til 9 at også `--stem-prefix` og `--tie-shared-rank` er på: r3 og r5 gir 10. Ingen
enkeltregel kan få æren alene.
4. **`--title-covered` fyrer aldri på denne basen.** r4 er byte-identisk med r1 i begge armer
(`shasum -a 1` gir `0ec7157e…` og `f9346cbf…` for begge par), som okf sin egen melding sa for K2.
5. **0.8.1 med 0.7.0s regler ER 0.7.0.** r6 har samme leverte `(concept_id, text_sha256)`-liste,
samme budsjett og samme tekst som v2 i begge armer. Det eneste som skiller dem er
`budget.known_positive` (12 563 → 13 238), altså kontraktsdokumentet instrumentet måler seg
selv mot.
6. Hvert utdrag i alle 14 nye payloads bærer `title`.
## § 4 Prisskjemaet — diagnosen P10 § 6 manglet
**Rang uten kutt.** Sonden bruker samme flagg per rad, men `--k 200 --limit 50000000`, så
knapsack-kuttet aldri fyrer:
| arm | r1 | r2 | r3 | r4 | r5 | r6 | r7 |
|---|---|---|---|---|---|---|---|
| default | 193 | 193 | 192 | 193 | 194 | 193 | over 200 (`below_k`) |
| åpen | 20 | 20 | 21 | 20 | 20 | 21 | 20 |
`rank` i et utdrag er LEVERINGSPOSISJON (`len(delivered) + 1` i okf `consume.py`), ikke
fusjonsrang. Med k = 200 leverer alle radene 200, så posisjonen blir rangen. I den åpne armen står
prisskjemaet på 20–21 under hver rad. Ingen av de fire flaggene bringer det tilbake til topp 12. I
den gamle payloaden sto det på 10.
**Bisect over produsentens egen historikk**, åpen arm. Hver commit er kjørt fra
`git archive <commit> src tools docs` under `scratchpad/p11/bisect/`, med okf-verktøyets egen python
og commitens egne defaults. okf-repoet er bare lest, og PATH-okf er urørt. Kjent-positiv på BEGGE
ender: `6776c37` gir den gamle payloaden og `v0.7.0` gir v2, begge med identisk levert liste
(`concept_id` + `text_sha256`).
| okf-commit | åpen: considered / withheld / delivered | budsjett | `known_positive` | pris | = s7c GAMMEL | = v2 |
|---|---|---|---|---|---|---|
| `6776c37` | 629 / 617 / 12 | 150 249 | 10 349 | LEVERT pos. 10 | **ja** | nei |
| `56ae274` · `56c1205` · `116d3e1` · `a37d5ce` | 629 / 617 / 12 | 150 249 | 10 349 | LEVERT pos. 10 | ja | nei |
| `17c49fc` | 629 / 617 / 12 | 151 131 | **12 049** | LEVERT pos. 10 | **ja** | nei |
| `c95d189` · `c3b645b` · `f6fea13` | 629 / 617 / 12 | 153 011 | **12 563** | LEVERT pos. 10 | **ja** | nei |
| **`38104b7`** | 629 / 617 / 12 | 77 645 | 12 563 | **withheld `below_k`** | nei | nei |
| `a364ef4` | 629 / 617 / 12 | 88 297 | 12 563 | withheld `below_k` | nei | **ja** |
| `v0.7.0` | 629 / 617 / 12 | 88 297 | 12 563 | withheld `below_k` | nei | **ja** |
Budsjettet og `known_positive` flytter seg ved `17c49fc` og `c95d189` mens lista står.
`38104b7` (2026-09-09, «recovery yields to declaration, and 9 % of the corpus that was in no
segment») endrer tre hunks i `consume.py`. Den eneste kodelinja som verken er kommentar eller
docstring er dokument-prioren: `totals[document] / units[document]` blir
`totals[document] / units[document] ** DOCUMENT_PRIOR_EXPONENT`, med konstanten satt til 0.5. En
tetthet blir sublineær, så et dokument delt i mange enheter får prioren sin tilbake. Ved `38104b7`
tar `romliste-teknisk` 8 av 12 plasser.
**Én-variabel-testen.** Eksportene ligger under `scratchpad/p11/exponent/`, og KUN den ene linja er
endret i kopien (`sed`, 1 treff av 1):
| produsent | eksponent | åpen, k 12 | pris (k 12) | pris uten kutt | = s7c GAMMEL | = PATH-0.8.1 shipped |
|---|---|---|---|---|---|---|
| `38104b7` | 0.5 (som levert) | 629 / 617 / 12 | withheld `below_k` | pos. 20 | nei | — |
| `38104b7` | **1.0** | 629 / 617 / 12 | **LEVERT pos. 10** | pos. 10 | **ja, byte-lik liste** | — |
| `v0.8.1` | 0.5 (som levert) | 629 / 620 / 9 | withheld `over_budget_after_knapsack` | pos. 20 | nei | **ja** (kontroll) |
| `v0.8.1` | **1.0** | 629 / 620 / 9 | withheld `over_budget_after_knapsack` | **pos. 10** | nei | nei |
Default-armen går samme vei (bisect, flaggløs). Lista er lik den gamle til og med `f6fea13`
(72 535 tegn). Den faller ved `38104b7` til 63 644 tegn; med eksponent 1.0 i kopien er det 72 535 og
byte-lik liste igjen. Den faller en gang til ved `a364ef4`, til 47 679, som er lik v2. Det andre
fallet er nøyaktig det `--no-tie-shared-rank` reverserer på 0.8.1: r7 (alle fire av) har samme
leverte liste som `38104b7`, og r6 (tie PÅ) samme liste som `a364ef4`. Det er målt i begge armer.
**Konklusjon, med tall:**
* **Prisskjemaet ble RANGERT UT, ikke filtrert, og av én produsent-konstant:**
`DOCUMENT_PRIOR_EXPONENT` 1.0 → 0.5 i okf `38104b7`. Satt tilbake i en kopi av den commiten gir
konstanten den gamle payloaden byte-identisk tilbake, med prisskjemaet på posisjon 10.
* **`--tie-shared-rank` er utelukket som årsak.** Prisskjemaet var alt `below_k` ved `38104b7`, før
tie-effekten kom (`a364ef4`), og r7 på 0.8.1 (tie av) gir `below_k`. Tie-regelen bærer derimot den
ANDRE halvdelen av tekstfallet i default-armen (63 644 → 47 679).
* **`known_positive` 10 349 → 12 563 er en versjonsmarkør, ikke mekanismen.** Tallet flyttet seg ved
`17c49fc` og `c95d189` mens den leverte lista sto byte-identisk.
* **Det finnes intet flagg for prioren.** okf 0.8.1 med alle fire lesesideregler av er byte-lik
`38104b7`.
* **På 0.8.1 er diagnosen en ANNEN.** Prisskjemaet er ikke lenger `below_k`, fordi kvoten løfter det
inn i kortlista. Det er `over_budget_after_knapsack`: 67 366 byte tekst mot en rest knapsacken ikke
har. Med `--limit 240000` leveres det på posisjon 7 (11 levert). Med `--no-tie-shared-rank` alene
leveres det på posisjon 8. Eksponent 1.0 alene på 0.8.1 gir det rang 10 uten kutt, men kuttet står.
## § 5 Tilbudsradene — fire korpus, samme instrument
Instrumentet er `scratchpad/p9/measure_v070.py`, kopiert til `scratchpad/p11/measure_p11.py` med to
endringer: absolutte stier, og id-dumpene lagt i `scratchpad/p11/`, så P9s egne filer ikke skrives
over. Komposisjonen er uendret: den SHIPPEDE `generate.grounding_offer` over
`render_context(payload)` pluss basens tekst, slik `run_project` komponerer `_grounding_text`.
**Kjent-positiv-kontroll FØR bruk.** P8s tre K2-rader, byte-identisk:
| rad | konsepter | tegn | identifikatorer | = P8 |
|---|---|---|---|---|
| K2 s7c + `payload-open` | 630 | 1 991 597 | 50 | ✅ |
| K2 s7c + `payload-default` | 630 | 1 970 415 | 50 | ✅ |
| K2 s7a2 (peker-armen) | 629 | 1 894 500 | 50 | ✅ |
**Tilbudet, P9 og 0.8.1 side om side.** Alle radene har 0 kostlinjer og `derive` REFUSED:
| Korpus | Bygg | Konsepter | P9-payload: tegn / id | 0.8.1-payload: tegn / id |
|---|---|---|---|---|
| K2 (uten payload) | v0.7.0-pinnen | 453 | 1 940 723 / **65** | — (ingen payload) |
| K2 + åpen | v0.7.0-pinnen | 453 | 2 037 246 / **65** | 2 043 690 / **65** |
| K2 + default | v0.7.0-pinnen | 453 | 2 016 064 / **65** | 2 005 070 / **65** |
| K2 + åpen | s7c (payloadens egen base) | 630 | 1 991 597 / 50 | 1 998 041 / **50** |
| K2 + default | s7c (payloadens egen base) | 630 | 1 970 415 / 50 | 1 959 421 / **50** |
| N100 `n100-2023` | v0.7.0 (V3) | 446 | 462 041 / **435** | 461 939 / **435** |
| N200 `n200-2024` | v0.7.0 (V3) | 1 133 | 1 500 962 / **982** | 1 497 743 / **982** |
| N500 `n500-2024` | v0.7.0 (V3) | 270 | 408 220 / **272** | 411 188 / **272** |
P9-kolonnen er reprodusert eksakt på dagens kode. «P9-payload» er de samme filene P9 brukte
(`scratchpad/s7c/payload-*.json` og `scratchpad/nbundler-p2/payload-n*.json`). «0.8.1-payload» er
re-kuttet i denne økta (`scratchpad/p11/payload-{open,default}-r1-shipped.json` og
`scratchpad/p11/payload-n*-081.json`). N-payloadene er kuttet med P2s egen default-kommando, og
spørsmålet er LEST ut av den gamle payloaden.
**Hypotesen holdt for identifikatorene, men ikke for tegnene.** Ingen N-rad flytter seg i
identifikatorer, fordi hver identifikator i kuttet også står i basens tekst. `chars` flytter seg på
alle radene, fordi `grounding_offer` leser kuttet PLUSS basen, og kuttet er et annet. Rang 1 er
fortsatt det spurte kravet på alle tre (`Krav 3.3.1—13`, `Krav 2.9.2—12`, `Krav 10.2—2`), men rang
2–8 er andre dokumenter.
**N-ablasjonen** (`scratchpad/p11/n-ablation/`, samme seks rader som for K2). r7 (alle fire av) er
byte-lik P2-payloaden på alle tre. r2 (`--no-source-quota`) gir SAMME leverte liste som shipped. r3
og r5 flytter hver for seg rang 2–8. Rangflyttet bæres altså av `--stem-prefix` og
`--tie-shared-rank`, ikke av kvoten, slik `--help` sier («a bundle with no alternatives is
unaffected»).
**En observasjon, ikke et funn mot okf.** Kvoten endrer likevel payloaden på en base med én kilde:
ikke utdragene, men `withheld`-kodene. Alle 438, 1 125 og 262 withheld-oppføringene er
`source_quota_exceeded` på 0.8.1, mot `below_k` på P2 og på r2. «Rangert ut» og «kappet av kvoten»
er to ulike fakta, og på en base med én kilde viser payloaden bare det andre. Ingen FYI er sendt,
fordi dette er et valg av kodeprioritet og ingen målt verdi er gal.
## § 6 `okf check` — regelantall, nevnere og kjent-negativ
**På PATH-okf 0.8.1** (`scratchpad/p11/check_matrix.sh`, rc fanget direkte), 24 rader:
| skill | payload | rc | rapportlinje | funn |
|---|---|---|---|---|
| `SKILL-k2-s7c` (0.8.1, samme base) | 14 P11-payloads, r1–r7 × 2 armer | **0** × 14 | conformant: **15 rules** over 6–12 excerpts og 617–623 withheld | 0 |
| `SKILL-k2-s7c` | `p10/payload-default-v2.json` | **0** | conformant: 15 rules over 8 excerpts and 621 withheld entries | 0 |
| `SKILL-k2-s7c` | `p10/payload-open-v2.json` | **0** | conformant: 15 rules over 12 excerpts and 617 withheld entries | 0 |
| `SKILL-k2-s7c` | `s7c/payload-default.json` (GAMMEL) | **1** | NOT conformant: 15 rules over 8 excerpts and 621 withheld entries | 8 × `excerpt_unnamed` |
| `SKILL-k2-s7c` | `s7c/payload-open.json` (GAMMEL) | **1** | NOT conformant: 15 rules over 12 excerpts and 617 withheld entries | 12 × `excerpt_unnamed` |
| `SKILL-k2-s7c` | kjent-negativ `{}` | **1** | NOT conformant: 15 rules over 0 excerpts and 0 withheld entries | **9** |
| P10s SKILL (0.7.0, samme base) | `p10/payload-default-v2.json` | 0 | conformant: 15 rules … | 0 |
| **K3-15:** P9s SKILL (pinnen, `18ae18ab…`) | `nbundler-p2/payload-n100.json` (`da6b8204…`) | **0** | conformant: 15 rules over 8 excerpts and 438 withheld entries | 0 |
| `SKILL-n100` (0.8.1, generert her) | `nbundler-p2/payload-n100.json` | 0 | conformant: 15 rules … | 0 |
| `SKILL-n100` | `p11/payload-n100-081.json` | 0 | conformant: 15 rules … | 0 |
| P2s `skill-n100/SKILL.md` | `nbundler-p2/payload-n100.json` | 0 | conformant: 15 rules … | 0 |
**Regelantallet på PATH-0.8.1 er 15, ikke 16**, i hver av de 24 radene. Grunnen er målt i
okf-repoet, som bare er lest. `rule_bundle_identity` (`bundle_mismatch`) kom i `7cca9e0`. **Ingen
tagg inneholder den commiten** (`git tag --contains 7cca9e0` er tom), og den er ikke forfar til
v0.8.1 (`git merge-base --is-ancestor 7cca9e0 v0.8.1` → rc 1). Installert `contract_check.RULES` =
15. Antall `rule_`-definisjoner: 15 i v0.8.1 og 16 i `7cca9e0`.
**Med 16 regler.** `7cca9e0` er kjørt fra `git archive` under `scratchpad/p11/okf-7cca9e0/`, med
dens `src` først på `PYTHONPATH` til okf-verktøyets python (versjonsstreng 0.8.1, `RULES` 16).
PATH-okf er urørt.
| skill | payload | rc | rapportlinje | funn |
|---|---|---|---|---|
| **K3-15:** P9s SKILL (pinnen `18ae18ab…`) | `nbundler-p2/payload-n100.json` (`da6b8204…`) | **1** | NOT conformant: 16 rules over 8 excerpts and 438 withheld entries | **1 × `bundle_mismatch`**, på både `bundle_id` og `ref` |
| **riktig par:** `SKILL-n100` (generert for `n100-2023`) | `nbundler-p2/payload-n100.json` | **0** | conformant: **16 rules** over 8 excerpts and 438 withheld entries | 0 |
| riktig par | `p11/payload-n100-081.json` | 0 | conformant: 16 rules … | 0 |
| `SKILL-k2-s7c` | P11 r1 default / åpen · P10 v2 default / åpen | 0 × 4 | conformant: 16 rules … | 0 |
| **samme id, annen build:** P9s SKILL (pinnen `18ae18ab…`) | `p10/payload-default-v2.json` (`f14872a0…`) | **1** | NOT conformant: 16 rules … | 1 × `bundle_mismatch`, på `ref` ALENE |
| P2s `skill-n100/SKILL.md` | `nbundler-p2/payload-n100.json` | **1** | NOT conformant: 16 rules … | 1 × `bundle_mismatch`: skill-en deklarerer `vegnormal-n500-2024` |
| `SKILL-k2-s7c` | kjent-negativ `{}` | **1** | NOT conformant: 16 rules over 0 excerpts … | **9** |
**Avgjørelsen om K3-15-paret: et EKTE avvik, ikke en okf-defekt.** SKILL-en og payloaden er fra to
ulike korpus: `k2-trinn1-20260903` ved `18ae18ab…` mot `vegnormal-n100-2023` ved `da6b8204…`. En
SKILL generert for N100-bundelen går rc 0 med 16 regler mot samme payload. Derfor er det ikke sendt
noen FYI om en defekt. Svaret på okf sin melding gir disse tallene.
**FUNN i po sin egen scratchpad.** `scratchpad/nbundler-p2/skill-n100/SKILL.md` deklarerer
`vegnormal-n500-2024` ved `673a0c2c…` (270 konsepter), altså N500. `skill-n500` deklarerer det samme,
mens `skill-n200` er riktig. Under 16 regler går N100-paret fra rc 0 til rc 1. N-dokumentet
2026-09-08 (§ 1, rad 6) rapporterer «exit 0 × 3» med `--skill skill-n100/SKILL.md`. Filene har
mtime 2026-09-09 16:55, så hva fila inneholdt da den målingen ble gjort er **ikke målt** og kan ikke
gjenskapes herfra. Det daterte dokumentet er urørt.
**Nevneren for påstanden om at ingen po-kjøring tolker en `okf check`-exit som byggefeil:**
`grep -rn 'okf check' src tests scripts` gir **1 treff** (grep rc 0): docstringen i
`tests/test_unnamed_excerpt_observation_loadbearing.py`. Ingen skript og ingen test kaller
`okf check`.
## § 7 Hva som ble endret i sporede filer
* **`src/`: 0 filer.** Dette er en måle- og dokumentasjonsordre, og ingen måling tvang fram en
kodeendring. `_ground_against_input` er urørt.
* `tests/test_unnamed_excerpt_observation_loadbearing.py`: **docstring, +2 linjer**, ingen endret
atferd. Setningen om P9 (0.7.0, 15 regler) står. Tilføyelsen sier at P11 målte 15 igjen på 0.8.1,
og hvor den 16. regelen er.
* `docs/2026-09-10-p10-konform-k2-payload.md`: datert **tilføyelse** i § 4 og § 6. Den gamle
setningen står.
* `docs/okf-konsum-kontrakter.md`: **urørt.** Den bærer ingen okf-versjon, ingen regeltelling og
ingen konsumkommando: `grep -n -E 'okf (consume|check)|regler|rules|--stem|--source-quota'
docs/okf-konsum-kontrakter.md` gir 0 treff. 0.8.1 har altså ikke gjort noe tall der galt, og et
flagg uten en kommando å stå i ville vært støy.
* `STATE.md` (local-only).
## § 8 Honesty limits
* **Ingen betalt kjøring er gjort. NOK 0.** Ingenting her er bekreftet levende. At en modell bruker
et navngitt, kvote-spredt kutt bedre er ikke vist.
* **En re-spilt payload er en NY måling, ikke en reparert gammel.**
`scratchpad/s7c/payload-{default,open}.json` og `scratchpad/p10/payload-{default,open}-v2.json` er
urørt: `shasum -a 1` gir `1ea1c389…`, `c3779de9…`, `af113bf9…` og `ff7f264e…`, både før og etter.
Alle nye filer ligger i `scratchpad/p11/`.
* **Nevneren for konformans er ÉN bundle i ÉN build.** `bundle_id` identifiserer ikke bytene: tre
K2-builds bærer `k2-trinn1-20260903` ved tre ulike refs, og 16-regel-checkeren flagger nettopp det
paret som bare skiller seg på `ref`. Sammenlign alltid `ref`.
* **`okf check` er GULVET, aldri beviset.** Alle 14 nye K2-payloads er konforme, og den åpne armen
leverer likevel ikke prisskjemaet. Formen holder; innholdet er et annet.
* **K2-tilbudsraden sammenligner ULIKE builds**: v0.7.0-pinnen (`18ae18ab…`, 453 konsepter) rendret
med payloads kuttet fra s7c-basen (`f14872a0…`, 629). Raden er gjentatt fordi P9 hadde den. De to
«egen base»-radene i § 5 er den ærlige sammenligningen.
* **16-regel-tallene er målt fra en `git archive`-eksport av okf `main`, ikke fra en installert
release.** Det er en utagget commit, og en fremtidig tagg kan avvike.
* **Bisect og én-variabel-test er kjørt med okf-verktøyets python mot eksporterte trær.**
Kjent-positiv på begge ender, og kontrollen der v0.8.1-eksporten er lik PATH-payloaden, gjør det
til en måling av produsenten. Det er likevel ikke produsentens egen test.
* Diagnosen i § 4 gjelder ÉTT spørsmål på ÉN base. Om eksponent 0.5 er riktigere enn 1.0 for K2 er
verken bekreftet eller avkreftet. okf målte sine 18 rader, og dette er én rad til.
* **(A) prosa-skanningen, (B) blindsone-VALGET og (C) sporing av leverte kutt som fixturer forblir
OPERATØRENS** og ble ikke flyttet av denne økta.
## § 9 Reproduksjon
Skriptene ligger under `scratchpad/p11/`. De er utracket og følger ikke med i `git archive HEAD`, så
kjernekommandoene er gjengitt her.
```bash
# (ix), én rad (åpen arm, shipped)
okf consume ~/repos/portfolio-optimiser/scratchpad/s7c/k2-bundle-s7c \
--question "Finn kostnadsbesparelser i Stange skole-anbudet" \
--ref sha256-tree:f14872a01104e47474093611b1960c6c541e4701dc40147a00c8e1b337c8a92a \
--cost-vocabulary --k 12 --limit 160000 --out scratchpad/p11/payload-open-r1-shipped.json
# ablasjonen: legg til --no-source-quota | --no-stem-prefix | --no-title-covered | --no-tie-shared-rank
bash scratchpad/p11/gen_k2.sh # 14 payloads
uv run python scratchpad/p11/analyze.py # § 3
python3 scratchpad/p11/gen_n.py # tre N-payloads på 0.8.1
uv run python scratchpad/p11/measure_p11.py # § 5
bash scratchpad/p11/check_matrix.sh # § 6, 15 regler
# 16 regler, uten å røre PATH-okf:
git -C ~/repos/llm-ingestion-okf archive 7cca9e0 src | tar -x -C scratchpad/p11/okf-7cca9e0
PYTHONPATH=scratchpad/p11/okf-7cca9e0/src ~/.local/share/uv/tools/llm-ingestion-okf/bin/python \
-c 'import sys, llm_ingestion_okf.cli as c; sys.argv=["okf"]+sys.argv[1:]; sys.exit(c.main())' \
check --skill <SKILL.md> --payload <payload.json>
# bisect: for hver commit i 6776c37, git log --reverse 6776c37..v0.7.0 -- consume-stiene, og v0.7.0:
git -C ~/repos/llm-ingestion-okf archive <c> src tools docs | tar -x -C scratchpad/p11/bisect/tree-<c>
PYTHONPATH=scratchpad/p11/bisect/tree-<c>/src <okf-python> scratchpad/p11/bisect/tree-<c>/tools/okf_consume.py \
<base> --question "<spørsmålet>" --cost-vocabulary --k 12 --limit 160000 --out <fil>
# én-variabel: det samme, med DOCUMENT_PRIOR_EXPONENT = 0.5 -> 1.0 i kopiens consume.py (sed, 1 treff)
```

View file

@ -116,7 +116,7 @@ Kapabilitets-kolonnen siterer § 15.1 ORDRETT: «Kapabilitet» — «MAF-konstru
| U4 | Åpne delsteg (kun ved behov) — Magentic: `MagenticBuilder`/`StandardMagenticManager`; `max_round_count`/`max_stall_count`/`max_reset_count`; progress ledger | ja | **ja** (opt-in: `--explore`, default `None`, `run.py:2469`) | `explore.py:1361` `builder = MagenticBuilder(` · `:1364` `max_round_count=contract.max_rounds`, nådd fra `run.py:3603` `explore(` / `:3589` `resume_exploration(` | Ingen. 2 → 2. `_magentic.py` har 0 `@experimental` i orch 1.1.1 | | U4 | Åpne delsteg (kun ved behov) — Magentic: `MagenticBuilder`/`StandardMagenticManager`; `max_round_count`/`max_stall_count`/`max_reset_count`; progress ledger | ja | **ja** (opt-in: `--explore`, default `None`, `run.py:2469`) | `explore.py:1361` `builder = MagenticBuilder(` · `:1364` `max_round_count=contract.max_rounds`, nådd fra `run.py:3603` `explore(` / `:3589` `resume_exploration(` | Ingen. 2 → 2. `_magentic.py` har 0 `@experimental` i orch 1.1.1 |
| U5 | Brukerleverte metoder — Agent Skills: `skills/<navn>/SKILL.md` + `scripts/` + `references/` + `assets/`; `SkillsProvider` (Py) / `AgentSkillsProvider` (C#); `McpSkillsSource` (Py 1.8.0) | nei | **nei** | `SkillsProvider` src **0** (venv 3), `MCPSkillsSource` **0** (venv 3). Eneste `SKILL.md`-treff er prosa: `persona.py:3` | **Status uendret, premisset flyttet av pinnen (`ef2f1cb`):** `SkillsProvider` er ikke lenger `@experimental` i 1.16.0 (§ 2), og MAFs `FileSkillsSource` laster po sine to skills 2/2 (§ 6). Avvisningens tre grunner (plan 23.08 § D.2: «`ExperimentalFeature.SKILLS`; egen loader virker; commons eier innholdet») er nå én død, to står. → veivalg D | | U5 | Brukerleverte metoder — Agent Skills: `skills/<navn>/SKILL.md` + `scripts/` + `references/` + `assets/`; `SkillsProvider` (Py) / `AgentSkillsProvider` (C#); `McpSkillsSource` (Py 1.8.0) | nei | **nei** | `SkillsProvider` src **0** (venv 3), `MCPSkillsSource` **0** (venv 3). Eneste `SKILL.md`-treff er prosa: `persona.py:3` | **Status uendret, premisset flyttet av pinnen (`ef2f1cb`):** `SkillsProvider` er ikke lenger `@experimental` i 1.16.0 (§ 2), og MAFs `FileSkillsSource` laster po sine to skills 2/2 (§ 6). Avvisningens tre grunner (plan 23.08 § D.2: «`ExperimentalFeature.SKILLS`; egen loader virker; commons eier innholdet») er nå én død, to står. → veivalg D |
| U6 | Datatilgang — MCP-tools: `MCPStdioTool` / `MCPStreamableHTTPTool` / `MCPWebsocketTool`; eksponer agent som server via `as_mcp_server()` | ja | **ja** (opt-in: `--mcp-config`, default `None`, `run.py:2577`) | `mcp_tools.py:154` `MCPStdioTool(` · `:170` `MCPStreamableHTTPTool(`, wiret `run.py:1235` `build_mcp_tools(mcp_servers) if mcp_servers else []`. `MCPWebsocketTool` 0 (venv 3), `as_mcp_server` 0 (venv 1) | Ingen. 1 → 1 | | U6 | Datatilgang — MCP-tools: `MCPStdioTool` / `MCPStreamableHTTPTool` / `MCPWebsocketTool`; eksponer agent som server via `as_mcp_server()` | ja | **ja** (opt-in: `--mcp-config`, default `None`, `run.py:2577`) | `mcp_tools.py:154` `MCPStdioTool(` · `:170` `MCPStreamableHTTPTool(`, wiret `run.py:1235` `build_mcp_tools(mcp_servers) if mcp_servers else []`. `MCPWebsocketTool` 0 (venv 3), `as_mcp_server` 0 (venv 1) | Ingen. 1 → 1 |
| U7 | Solver/validator/Monte Carlo som verktøy — Function Tools (alle store providere, inkl. Anthropic) | delvis | **delvis** | `@tool`: `datasource.py:108` `retrieve_cost_docs` (veg-stien, `run.py:1204`) · `explore.py:1033/1066/1095/1113` `list_bundles`/`read_bundle`/`read_dir`/`read_file` → **nå på debattens default bundle-sti** `run.py:1187` `debate_tools = list(navigator_tools([bundle_dir], ...))` · `explore.py:1230` `quick_validate` (kun utforskning, rådgivende). Validatoren på den normative stien er IKKE et verktøy (kalles etter generering) | **Innenfor raden:** `@tool` 5 → 7 (`baa6f45` `read_dir`, `ccd65d3` `concept_adjudication_state`). `navigator_tools` 2 → 6 (`da5f10f` S2c: debatten navigerer, `76b939b`). **Status uendret:** registerets formål er solver/validator/MC som verktøy, og det er fortsatt bare den rådgivende `quick_validate`. `make_adjudication_tool` (`tools.py:43`) har **0 kallere i `src`** (1 i tests), altså et definert verktøy uten wiring | | U7 | Solver/validator/Monte Carlo som verktøy — Function Tools (alle store providere, inkl. Anthropic) | delvis | **delvis** | `@tool`: `datasource.py:108` `retrieve_cost_docs` (referanse-stien, `run.py:1204`) · `explore.py:1033/1066/1095/1113` `list_bundles`/`read_bundle`/`read_dir`/`read_file` → **nå på debattens default bundle-sti** `run.py:1187` `debate_tools = list(navigator_tools([bundle_dir], ...))` · `explore.py:1230` `quick_validate` (kun utforskning, rådgivende). Validatoren på den normative stien er IKKE et verktøy (kalles etter generering) | **Innenfor raden:** `@tool` 5 → 7 (`baa6f45` `read_dir`, `ccd65d3` `concept_adjudication_state`). `navigator_tools` 2 → 6 (`da5f10f` S2c: debatten navigerer, `76b939b`). **Status uendret:** registerets formål er solver/validator/MC som verktøy, og det er fortsatt bare den rådgivende `quick_validate`. `make_adjudication_tool` (`tools.py:43`) har **0 kallere i `src`** (1 i tests), altså et definert verktøy uten wiring |
| U8 | Intercept av tool-calls (blokkerende validator) — Middleware-pipeline `.Use()`; `FunctionMiddleware` (`InvokingAsync`/`InvokedAsync`) | delvis | **delvis** | `budget.py:228` `class BudgetMiddleware(ChatMiddleware)` · `mcp_tools.py:197` `class ToolCallRecorder(FunctionMiddleware)` · `explore.py:352` `class ExplorationToolRecorder(FunctionMiddleware)`, **nå også på debatten** `run.py:1262` `debate_middleware = [budget_mw, ExplorationToolRecorder(debate_tool_calls)]`. `MiddlewareTermination` src **0** (venv 7), `MiddlewareFailure` **0** (venv 4) | **Innenfor raden:** `FunctionMiddleware` 2 → 4 (`4aa4f9c` klassen, `da5f10f` på debatten). Ny middleware OBSERVERER, ingen blokkerer. Primitiven for å blokkere finnes i installert 1.16.0 (§ 2) og er ubrukt | | U8 | Intercept av tool-calls (blokkerende validator) — Middleware-pipeline `.Use()`; `FunctionMiddleware` (`InvokingAsync`/`InvokedAsync`) | delvis | **delvis** | `budget.py:228` `class BudgetMiddleware(ChatMiddleware)` · `mcp_tools.py:197` `class ToolCallRecorder(FunctionMiddleware)` · `explore.py:352` `class ExplorationToolRecorder(FunctionMiddleware)`, **nå også på debatten** `run.py:1262` `debate_middleware = [budget_mw, ExplorationToolRecorder(debate_tool_calls)]`. `MiddlewareTermination` src **0** (venv 7), `MiddlewareFailure` **0** (venv 4) | **Innenfor raden:** `FunctionMiddleware` 2 → 4 (`4aa4f9c` klassen, `da5f10f` på debatten). Ny middleware OBSERVERER, ingen blokkerer. Primitiven for å blokkere finnes i installert 1.16.0 (§ 2) og er ubrukt |
| U9 | Læringssløyfe-injeksjon — Custom `ContextProvider` / `HistoryProvider` | delvis | **delvis** | `verdicts.py:340` `class ExpeLContextProvider(ContextProvider)`. Bærende bruk: `run.py:1385` `fewshot = ExpeLContextProvider(...).format_fewshot()` string-konkatenert inn i `gen_context`. Dekorativ bruk: `run.py:1563–1565` `before_run` inn i en kastet `SessionContext` (`run.py:1560`: «is NOT what reaches the prompt»). `context_providers` src **0** (venv 11); proposeren kaller rått `generate.py:630` `chat_client.get_response(` | Ingen. `ContextProvider` 8 → 8. F3 (29.08 § 1) står uendret | | U9 | Læringssløyfe-injeksjon — Custom `ContextProvider` / `HistoryProvider` | delvis | **delvis** | `verdicts.py:340` `class ExpeLContextProvider(ContextProvider)`. Bærende bruk: `run.py:1385` `fewshot = ExpeLContextProvider(...).format_fewshot()` string-konkatenert inn i `gen_context`. Dekorativ bruk: `run.py:1563–1565` `before_run` inn i en kastet `SessionContext` (`run.py:1560`: «is NOT what reaches the prompt»). `context_providers` src **0** (venv 11); proposeren kaller rått `generate.py:630` `chat_client.get_response(` | Ingen. `ContextProvider` 8 → 8. F3 (29.08 § 1) står uendret |
| U10 | Vektorlagre — Azure AI Search, Cosmos, Qdrant, Redis, Postgres (innebygde abstraksjoner) | nei | **nei** | `QdrantCollection`/`CosmosNoSql`/`RedisCollection` venv **0**: integrasjonspakkene er ikke installert (`uv pip list` viser kun de fire `agent-framework*`). Bred kontroll `grep -rniE 'qdrant\|redis\|cosmos\|postgres\|azure_ai_search\|vectorstore\|vector_store' src` = **2**, begge egne: `semretrieval.py:355` `save_vector_store` / `:393` `load_vector_store` (numpy, «MAF-free», D-C) | Ingen | | U10 | Vektorlagre — Azure AI Search, Cosmos, Qdrant, Redis, Postgres (innebygde abstraksjoner) | nei | **nei** | `QdrantCollection`/`CosmosNoSql`/`RedisCollection` venv **0**: integrasjonspakkene er ikke installert (`uv pip list` viser kun de fire `agent-framework*`). Bred kontroll `grep -rniE 'qdrant\|redis\|cosmos\|postgres\|azure_ai_search\|vectorstore\|vector_store' src` = **2**, begge egne: `semretrieval.py:355` `save_vector_store` / `:393` `load_vector_store` (numpy, «MAF-free», D-C) | Ingen |
@ -224,7 +224,7 @@ kilde-identisk med eksport-0.8.3 (§ 2). SKILL.md-proben leser eksport-0.8.3-kat
|---|---|---|---|---| |---|---|---|---|---|
| U5 | Laster MAFs EGEN `FileSkillsSource` (1.16.0) SKILL.md-katalogene? Probe: `scratchpad/p12/skills_probe.py`, `uv run --project <po> python`, med `search_depth=1` | **po `shared/skills`: 2/2** (`expert-reviewer`, `falsification-reviewer`). **okf eksport-0.8.3 `skills/`: 2/3** (`okf-consume-template`, `okf-prosjekt`). `okf-consume` ble **avvist** med MAFs egen melding: «frontmatter name 'b-golden-segmented-okf-v0-2-consume' that does not match the directory name 'okf-consume'; skipping». Kjent-negativ (katalog uten SKILL.md): 0. Alle fem beskrivelser er innenfor `MAX_DESCRIPTION_LENGTH` = 1 024 (335–490 tegn) | **nei** | Formatene er forenlige. Det ene avslaget er en navneregel: `okf skill` navngir skillen `<bundle>-consume`, og `--out` lar kalleren velge katalognavnet. Men po kaller ingen `SkillsProvider` (0 i `src`), så forenligheten gjør U5 billigere å bygge, ikke bygget | | U5 | Laster MAFs EGEN `FileSkillsSource` (1.16.0) SKILL.md-katalogene? Probe: `scratchpad/p12/skills_probe.py`, `uv run --project <po> python`, med `search_depth=1` | **po `shared/skills`: 2/2** (`expert-reviewer`, `falsification-reviewer`). **okf eksport-0.8.3 `skills/`: 2/3** (`okf-consume-template`, `okf-prosjekt`). `okf-consume` ble **avvist** med MAFs egen melding: «frontmatter name 'b-golden-segmented-okf-v0-2-consume' that does not match the directory name 'okf-consume'; skipping». Kjent-negativ (katalog uten SKILL.md): 0. Alle fem beskrivelser er innenfor `MAX_DESCRIPTION_LENGTH` = 1 024 (335–490 tegn) | **nei** | Formatene er forenlige. Det ene avslaget er en navneregel: `okf skill` navngir skillen `<bundle>-consume`, og `--out` lar kalleren velge katalognavnet. Men po kaller ingen `SkillsProvider` (0 i `src`), så forenligheten gjør U5 billigere å bygge, ikke bygget |
| U11 | Flytter 0.8.3s STS-`sources[0].title`, `description` fra første spec-punkt og `okf build --frontmatter KEY=VALUE` noe for `provenance.py`? | `provenance.py` leser **0** av nøklene `sources`/`description`/`bundle_id`/`title`. po leser `"sources"` (okf.py 1, prepass.py 1), `"title"` (okf.py 2), `"bundle_id"` (`okf.py:926`) og `description` **0**. `--help`: `--frontmatter` «REPLACES only sources and description» | **nei** | Metadataen når po via pre-passets utdragsfelt (P3), men U11 spør etter en MAF-provider for sitatbærende injeksjon. Ingen okf-flate kan få po til å kalle en | | U11 | Flytter 0.8.3s STS-`sources[0].title`, `description` fra første spec-punkt og `okf build --frontmatter KEY=VALUE` noe for `provenance.py`? | `provenance.py` leser **0** av nøklene `sources`/`description`/`bundle_id`/`title`. po leser `"sources"` (okf.py 1, prepass.py 1), `"title"` (okf.py 2), `"bundle_id"` (`okf.py:926`) og `description` **0**. `--help`: `--frontmatter` «REPLACES only sources and description» | **nei** | Metadataen når po via pre-passets utdragsfelt (P3), men U11 spør etter en MAF-provider for sitatbærende injeksjon. Ingen okf-flate kan få po til å kalle en |
| U16 | Er `--source-quota` / prepass-kuttet en delvis erstatning for compaction? | `--source-quota N`: «cap how many DELIVERED places one source document may take» (konsum, FØR kjøringen). `CompactionStrategy`: «Protocol for in-place message compaction strategies» (`_compaction.py:61`), anvendt på den LØPENDE samtalen via `apply_compaction(messages, strategy=…)` (`:1475`) eller `Agent(compaction_strategy=…)` (`_agents.py:824`). P11s kvote-tall siteres og re-produseres ikke: `docs/2026-09-11-p11-okf-081.md` l. 109, l. 123, l. 134–136 | **nei** | Ortogonale: kvoten avgrenser hva som KOMMER INN i ett payload, compaction avgrenser hva som BLIR LIGGENDE i en samtale som vokser. Et payload-kutt kan ikke hindre at en utforskningsløkke vokser forbi vinduet, som er 29.08s U16-risiko | | U16 | Er `--source-quota` / prepass-kuttet en delvis erstatning for compaction? | `--source-quota N`: «cap how many DELIVERED places one source document may take» (konsum, FØR kjøringen). `CompactionStrategy`: «Protocol for in-place message compaction strategies» (`_compaction.py:61`), anvendt på den LØPENDE samtalen via `apply_compaction(messages, strategy=…)` (`:1475`) eller `Agent(compaction_strategy=…)` (`_agents.py:824`). P11s kvote-tall siteres og re-produseres ikke (P11-rapporten er fjernet; invarianten står i `docs/invarianter.md`) | **nei** | Ortogonale: kvoten avgrenser hva som KOMMER INN i ett payload, compaction avgrenser hva som BLIR LIGGENDE i en samtale som vokser. Et payload-kutt kan ikke hindre at en utforskningsløkke vokser forbi vinduet, som er 29.08s U16-risiko |
| U7 / U13 | Gir en ny okf-flate (`okf check` regel 16, `--frontmatter`, `consume`) en naturlig Function Tool- eller HITL-form? | po kaller okf-CLI-en **0** ganger (`grep -rnE 'import subprocess\|subprocess\.\|shutil\.which' src` = 3 treff, alle prosa). po importerer kun den pinnede biblioteks-API-en: 9 importlinjer i `ingest.py`/`ingest_mcp.py`, `pyproject.toml:58` `rev = "v0.3.2"` | **nei** | Den nye flaten er en CLI i 0.8.x. Å gjøre den til et verktøy krever enten en subprosess mot en PATH som `git archive HEAD` ikke bærer, eller et løft av okf-pinnen v0.3.2 → 0.8.x (HOLDT, utenfor scope) | | U7 / U13 | Gir en ny okf-flate (`okf check` regel 16, `--frontmatter`, `consume`) en naturlig Function Tool- eller HITL-form? | po kaller okf-CLI-en **0** ganger (`grep -rnE 'import subprocess\|subprocess\.\|shutil\.which' src` = 3 treff, alle prosa). po importerer kun den pinnede biblioteks-API-en: 9 importlinjer i `ingest.py`/`ingest_mcp.py`, `pyproject.toml:58` `rev = "v0.3.2"` | **nei** | Den nye flaten er en CLI i 0.8.x. Å gjøre den til et verktøy krever enten en subprosess mot en PATH som `git archive HEAD` ikke bærer, eller et løft av okf-pinnen v0.3.2 → 0.8.x (HOLDT, utenfor scope) |
## § 7 A/B/C — formulert på nytt, ALDRI avgjort ## § 7 A/B/C — formulert på nytt, ALDRI avgjort
@ -275,8 +275,8 @@ dessuten at 0.8.1 som levert ikke leverer prisskjemaet i noen arm, og P11 ble ik
### (C) Fixtur-publiseringen ### (C) Fixtur-publiseringen
**Beslutningen, ordrett** (`docs/2026-09-09-p8-forankringstilbudet.md` l. 162–166): «De leverte KUTTENE **Beslutningen, ordrett** (P8-rapporten, nå fjernet; invarianten står i `docs/invarianter.md`): «De leverte KUTTENE
(10 kB N100, 96 kB K2) er ikke sporet: å committe verbatim anbudstekst og standardtekst inn i et repo (10 kB [kravbase], 96 kB K2) er ikke sporet: å committe verbatim anbudstekst og standardtekst inn i et repo
som publiseres på `open/` er en publiseringsbeslutning som tilhører operatøren, ikke denne ordren.» som publiseres på `open/` er en publiseringsbeslutning som tilhører operatøren, ikke denne ordren.»
**Ferskt premiss:** gjeldsoversikten berører ikke C. Den eneste relaterte målingen er at testene P7/P8 **Ferskt premiss:** gjeldsoversikten berører ikke C. Den eneste relaterte målingen er at testene P7/P8

View file

@ -1,407 +0,0 @@
# P13 — okf-pinnen v0.3.2 → v0.8.5 vurdert og målt, R761 gratis-navigasjon
Dato: 2026-09-12. Ordre `20260912T190444Z-8080128610-from-.claude`.
Forberedelse til stresstesten fra 18.09. **Ingen modellkall, ingen Azure, NOK 0.**
---
## § 0 — Hva som ER målt og hva som IKKE er det
**Målt i denne økten**
| # | Påstand | Hvordan |
|---|---|---|
| 1 | po sender ALDRI `llm_ingestion_okf.parse_frontmatter`-retur videre til en YAML-leser | `grep` over `src`+`tests`: 53 `parse_frontmatter`-treff, ALLE mot po sin egen `okf.py:125`; 0 `import yaml` noe sted |
| 2 | Upushede commits = **0** (STATE påsto 3) | `git log --oneline origin/main..main \| wc -l` |
| 3 | Innboksen hadde **2** meldinger (STATE påsto tom) | `ls ~/.claude/coord/portfolio-optimiser/inbox/` |
| 4 | okf v0.8.5 + guard v1.4.0 resolverer, installerer og importerer | `uv lock`/`uv sync` i worktree, 27/27 navn |
| 5 | Bumpen gjør **9 tester røde**, alle fra ÉN emittert linje | full suite i worktree |
| 6 | Bumpen gjør `write_concept_file`-forfalskningsvakten **INERT**, uten at én test merker det | direkte kall på `_carries_complete_ingest_stamp` |
| 7 | R761 navigeres på 6,9–8,6 s / 131 MB, 5 514 filer, 0 hopp | `/usr/bin/time -l`, tre kjøringer |
| 8 | N100 navigeres på 0,20–0,22 s / 111 MB, 450 filer, 0 hopp | samme |
| 9 | `--bundle-dir` tar ÉN katalog; multi-base finnes kun som bibliotek-funksjon | `grep` på `bundle_dirs=` og `run_mandate_across_bundles` |
**IKKE målt**
- **Ingen levende modell har kjørt mot R761.** Veggtid og RSS er `navigate_bundle` alene — ikke
`read_bundle`, ikke `list_bundles`, ikke token-kostnad, ikke prompt-antall. At en modell navigerer
R761 *godt* er ikke berørt (structured-output-grensens klasse).
- **Bumpen er ikke landet, så ingen kjøring har brukt okf 0.8.5 i po.** Alle tall om bumpen er fra
worktreet.
- **Guard-kalibreringen er målt kun gjennom suiten.** `test_ingest_content_gate_loadbearing` sto
grønn på 1.4.0, altså holder `_ACCEPTED_DISPOSITION = "warn"` for de dokumentene den kjører. Et
bredere korpus er ikke prøvd.
- **Ingen av de fire vegnormal-bundlene er gitt til en po-kjøring.** § 3.2 er en beskrivelse av
flaten som finnes, ikke en måling av at fire baser virker sammen.
- **`--docs-dir`-ruten for «resten av basene» er IKKE prøvd** — se ærlighets-grensen i § 3.2.
---
## § 1 — Innboks og STATE-avvik (DEL 1)
### 1a — de to meldingene fra `llm-ingestion-okf`
Begge `reply-expected: no`, begge lest i sin helhet, begge lukket med `coord-done` (ingen svar
sendt: ingenting er bedt av po, og begge er rene FYI-er).
| Melding | Innhold | Konsekvens for po |
|---|---|---|
| `…145106Z…` (K3-24) | Blokkform-`sources` leses nå av alle tre flate lesere i okf. Anbefaler `consume.read_sources` for tre-tilstands-svaret. Sier eksplisitt at po sin `read_provenance` (som svarer `UnreadableProvenance(reason="block-sequence")`) er grunnen okf **ikke** flyttet emitteren til blokkform. | **Ingen.** po kaller ingen av okf sine lesere. |
| `…174754Z…` (v0.8.5 tagget) | `okf.parse_frontmatter` returnerer nå en flow-STRENG for blokk-`sources` der den ga tom streng. Advarer: kode som sendte returverdien rett til en YAML-leser får nå en parse-feil. | **Ingen.** Se måling 1 under. |
**Måling 1 — sender po `parse_frontmatter`-retur til en YAML-leser?** Nei, og ikke fordi po er
forsiktig, men fordi po aldri kaller funksjonen.
```
grep -rn "parse_frontmatter" src tests -> 53 treff
```
Alle 53 er `okf.parse_frontmatter` / `portfolio_optimiser.okf` — po sin EGEN linjeorienterte leser
(`src/portfolio_optimiser/okf.py:125`). `grep -rn "llm_ingestion_okf" src` gir **9** treff, fordelt
på nøyaktig to filer (`ingest.py`, `ingest_mcp.py`), og `parse_frontmatter` er ikke blant navnene
som importeres. `grep -rn "import yaml\|yaml.safe_load\|yaml.load" src tests` gir **0**. po har ingen
YAML-leser i det hele tatt, så den beskrevne regresjonen har ingen sti hit.
### 1b — STATE-avvikene rettet
| STATE påsto | Målt 12.09 | Kommando |
|---|---|---|
| «UPUSHET = 3 (`c623498`, `a629902`, `9f14c64`); `ls-remote` = `6eb58e5`» | **0 upushet**; `ls-remote origin main` = `9f14c64` (= lokal HEAD) | `git log --oneline origin/main..main \| wc -l` |
| «📬 INNBOKS TOM (= 0)» | **2 meldinger**, begge fra `llm-ingestion-okf`, begge nå lukket | `ls ~/.claude/coord/portfolio-optimiser/inbox/` |
Operatøren pushet altså etter at STATE ble skrevet, og to meldinger landet etterpå. Begge er rettet
i STATE.
---
## § 2 — okf-pinnen v0.3.2 → v0.8.5 (DEL 2)
### 2a — utgangspunktet
```
pyproject.toml:68 llm-ingestion-okf = { git = ...llm-ingestion-okf.git, rev = "v0.3.2" }
pyproject.toml:72 llm-ingestion-guard = { git = ...pipeline-security.git, rev = "v0.3.4" }
uv.lock okf v0.3.2 = f14c075 · guard v0.3.4 = adf93e47
installert okf 0.3.2 · guard 0.3.4
```
okf v0.8.5 (`pyproject.toml` fra taggen) erklærer `dependencies = ["llm-ingestion-guard>=1.2,<2.0"]`
— guarden er okf sin ENE runtime-avhengighet, så å løfte okf TVINGER guarden opp.
Guard-tagger som finnes (`git ls-remote --tags`): 0.1.0 … 0.7.0, **1.0.0, 1.1.0, 1.2.0, 1.3.0,
1.4.0**. Laveste som tilfredsstiller `>=1.2,<2.0` er **1.2.0**.
**PREMISS FELT (1 av 2): «laveste 1.x» er ikke valgbar.** okf v0.8.5 pinner guarden i sin EGEN
`[tool.uv.sources]` til `tag = "v1.4.0"`, og uv behandler det som et direkte-URL-krav. Med po på
v1.2.0:
```
x Failed to resolve dependencies for `llm-ingestion-okf` (v0.8.5)
Requirements contain conflicting URLs for package `llm-ingestion-guard`
- ...pipeline-security.git@v1.4.0
- ...pipeline-security.git@v1.2.0
```
**PREMISS FELT (2 av 2): `rev =` og `tag =` er ULIKE URL-er for uv, selv med samme verdi.** Med po
på `rev = "v1.4.0"` — altså samme tag som okf — nekter uv fortsatt, og skriver ut to identiske
strenger som «conflicting». Først `tag = "v1.4.0"` resolverer. Konsekvensen for en framtidig bump
er en konkret skrivemåte, ikke et valg: **po må skrive `tag = "v1.4.0"`**, ikke `rev =`, og ikke en
lavere 1.x.
**Guardens brudd mot po sine fire importer (`Channel`, `Origin`, `format_log_entry`,
`import_bundle`).** Ingen. Guardens CHANGELOG `[1.0.0]` fryser den eksporterte flaten under semver
— «no name exported from `llm_ingestion_guard` is removed, renamed or given a different meaning
without a `2.0.0`», og målt før taggen: «four names added, none removed or renamed» siden 0.3.4.
**Men samme seksjon holder deteksjons-ATFERD utenfor frysen:** «Severities, thresholds, lexicon
entries and the dispositions they produce are calibration, and calibration moves in minor and patch
releases.» Det er nøyaktig det `ingest._ACCEPTED_DISPOSITION = "warn"` hviler på
(`ingest.py:235-239`). Målt utfall: `test_ingest_content_gate_loadbearing` sto GRØNN på guard 1.4.0,
så kalibreringen har ikke flyttet seg for de dokumentene den kjører — men det er en måling med en
smal nevner, ikke en garanti.
### 2b — rød test først (Iron Law)
`tests/test_okf_version_guard.py` skrevet mot målet v0.8.5/v1.2.0 og kjørt FØR noen produksjonskode
ble rørt: **3 failed / 2 passed** på dagens installasjon (de to installerte versjonene + `pyproject`-
halvdelen røde). Dermed er vakten bevist i stand til å bli rød.
### 2c — målingene i worktree
Alt under kjørt i `git worktree add --detach` (aldri i det sporede treet).
**Kontrollkjøring FØR bumpen, samme worktree:** 1579 passed / 3 failed / 5 skipped, der de tre røde
er vaktens egne. `1577 (STATE-basislinje) + 2 grønne vakt-armer = 1579`, altså er kontrollen
avstemt mot basislinjen.
> Én forstyrrelse måtte ryddes først, og den er en egenskap ved WORKTREET, ikke ved bumpen:
> `test_package_leaks_no_local_or_secret_files` har en KONTROLL på at `STATE.md` finnes lokalt
> (ellers kan gaten ikke diskriminere), og `STATE.md` er local-only, altså ikke i et worktree.
> Kopiert inn → 10/10 grønne. Uten den kopien ville nevneren vært 1 for lav.
**(i) Importerer alle navnene fortsatt? JA — 27 av 27.** Ordren sa «13 navn»; den målte nevneren er
27 fordelt på fem moduler (14 fra `llm_ingestion_okf`, 5 `…connectors`, 1 `…manifest`,
3 `…render`, 4 `llm_ingestion_guard.okf`). 0 manglende.
**(ii) Hele suiten: 1573 passed / 9 failed / 5 skipped.** IKKE grønn.
**(iii) Begge demo-goldener BYTE-UENDRET.** `ea8c534773acdbe41ae68f2c55724d69aaf8be4f` (stdout) og
`ede3e2f685ce6a14ad9888e9de421d1a66f6c611` (stderr) — `shasum -a 1` av INNHOLDET, ikke git-blob-id.
**(iv) De tre portene:** `ruff check --exclude scratchpad` → All checks passed! · `mypy src` →
Success, 37 filer · `ruff format --check` → 211 filer formatert, 1 ville reformateres, og det var
vaktfila jeg nettopp hadde skrevet (rettet i det sporede treet).
**(v) Door A-goldenen: ETT avvik, én linje, rad for rad.**
```diff
--- examples/ingest-golden-file/expected-bundle/ingest-costs.md
@@ -8 +8 @@
-generated: true
+generated: { by: process:okf-ingest, at: 2026-07-03T12:00:00Z }
```
Identisk diff i alle sju genererte konseptfiler. Dette er pre-V1 → V1-formen ordren forutså, og det
er samme flow-mapping guarden åpnet for i sin `1.2.0`.
Alle **ni** røde tester, med årsak:
| # | Test | Årsak |
|---|---|---|
| 1 | `test_ingest_golden::…bit_deterministic` | byte-diff, `ingest-costs.md` + `ingest-edge.md` |
| 2 | `test_ingest_golden_http::…` | byte-diff, `ingest-report.md` + `ingest-status.md` |
| 3 | `test_ingest_golden_sql::…` | byte-diff, `ingest-costs.md` + `ingest-meta.md` |
| 4 | `test_ingest_golden_mcp::…` | byte-diff, `ingest-cost-docs.md` |
| 5 | `test_ingest_loadbearing::test_every_generated_file_carries_the_provenance_layer` | `fm["generated"] == "true"` |
| 6 | `test_ingest_materialize::test_provenance_roundtrips_via_unchanged_parse_frontmatter` | samme assert |
| 7 | `test_ingest_mcp::test_mcp_source_materializes_a_bundle_through_the_http_family` | `"generated: true" in text` |
| 8 | `test_ingest_sql::test_sql_manifest_materializes_with_provenance` | `fm["generated"] == "true"` |
| 9 | `test_okf_version_guard::test_pyproject_pins_both_revisions` | vaktens egen konstant (v1.2.0 → v1.4.0) |
Åtte av ni har SAMME enkeltårsak. Alle sju golden-filene ligger under po sin egen `examples/` — **0
under `shared/`**, altså treffer en framtidig bump ikke den pull-only subtree-kontrakten.
### 2c′ — FUNNET som avgjør: forfalskningsvakten går INERT, og suiten sier ingenting
`okf._carries_complete_ingest_stamp` (`okf.py:1239`) er halvdelen av invarianten «kuraterte skrivere
kan ikke forfalske ingest-stempelet»: `write_concept_file` reiser `IngestStampError` når frontmatter
bærer BEGGE halvdeler av eierskaps-stempelet. `generated`-halvdelen leses mot
`_YAML_TRUE_LITERALS = {"true", "yes", "on"}`.
Målt på guard 1.4.0 / okf 0.8.5:
```
pre-V1 form ({"generated": "true", ...}) -> True
V1 flow-mapping ({"generated": "{ by: process:okf-ingest, at: ... }"}) -> False
```
Emitteren skriver nå nøyaktig den formen vakten IKKE gjenkjenner. Konsekvensen er at en kuratert
skriver kunne skrevet `generated: { by: … } + ingest_manifest` og **ikke blitt nektet** — vakten
slutter å vokte. Og `test_ingest_stamp_fail_closed_loadbearing.py` sto **GRØNN** gjennom hele
bump-kjøringen, fordi den bruker literalformene. Dette er ordrett den fella invariant-raden i
CLAUDE.md ble skrevet for: *«en fremtidig `uv sync` mot en skrivemåte som `yes`/`on` ville latt
vakten slutte å vokte uten én lokal diff»* — bare med en annen skrivemåte enn den forutså.
Dette er ikke «et avvik å navngi rad for rad». Det er en gate som slutter å gate, og den er ikke
dekket av noen test.
### 2d — BESLUTNING: **INGEN BUMP**
(ii) er rød, og 2c′ er den ekte grunnen: å lande bumpen nå ville shippet en avvæpnet
sikkerhets-invariant med suiten grønn på nøyaktig den sømmen. Ordrens regel er fulgt
(«Er noe rødt: INGEN bump»), og begge utfall er godkjent leveranse.
**Hva som må endres i po FØR bumpen kan landes — komplett, målt liste:**
1. **`okf._carries_complete_ingest_stamp` + `_YAML_TRUE_LITERALS`** må gjenkjenne V1-flow-mapping-
formen i tillegg til literalene. **FØRST** — dette er den eneste raden som er sikkerhetsbærende.
2. **`tests/test_ingest_stamp_fail_closed_loadbearing.py`** utvides med en mutasjon for den nye
formen, ellers gjentar raden 1 seg ved neste emitter-endring.
3. **7 golden-konseptfiler** regenereres (1 linje hver): `examples/ingest-golden-file/expected-bundle/`
`ingest-costs.md`, `ingest-edge.md` · `…-http/…/ingest-report.md`, `ingest-status.md` ·
`…-sql/…/ingest-costs.md`, `ingest-meta.md` · `…-mcp/…/ingest-cost-docs.md`. Regenerering er en
BESLUTNING, ikke opprydding — de pinner formen Door A ble målt mot.
4. **4 tester** som asserterer `generated == "true"`: `test_ingest_loadbearing`,
`test_ingest_materialize`, `test_ingest_mcp`, `test_ingest_sql`.
5. **2 prosa-steder** som navngir den gamle formen: `ingest_mcp.py:29`, `okf.py:1278`.
6. **`pyproject.toml`**: okf `rev = "v0.8.5"` OG guard `tag = "v1.4.0"` — `tag=`, ikke `rev=`, og
ikke v1.2.0 (§ 2a).
7. **`pyproject.toml:40`-kommentaren** («zero runtime deps») er allerede usann for v0.8.5 og må
rettes i samme trekk: okf-kjernen har nå ÉN runtime-avhengighet, guarden.
8. **`tests/test_okf_version_guard.py`**: pinnene og kostnads-meldingene oppdateres.
Punkt 7 er IKKE rettet nå. Kommentaren beskriver den pinnede v0.3.2, som faktisk har null runtime-
avhengigheter; å skrive om den mens pinnen står ville gjort den usann i den andre retningen.
**Leveransen fra DEL 2 er `tests/test_okf_version_guard.py`**, som nå pinner det MÅLTE (0.3.2/0.3.4)
i to halvdeler — installert distribusjon + `pyproject` — og hvis nekt-meldinger NAVNGIR kostnaden
over, slik at neste økt ikke løfter pinnen uten å re-måle.
Fire mutasjoner, hver med sin egen signatur, alle røde, deretter restaurert grønn:
| # | Mutasjon | Utfall |
|---|---|---|
| M1 | okf-asserten reiser aldri | 1 rød (nekt-armen) |
| M2 | okf-asserten reiser alltid | 1 rød (installert-kontrollen) |
| M3 | guard-asserten reiser aldri | 1 rød (guardens nekt-arm) |
| M4 | pinne-konstanten drives til `0.3.3` | **2 røde** — installert-armen OG `pyproject`-armen, altså er avledningen levende og ikke to literaler |
Sporet tre: **1582 passed / 5 skipped** (fra 1577/5, strengt supersett, 0 fjernet) · begge goldener
byte-uendret · `ruff check` All checks passed · `ruff format` 212 filer · `mypy` 37 filer.
---
## § 3 — R761 gratis-navigasjon (DEL 3)
### 3a — målingene
`portfolio_optimiser.okf.navigate_bundle`, tre kjøringer hver, `/usr/bin/time -l`. po hadde ALDRI
sett R761 før denne økten (`grep -rln "R761\|r761" docs src tests` = 0).
| Base | Navigasjon (s) | Prosess-real (s) | Maks RSS (MB) | `files` | `context_files` | `verdicts` | `skipped` |
|---|---|---|---|---|---|---|---|
| `n100-2023` | 0,219 · 0,201 · 0,210 | 2,01 · 2,03 · 2,06 | 110,9 · 111,2 · 111,4 | 450 | 446 | 0 | **0** |
| `r761-2025` | 8,580 · 6,889 · 7,108 | 10,39 · 8,59 · 9,03 | 131,2 · 131,1 · 131,9 | 5 514 | **2 756** | 0 | **0** |
Prosess-real inkluderer ~1,8 s tolkerstart (`uv run python`); navigasjonstallet er `perf_counter`
rundt `navigate_bundle` alene.
**Avstemming av nevneren.** R761 på disk: 2 758 `index.md` + 2 756 andre `.md` = 5 514 filer i
2 758 kataloger. `navigate_bundle` når alle 5 514 og hopper over **null** lenker;
`context_files` = 2 756, altså faller nøyaktig de 2 758 nestede/rot-indeksene bort (navigasjon, ikke
innhold), og `verdicts` = 0. Ordrens «2 757 seksjoner» = de 2 757 underkatalogene; den målte
konsept-tellingen er **2 756**.
**Leseretning.** R761 er ~12× N100 i filer og koster ~34× i navigasjonstid (7,1 s mot 0,21 s) men
bare **+18 % RSS** (131 mot 111 MB). Tiden er I/O-dominert (sys-tid 2,7–3,1 s mot N100s 0,35 s;
2 758 katalog-traverseringer), ikke minne. Ingen av dem er i nærheten av et problem for en gratis
oppstart: verste målte navigasjon av R761 er 8,6 s, én gang per verktøykall.
**Ikke målt her, og det er den interessante kostnaden:** hva R761 koster i TOKENS gjennom stigen
(`list_bundles` → `read_bundle` → `read_dir` → `read_file`). S7a-3 målte K2s 629 konsepter til
42 761 o200k-tokens i flat form, og hierarkiet tok rota ned til 1 495. R761 har **4,4× så mange
konsepter som K2**, og det tallet er ikke tatt.
### 3b — hvordan fire bundler gis til ÉN kjøring i dag
**Målt flate, ikke forslag:**
- `--bundle-dir` tar **ÉN** katalog (`run.py:2444`). CLI-en mater utforskningen med en 1-tuppel:
`bundle_dirs=(args.bundle_dir,)` (`run.py:3606`) — det er det eneste `bundle_dirs=`-kallstedet i
hele `run.py`.
- `run_mandate_across_bundles` (`run.py:2124`) TAR N baser og partisjonerer et mandat over dem via
`mandate.route_by_bundle`, men har **ingen CLI-flate**: ingen kaller i `main()`, kun tester og
`explore.py`-prosa refererer den. Dette er den bevisste grensen fra multi-base-raden.
- `--docs-dir` er en ANNEN søm: den mater `retrieve_chunks`/`make_retrieval_tool`
(`run.py:1194`/`1204`), altså keyword-chunk-henting — ikke OKF-navigasjon. På bundle-stien settes
`docs_dir=bundle_dir` (`run.py:678`, `:2263`).
- Repeterbart `--bundle-dir` er **NEI** i STATE og bygges ikke her.
**`--mandate`-JSON-formatet** (`run.py:2459`, `mandate.py`) — utgangspunktet for «hva man ønsker å
optimalisere på»:
```json
{
"objective": "<fri prosa: hva kjøringen er til for>",
"allow_own_proposals": true,
"success_criteria": ["<fri prosa>", "..."],
"approaches": [
{
"id": "<unik, ikke 'own-proposal'>",
"label": "<ekspertens ord; blir SavingsProposal.measure VERBATIM>",
"description": "<ekspertens begrunnelse; går ordrett i proposer-prompten>",
"affected_codes": ["<kostkode>", "..."],
"claimed_saving_nok": 0,
"bundle_id": "<rutingsnøkkel; tom = 'ingen base navngitt'>"
}
]
}
```
`Approach.bundle_id` ER multi-base-nøkkelen og finnes allerede, default `""`. Tallmålet hører IKKE
hjemme her — det bor i `contracts.GoalContract` / `--goals`.
**Hva P14 må VELGE mellom (ikke avgjort her):**
| Alternativ | Hva det koster | Hva det kjøper |
|---|---|---|
| **(a) Én bundle per kjøring, fire kjøringer** | Fire `run_id`-er, fire utbokser, ingen kryss-base-læring i én `VerdictStore` med mindre en kaller tråder den; ingen kodeendring | Virker I DAG, uendret CLI. Den eneste som er nåbar uten ny flate. |
| **(b) `--docs-dir` for «resten»** | Er en ANNEN mekanisme — keyword-chunks, ikke navigasjon; §4.1a-dimensjonsgaten og verdict-gaten sitter på navigatør-verktøyene, ikke på chunk-verktøyet | Billig å skrive. **Men den omgår stigen og to gater — jeg fraråder den, og den er uprøvd.** |
| **(c) CLI-flate for `run_mandate_across_bundles`** | NY operatørflate (repeterbart `--bundle-dir` eller `--bundle-dirs`), altså en egen beslutning STATE i dag sier NEI til; dispatchen er sekvensiell, N kjøringer trenger N `run_id`-er, og outboxen er ikke wiret | Den ENESTE som gir ett mandat rutet over fire baser med én delt `VerdictStore`. Motoren finnes ferdig og er load-bearing-testet; det som mangler er argparse + `run_id`-mynting. |
P14 skriver kontekstsettet; valget mellom (a) og (c) er operatørens, og (c) er en bygge-ordre —
ikke noe denne økten har mandat til.
---
## § 4 — Honesty limits
1. **Bumpen er ikke prøvd mot et EKTE ingest-kjør** utover suitens fixturer. At okf 0.8.5 oppfører
seg riktig på et levende manifest er ikke vist.
2. **Guard-kalibreringen** (`_ACCEPTED_DISPOSITION = "warn"`) er bekreftet kun gjennom
`test_ingest_content_gate_loadbearing`s egne dokumenter. Guardens egen CHANGELOG sier at
dispositions er fri til å flytte i enhver 1.x; en bredere måling er ikke gjort.
3. **De sju golden-filene er TALT, ikke regenerert.** At regenereringen gir nøyaktig den ene
linje-endringen i alle sju er målt for `ingest-golden-file` (to filer, direkte diff) og utledet
for de fem andre fra deres feilmeldinger — http/sql/mcp-goldenene kan ikke materialiseres utenfor
testenes stubber (de krever `PORTEFOLJE_SQL_DSN` / nettverks-opt-in).
4. **R761-tallene er ÉN maskin, tre kjøringer, varm filsystem-cache.** Første R761-kjøring var
8,58 s mot 6,89/7,11 s for de to neste; spredningen er oppgitt, ikke bortsnittet.
5. **`Bundle.skipped` = 0 på begge baser** betyr at hver kryss-lenke ble fulgt — ikke at basene er
komplette. Det er et utsagn om navigasjonen, ikke om innholdet.
6. **Ingen modellkall, ingen Azure, NOK 0.** Ingenting her sier hva en modell gjør med R761.
7. **§ 3.2 (b) er frarådet uten å være målt.** Begrunnelsen er kodelesning (gatene sitter på
navigatør-verktøyene), ikke en kjøring.
---
## § 5 — Reproduksjon
```bash
# DEL 1
git log --oneline origin/main..main | wc -l
ls ~/.claude/coord/portfolio-optimiser/inbox/*.md | wc -l
grep -rn "parse_frontmatter" src tests | wc -l
grep -rn "llm_ingestion_okf" src | wc -l
grep -rn "import yaml\|yaml.safe_load\|yaml.load" src tests | wc -l
# DEL 2a
grep -n "llm-ingestion-okf\|llm-ingestion-guard" pyproject.toml uv.lock
git -C ~/repos/llm-ingestion-okf show v0.8.5:pyproject.toml | grep -A2 "tool.uv.sources"
git ls-remote --tags https://git.fromaitochitta.com/open/llm-ingestion-pipeline-security.git
# DEL 2b (rød FØRST, på dagens pinne -- men vaktfila er nå oppdatert til å pinne 0.3.2/0.3.4,
# så re-kjøring i dag er GRØNN; RED-beviset var mot v0.8.5/v1.2.0-konstantene)
uv run pytest tests/test_okf_version_guard.py -q
# DEL 2c (worktree -- ALDRI i det sporede treet)
git worktree add --detach /tmp/po-wt HEAD
cp STATE.md /tmp/po-wt/STATE.md # handover-gatens egen kontroll trenger den
cd /tmp/po-wt
uv sync && uv run pytest -q # kontroll: 1579/3/5
sed -i '' 's|okf.git", rev = "v0.3.2"|okf.git", rev = "v0.8.5"|' pyproject.toml
sed -i '' 's|security.git", rev = "v0.3.4"|security.git", tag = "v1.4.0"|' pyproject.toml
uv lock && uv sync && uv run pytest -q # etter: 1573/9/5
shasum -a 1 tests/golden/demo-transcript.stdout tests/golden/demo-transcript.stderr
uv run ruff check . --exclude scratchpad && uv run mypy src
# DEL 2c' -- funnet
uv run python -c "
from portfolio_optimiser.okf import _carries_complete_ingest_stamp as f
print(f({'generated':'true','ingest_manifest':'m.json'}))
print(f({'generated':'{ by: process:okf-ingest, at: 2026-07-03T12:00:00Z }','ingest_manifest':'m.json'}))"
# DEL 3a
for b in n100-2023 r761-2025; do for i in 1 2 3; do
/usr/bin/time -l uv run python - "$HOME/repos/vegnormal-okf/build/ferdig/$b" <<'PY'
import sys, time
from portfolio_optimiser import okf
t0 = time.perf_counter(); b = okf.navigate_bundle(sys.argv[1]); dt = time.perf_counter() - t0
print(f"WALL={dt:.3f}s FILES={len(b.files)} CONTEXT={len(b.context_files)} "
f"VERDICTS={len(b.verdicts)} SKIPPED={len(b.skipped)}")
PY
done; done
# DEL 3b
grep -n '"--bundle-dir"\|"--docs-dir"\|"--mandate"' src/portfolio_optimiser/run.py
grep -n "bundle_dirs=" src/portfolio_optimiser/run.py
grep -rn "run_mandate_across_bundles" src tests
```

View file

@ -1,280 +0,0 @@
# P13b — okf-bumpen v0.3.2 → v0.8.5 LANDET, stempelvakten lukket, blokk-`sources` lest
Dato: 2026-09-12. Ordre `20260912T195112Z-995611104-from-.claude` (operatørsvar på P13-E).
Forutgående måling: [`docs/2026-09-12-p13-okf-pin-r761.md`](2026-09-12-p13-okf-pin-r761.md).
**Ingen modellkall, ingen Azure, NOK 0.**
---
## § 0 — Hva som ER målt og hva som IKKE er det
**Målt i denne økten**
| # | Påstand | Hvordan |
|---|---|---|
| 1 | (P1) Stempelvakten var inert på V1-formen | direkte kall på `_carries_complete_ingest_stamp`: `true`→True, `yes`→True, `{ by: …, at: … }`→**False** |
| 2 | (P2) Alle fire bundler skriver `sources` i BLOKK-form | `grep -rl`, hele nevneren: **4605 av 4605**, 0 flat |
| 3 | Evidens-tilstand FØR: `unreadable` på 4605 av 4605 | `evidence_for` over `context_files` i alle fire |
| 4 | Evidens-tilstand ETTER: `present` på 4605 av 4605, 4605 oppføringer | samme kjøring |
| 5 | 27/27 importerte navn resolverer på okf 0.8.5 / guard 1.4.0 | `hasattr` over fem moduler |
| 6 | Suite 1582 → **1606 passed / 5 skipped** | `uv run pytest -q` |
| 7 | Begge demo-goldener byte-uendret | `shasum -a 1` av INNHOLDET |
| 8 | De sju golden-konseptfilene stemmer mot materialisatoren | de fire `test_ingest_golden_*`-suitene grønne |
| 9 | Seks mutasjoner, alle røde, hver med egen signatur | § 4 |
**IKKE målt**
- **Ingen levende modell, ingen betalt kjøring.** Ingenting her sier hva en modell gjør med en
base hvis proveniens nå er lesbar.
- **Ingen ekte ingest-kjøring mot et levende manifest** utover suitens fixturer og stubber.
- **Guard-kalibreringen** (`_ACCEPTED_DISPOSITION = "warn"`) er bekreftet kun gjennom
`test_ingest_content_gate_loadbearing`s egne dokumenter — guardens CHANGELOG holder dispositions
eksplisitt utenfor semver-frysen, så et bredere korpus er uprøvd.
- **De fem http/sql/mcp-golden-linjene ble AVLEDET, ikke materialisert** — se § 2.3.
- **Den nye lesertilstanden er ikke wiret inn i `run_project`.** `evidence_for` er fortsatt en
bibliotek-primitiv (B4-regelen, urørt); at 4605 dokumenter nå har en adresse endrer ingen kjøring
av seg selv.
---
## § 1 — Premissene, målt før noe ble bygget
### P1 — stempelvakten var inert
```
literal true : True
yes : True
V1 flow : False <-- formen okf >=0.8.5 faktisk skriver
```
### P2 — alle fire bundler er blokkform, hele nevneren
| Base | Konsepter | `sources`-nøkkel | BLOKK | FLAT |
|---|---|---|---|---|
| n100-2023 | 446 | 446 | **446** | 0 |
| n200-2024 | 1133 | 1133 | **1133** | 0 |
| n500-2024 | 270 | 270 | **270** | 0 |
| r761-2025 | 2756 | 2756 | **2756** | 0 |
| **SUM** | **4605** | **4605** | **4605** | **0** |
Ordren oppga «400 av de 400 første i hver». Den fulle nevneren er målt her og stemmer med tallet
`llm-ingestion-okf` selv oppga i sin v0.8.5-FYI (2756 + 446 + 1133 + 270 = 4605).
---
## § 2 — Utført, rad for rad
### 2.1 Rad 1 — stempelvakten (sikkerhetsbærende, FØRST)
**Rød først:** fire nye armer i `tests/test_ingest_stamp_fail_closed_loadbearing.py`; to av dem
røde mot den uendrede vakten (de to andre beskriver uendret oppførsel og var grønne, som de skal).
`_claims_ingest_ownership` er ny og er hele endringen: predikatet utvides fra «leses som boolsk
True» til «hevder ingest-eierskap», der booleanen er pre-V1-stavemåten.
**Gjenkjenneren for den nye halvdelen er `decode_flow_value`** — modulens ENE flow-dekoder, aldri en
andre kopi (kø-(p), og samme argument `write_concept_file` alt gjør for `verified`: skriveren nekter
nøyaktig det leseren kan lese). En verdi dekoderen REFUSERER er derfor ikke et eierskapskrav og
skrives gjennom — det er dét som holder regelen fra å kollapse til «enhver ikke-tom `generated`».
Ingen YAML-bibliotek innført (po har fortsatt 0 `import yaml`).
### 2.2 Rad 6–7 — pinnene
```toml
llm-ingestion-okf = { git = "…/llm-ingestion-okf.git", rev = "v0.8.5" }
llm-ingestion-guard = { git = "…/llm-ingestion-pipeline-security.git", tag = "v1.4.0" }
```
`tag =`, ikke `rev =`, og ikke 1.2.0 — begge P13-premissene står, og begrunnelsen er nå skrevet inn
i `pyproject.toml` ved siden av pinnen. `pyproject.toml:40`-kommentaren er rettet: okf-kjernen har
ÉN runtime-avhengighet, guarden, og det er dét som binder de to linjene sammen.
Etter `uv lock && uv sync`: okf **0.8.5** (`64661c7`), guard **1.4.0** (`d19de8c`), **27/27** navn.
### 2.3 Rad 3–5 og 8 — goldenene, assertene, prosaen, vakten
**De to `ingest-golden-file`-filene ble regenerert av den EKTE materialisatoren** og diffen er én
linje hver:
```diff
-generated: true
+generated: { by: process:okf-ingest, at: 2026-07-03T12:00:00Z }
```
**De fem øvrige ble AVLEDET** (http/sql/mcp kan ikke materialiseres utenfor testenes stubber — de
krever `PORTEFOLJE_SQL_DSN` respektive nettverks-opt-in), med hver bundles egen `ingested-at.txt`
som `at`-verdi. **Avledningen er deretter MÅLT, ikke antatt:** alle fire `test_ingest_golden_*`
sammenligner byte for byte mot det stubbene faktisk materialiserer, og alle fire er grønne. Hadde
tidsstempelet eller formen vært feil ett eneste sted, ville suiten stått rød.
De fire assertene på `generated == "true"` leser nå **ÉN kilde**: `conftest.expected_generated_stamp
(ingested_at)`. Fire literaler for ett emitter-faktum er fire steder en senere okf-utgivelse kan
etterlate halvrettet — det er nøyaktig slik pre-V1-formen overlevde i fire asserts til P13 målte
den. `tests/test_okf.py`s `generated: true` er BEVISST urørt: den runder en KURATERT halv-stempel
gjennom po sin egen skriver og er et annet faktum enn hva okf emitterer.
Prosa: `ingest_mcp.py:29`, `okf.py`s nekt-melding. Vakten (`tests/test_okf_version_guard.py`) pinner
nå 0.8.5 / 1.4.0, og nekt-meldingen er skrevet om fra «her er kostnaden ved å løfte» til «re-mål hva
denne utgivelsen EMITTERER som §7-stempel, og sjekk at `_claims_ingest_ownership` ser det» — fordi
en tredje stavemåte ville gjort vakten inert igjen uten at én test ble rød.
### 2.4 Steg 4 — blokk-`sources`-leseren
`okf.decode_block_mappings` er ny og er den ANDRE bæreren av ÉN grammatikk, aldri en andre
grammatikk: par-separatoren er kolon-MELLOMROM (`_find_pair_separator`), navn og verdier gjennom
`unquote_scalar`, duplikate nøkler nektet, og §5.2-regelen (en `verified`-oppføring må navngi en
aktør) gjelder her også. okf sin `consume.read_sources` ble LEST for formen og ikke kalt — po kaller
ingen okf-leser, som er målt og bevisst (P13 § 1a).
**To nekter er BEHOLDT og er filens diskriminatorer:** `block-mapping` (ingen `- `-oppføring åpnet)
og `unsupported-flow`. Uten dem ville en leser utvidet til «alt innrykket er oppføringer» bestått
hver positiv arm mens den fant på oppføringer dokumentet ikke har.
| Base | FØR | ETTER |
|---|---|---|
| n100-2023 (446) | 446 `unreadable` / 0 oppføringer | **446 `present` / 446 oppføringer** |
| n200-2024 (1133) | 1133 `unreadable` / 0 | **1133 `present` / 1133** |
| n500-2024 (270) | 270 `unreadable` / 0 | **270 `present` / 270** |
| r761-2025 (2756) | 2756 `unreadable` / 0 | **2756 `present` / 2756** |
| **SUM (4605)** | **4605 / 0** | **4605 / 4605** |
Fixturen er en **byte-identisk kopi av en EKTE levert konseptfil**
(`tests/fixtures/p13b-block-sources/n100-krav-4-2-5-1-3.md`, `shasum -a 1` `8c2952d1…`, lest fra
`vegnormal-okf`, aldri skrevet der). En håndskrevet tilnærming ville bevist at leseren håndterer den
formen jeg forestilte meg.
**Lesing er ikke lov til å SKRIVE:** `materialize._render_sources` i okf emitterer fortsatt flow,
og `write_concept_file`/`verified_field` nekter fortsatt nøyaktig det `decode_flow_value` nekter —
skriveren nekter altså fortsatt det leseren ikke kan lese, som er egenskapen rundturs-gaten finnes
for.
---
## § 3 — Tre funn under arbeidet
### F1 — min egen leser fant på data (funnet ved å måle, ikke av en rød arm)
Første blokk-leser dekodet `- { id: a, resource: x }` — en blokk-sekvens hvis ITEMS er FLOW-mappinger,
SPEC-kanonisk og nøyaktig formen `tests/golden/block-form-provenance` skriver for `verified` — som
`{'{ id': 'a, resource: x }'}`. **En nøkkel som ikke er en nøkkel**, altså «oppfinn en oppføring
dokumentet ikke har»-feilen min egen docstring forbyr. **Ingen arm fanget den:** på `verified` nektet
§5.2-regelen den av en helt annen grunn, så fixturen som bærer formen var skjermet ved et uhell.
Lukket med en egen gren som sender item-et gjennom `decode_flow_value`, og med fire nye armer
(dekoding, rekkefølge, blandet bærer nektet, malformet item nektet). M3 er mutasjonen.
### F2 — en av mine egne armer var vakuøs, funnet av min egen mutasjon
`test_a_value_carrying_a_colon_is_split_on_colon_space_only` påsto å bevise kolon-MELLOMROM-regelen.
**M5 (separator → FØRSTE kolon) lot den stå GRØNN**: første kolon i `resource: https://…` og i
`title: N100:2023` ER den kolon-mellomrom finner, så de to reglene er enige om hver verdi formet som
en levert. Armen som faktisk skiller dem er `test_an_item_with_no_pair_separator_is_refused` — under
første-kolon dekodes `- https://a.example/d` til `{'https': '//a.example/d'}`, en nøkkel oppfunnet av
et URL-skjema. Armen er BEHOLDT som regresjonsvakt og OMDØPT + merket ærlig; kolon-MELLOMROM-påstanden
er flyttet til armen som bærer den.
### F3 — commons-eksempelet erklærer nå noe som er usant for po
`shared/skills/falsification-reviewer/references/example-evidence.json` (commons, **pull-only**)
erklærer konsept 2 som `state: unreadable, reason: block-sequence, items_seen: 2` for en SPEC §5.1
blokk-sekvens. po leser den nå som to oppføringer. **Divergensen kan ikke lukkes herfra** — det
krever et commons-amendment.
`test_the_worked_example_round_trips_through_the_real_readers` er skrevet om til å **asserte
divergensen i stedet for å hoppe over den**, og beholder den diskriminerende halvdelen: eksempelet
sier to oppføringer ble sett, og po sin leser returnerer nøyaktig to — så en leser som mistet en
oppføring, beholdt én, eller fant på en nøkkel som ikke er en nøkkel, faller fortsatt her.
**Dette er en åpen sak for operatøren, ikke lukket av denne økten.**
---
## § 4 — Mutasjonene
Alle mot HELE suiten, én per kjøring, restaurert fra scratchpad + `shasum -c`.
Grønn kontroll: **1606 passed / 5 skipped**, begge goldener byte-uendret.
| # | Mutasjon | Røde | Signatur |
|---|---|---|---|
| M1 | `_claims_ingest_ownership` tilbake til literalene | **2** | KUN de to nye V1-stempel-armene — altså er rad 1 gatet av seg selv |
| M2 | blokk-leseren frakoblet `read_provenance` | **17** | hele blokk-lesersuiten + fem eldre B4-armer, altså uavhengige vitner |
| M3 | flow-mapping-item faller til par-løkka (F1-defekten) | **7** | de fire nye flow-item-armene + tre eldre |
| M4 | en frittstående innrykket linje folder inn i en OPPFUNNET oppføring | **4** | begge `block-mapping`-armene + to eldre dekoder-armer |
| M5 | separator = FØRSTE kolon i stedet for kolon-MELLOMROM | **1** | bare-skalar-armen ALENE — og dét er F2 |
| M6 | `expected_generated_stamp` tilbake til literalen `"true"` | **4** | nøyaktig de fire ingest-assertene som leser den ene kilden |
---
## § 5 — Suite-regnskap
| | Node-ider | Kommentar |
|---|---|---|
| Før P13b | 1582 passed / 5 skipped | P13s kontroll |
| Etter | **1606 passed / 5 skipped** | +24 |
De 24: +4 V1-stempel-armer · +18 blokk-leser-armer · +2 B4-armer
(`test_an_unreadable_document_yields_NO_tier`, `test_the_committed_block_form_fixture_is_now_READ`).
**Ikke et strengt supersett — tre node-ider er OMDØPT, ingen fjernet i substans:**
| Gammelt navn | Nytt navn | Hvorfor |
|---|---|---|
| `test_a_block_form_document_is_unreadable_and_says_WHY` | `test_an_undecodable_document_is_unreadable_and_says_WHY` | spesimenet måtte flytte; påstanden er uendret |
| `test_a_two_entry_block_document_yields_NO_tier` | `test_a_two_entry_block_document_keeps_the_human_sign_off` | påstanden er nå den fixturen ble AUTORERT for |
| `test_a_value_carrying_a_colon_is_split_on_colon_space_only` | `test_a_value_carrying_a_colon_survives_whole` | F2 |
**Ni eksisterende armer er skrevet om, ingen svekket.** Alle ni pinnet «blokkformen er uleselig» —
nøyaktig oppførselen ordren endrer. Hver av dem beholder sin påstand og har fått enten (a) et
spesimen som fortsatt er uleselig av en grunn av sitt eget (SPEC §5.2: en oppføring som navngir ingen
aktør, som holder både reason-tokenet `block-sequence` og et valgbart antall), eller (b) den motsatte
påstanden pinnet der den gamle sto, så retningsendringen ikke kan være stille. To av dem ble
STERKERE: `multi-verified.md` ble autorert for «en leser som beholder siste oppføring rapporterer
maskin-bekreftet for et konsept et menneske signerte», og den påstanden kunne ikke testes så lenge
formen var uleselig — nå asserteres rekkefølgen og `human-reviewed`.
---
## § 6 — Honesty limits
1. **Ingen betalt kjøring, ingen levende modell.** At 4605 dokumenter nå har en lesbar adresse er en
egenskap ved leseren, ikke et bevis på at noen kjøring blir bedre.
2. **De fem avledede golden-linjene** er verifisert av testene mot stubbene, ikke av en materialisering
jeg kjørte selv.
3. **F3 er ÅPEN.** Commons-eksempelet erklærer fortsatt noe usant for po, og `shared/` er pull-only.
4. **`evidence_for` er ikke wiret inn i kjørestien.** B4-regelen står: systemet leser, kalleren
avgjør. Ingen `run_project`-oppførsel endres av denne økten.
5. **Guard 1.4.0s kalibrering** er bekreftet på en smal nevner (§ 0).
6. **`_claims_ingest_ownership` gjenkjenner to stavemåter.** En TREDJE ville gjøre vakten inert igjen,
og ingen test i suiten ville bli rød — det er dét vaktens nekt-melding nå sier høyt.
7. **Blokk-formen er LEST, aldri SKREVET.** po emitterer fortsatt ingen blokkform, og skriverne
nekter den uendret.
---
## § 7 — Reproduksjon
```bash
# premissene
uv run python -c "
from portfolio_optimiser.okf import _carries_complete_ingest_stamp as f
print(f({'generated':'true','ingest_manifest':'m'}), f({'generated':'{ by: a, at: b }','ingest_manifest':'m'}))"
B=~/repos/vegnormal-okf/build/ferdig
for b in n100-2023 n200-2024 n500-2024 r761-2025; do
echo "$b konsepter=$(find $B/$b -name '*.md' ! -name index.md | wc -l) \
blokk=$(grep -rl -E '^sources:[[:space:]]*$' $B/$b --include='*.md' | grep -vc '/index.md$') \
flat=$(grep -rl -E '^sources:[[:space:]]*\[' $B/$b --include='*.md' | grep -vc '/index.md$')"
done
# evidens-tilstand foer/etter (samme skript, kjoert paa hver side av endringen)
# se docs/2026-09-12-p13-okf-pin-r761.md for navigasjonsmaalingen
# bumpen
uv lock && uv sync
uv run python -c "import importlib.metadata as m; print(m.version('llm-ingestion-okf'), m.version('llm-ingestion-guard'))"
# portene
uv run pytest -q # 1606 passed / 5 skipped
uv run ruff check . --exclude scratchpad
uv run ruff format --check . --exclude scratchpad
uv run mypy src
shasum -a 1 tests/golden/demo-transcript.stdout tests/golden/demo-transcript.stderr
```

View file

@ -1,346 +0,0 @@
# P14 — kontekstsettet for stresstesten
**Ordre `20260912T202210Z-7590723260-from-.claude`, økt 118, 2026-09-12.**
Operatørens valg 12.09, ORDRETT fra `~/.claude/docs/2026-09-12-ukesgrunnlag-uke39.md § Operatørens
valg 12.09`: «**Tredje lesning:** D-1 står; mål, bundle-valg og all kontekst gis til po per
prosjektkjøring (se § 1). Rad 2 + 3, ikke 3′». Ordren leser dette som **P14-valg (a)** — én bundle
per kjøring, fire kjøringer — og fører **(c)**, CLI-flate for `run_mandate_across_bundles`, som en
EGEN bygge-ordre etter første stressrunde. (c) er IKKE bygget her.
Ingen modellkall, ingen Azure. Alt under er målt mot disken, ikke antatt.
---
## 1. Formen (DEL 1)
Ett kontekstsett = én katalog under `contexts/<prosjekt-id>/`:
| Fil | Rolle | Leses av |
|---|---|---|
| `mandate.json` | kommisjonen — `Mandate`-skjemaet ORDRETT (`mandate.py`) | `load_mandate` (fail-fast) |
| `bundle.txt` | hvilken kunnskapsbase settet hører til, og hvilken id den erklærer | testen + operatøren |
| `fasit.json` | fasiten: hva et riktig svar MÅ peke på, hva som IKKE kan besvares, og ærlighetsfeltet | testen + operatøren etter kjøringen |
| `docs/` | 0–n syntetiske prosjektdokumenter | *ingen i dag* — se § 1.4 |
### 1.1 `mandate.json` — ingen ny form
Filen er `Mandate` slik `mandate.py` allerede definerer den. Ingen felt er funnet på:
`objective`, `success_criteria`, `approaches[]` med `id`/`label`/`description`/`affected_codes`/
`claimed_saving_nok`/`bundle_id`. `allow_own_proposals` står på sin default (`true`), fordi
kravet er «disse **og/eller** dine egne» og den permissive halvdelen er dagens oppførsel.
`bundle_id` settes EKSPLISITT på hver approach, også når settet bare har én base. Med én base ville
`route_by_bundle` løst et tomt felt til den ene basen uansett (`sole`-regelen), så feltet er
strengt tatt valgfritt her — men det er nøyaktig det feltet **(c)** kommer til å rute på, og et sett
som allerede bærer det er dispatchbart uendret den dagen (c) finnes. Å la det stå tomt nå ville
betydd fire redigeringer da.
### 1.2 `bundle.txt` — symbolsk navn, aldri en absolutt sti
To linjer, `nøkkel: verdi`:
```
name: n500-2024
bundle_id: vegnormal-n500-2024
```
**Symbolsk navn, ikke absolutt sti** — ordren tillater begge. En absolutt sti ville pinnet settet til
én maskins hjemmekatalog i et repo som publiseres på `open/`, og `git archive HEAD`-pakka bærer
tracked filer (Fase 5-invarianten), altså ville stien fulgt med ut. Navnet resolveres mot
`PORTFOLIO_VEGNORMAL_ROOT`, som defaulter til `~/repos/vegnormal-okf/build/ferdig`.
`bundle_id` er DEKLARERT i fila fordi den gjør DEL 3(d) målbar **uten** basen til stede: testen kan
sammenligne mandatets `bundle_id` mot settets erklæring i enhver checkout. Når basen ER til stede,
verifiseres erklæringen i tillegg mot basens egen `okf.reconcile_bundle_id` — så erklæringen kan
ikke drifte fra basen i stillhet.
### 1.3 `fasit.json` — ÉN maskinlesbar kilde, ikke `fasit.md`
Ordren foreslår `fasit.md` («f.eks.»). **Avvik, uttalt:** fasiten er `fasit.json`. Grunnen er
kø-(p): testen må lese fasit-id-ene, og en prosa-fil ved siden av en maskinlesbar ville vært to
kopier av ett faktum, fri til å drifte. Prosaen bor derfor INNI JSON-en (`rationale`, `honesty`,
`question`), så mennesket og testen leser den samme fila.
```jsonc
{
"project_id": "...",
"bundle": "n500-2024",
"must_cite": [ // per approach: hva et riktig svar MÅ peke på
{"approach_id": "...", "rationale": "...",
"concepts": [{"path": "krav/N500/id-….md", "title": "…", "ref": "Krav 10.4.3—2"}]}
],
"unanswerable": [ // falsifiseringsgaten
{"question": "…", "anchors": ["enhetspris"], "expected": "ubesvart — riktig svar er å si det"}
],
"honesty": "…" // DEL 2(iii)
}
```
`path` er **bundle-relativ**, altså nøyaktig den identifikatoren `read_file(bundle_id, path)` tar.
Det er den eneste formen som er både grep-bar mot basen og brukbar ORDRETT som neste kalls argument
(S7a-3s regel om at en sti aldri skal måtte komponeres av en modell). `title`/`ref` er **målt** ut av
konseptets egen frontmatter ved forfatting og re-verifiseres av testen — de er et opptak, ikke en
andre kilde.
### 1.4 `docs/` er TOM, og det er en beslutning
Scenarioet er MS Office + PDF. Ingen av dem kan mates inn her: `--docs-dir`-omveien er **frarådet**
(P13 — den omgår stigen `list_bundles → read_bundle → read_dir → read_file` og de to gatene
§4.1a-dimensjonen og verdict-laget), og ordren forbyr å bygge den. Veien et ekte prosjektdokument
skal ta er gjennom okf-ingest inn i en base, altså en base til, ikke en katalog ved siden av.
Katalogen finnes derfor med en `README.md` som sier nøyaktig det, og `0` dokumenter — som ordrens
«0–n» tillater.
---
## 2. De fire settene (DEL 2)
Basene er MÅLT, ikke lest av ordren (`find <base> -name '*.md' | wc -l`):
| Sett | Base | `.md`-filer | konsepter (`type: Krav`/`Prosess`) | erklært `bundle_id` |
|---|---|---|---|---|
| `gate-nordvik-2027` | `n100-2023` | 450 | 445 Krav | `vegnormal-n100-2023` |
| `fv412-dekkefornyelse-2027` | `n200-2024` | 1 137 | 1 132 Krav | `vegnormal-n200-2024` |
| `tunnel-hauglia-2027` | `n500-2024` | 274 | 269 Krav | `vegnormal-n500-2024` |
| `kontrakt-sorasen-2027` | `r761-2025` | 5 514 | 2 727 Prosess + 28 Kapittel | `vegnormal-r761-2025` |
Ordrens tall (450 / 1 137 / 274 / 5 514) reproduseres eksakt. Differansen mellom `.md`-filer og
konsepter er `index.md`-ene, som er navigasjon og ikke innhold.
### 2.1 `affected_codes` — hvor de kommer fra, og hvor de IKKE gjør det
**Bare `kontrakt-sorasen-2027` har ekte koder.** R761 bærer `prosessnr` i frontmatter (`12.1`,
`22.1`, `51.1`, `52.1` …), altså en kodeserie som finnes i basen og er grep-bar. De tre
vegnormal-settene bærer INGEN kostkoder — N100/N200/N500 er krav, ikke prissatte poster — så
kodene der er **syntetiske prosjekt-kostlinjer jeg har funnet på**, navngitt i ærlighetsfeltet i
hvert sett. Dette er ikke pynt: `candidate_from_approach` (S7b-døra, `--proposals-from-mandate`)
avviser en kode basens baseline ikke bærer, så de tre settene er BEVISST ikke kjørbare på den døra.
Den døra er ikke stresstestens sti — stresstesten går `--mandate` over den ordinære løkka.
### 2.2 Falsifiseringsgaten — og hvorfor den er så skarp her
**RETTET 2026-09-13 (P15, ordre `20260912T220951Z`).** Den opprinnelige setningen her — «samtlige 22
er FRAVÆRENDE fra n100/n200/n500» — var USANN og er fjernet. Kun 8 av de 22 opprinnelig prøvde
ordene ble noensinne navngitt (listen over, med et avsluttende «…»), og selve 22-ordslisten ble
ALDRI persistert noe sted i repoet — den kan derfor ikke re-måles verbatim. Det er en
nevner-svikt av Verifiseringslovens ansikt 4-type: et «fraværende»-utsagn uten en gjenfinnbar
liste er ikke et mål, det er en påstand om et mål som fant sted.
**Ny, navngitt og persistert 22-ordsliste** (2026-09-13, denne rettelsen — velges for domene-treffsikkerhet, ikke for å oppnå et bestemt utfall), re-målt med gatens egen
substring-regel (`anchors_are_absent`, `tests/test_context_sets_loadbearing.py`) mot alle fire
basene:
`enhetspris`, `kroner`, `budsjett`, `kostnadsestimat`, `prisskjema`, `timepris`, `nåverdi`,
`driftskostnad`, `anleggskostnad`, `materialkostnad`, `arbeidskostnad`, `investeringskostnad`,
`vedlikeholdskostnad`, `kapitalkostnad`, `finansieringskostnad`, `livssykluskostnad`,
`kontraktssum`, `anbudspris`, `tilbudspris`, `merverdiavgift`, `avskrivning`,
`besparelsespotensial`.
**Faktisk telling per base (446/1133/270/2756 konsepter):**
- **n100-2023: 22 av 22 fraværende.**
- **n200-2024: 22 av 22 fraværende.**
- **n500-2024: 21 av 22 fraværende — IKKE 22.** Basen bærer `kroner`, men som en FALSK POSITIV:
ordet forekommer kun inni `borkroner` (boreutstyr, ikke penger) i
`krav/N500/id-41a2f459-e362-42d8-d725-aaee8290c7c1.md` — «(styrestenger, borkroner), nøyaktighet
ved a…». Delstreng-regelen i Rule U treffer bevisst uten ordgrenser (§ 2.3), og dette er den
konkrete prisen for det: en ekte nulltreff-base kan likevel «bære» et pengeord gjennom et
urelatert sammensatt ord. Funnet endrer INGEN gate — ingen fasit-anchor er `kroner` — men det
gjør STATE-påstanden usann og er derfor rettet her.
- **r761-2025: 18 av 22 fraværende** (uendret konklusjon fra tidligere, men re-målt mot den nye
listen): basen bærer `enhetspris`, `kroner`, `budsjett` og `kapitalkostnad` — alle fire er ekte
kostnadsspråk (f.eks. «kapitalkostnader», «mengder i kroner avviker fra summen»), konsistent med
at prosesskoden ER kontraktsspråk.
Konklusjonen står: tre av fire baser er reelt uten kostnadsspråk (n500s ene treff er en
falsk positiv, ikke et pengeord), og det er hele grunnen til at gaten er verdt å teste. En kjøring
som svarer med et kronebeløp mot N100 har ikke lest basen — den har funnet på.
### 2.3 Regelen for «kan ikke besvares» (DEL 3c) — skrevet ned
> **Regel U.** Hvert ubesvarbart spørsmål erklærer ≥ 1 `anchor`: et ord på ≥ 4 tegn, små bokstaver.
> Spørsmålet er admittert **iff hver anchor er fraværende — case-insensitivt, som delstreng — fra
> HELE teksten (frontmatter + kropp) i HVERT konseptdokument i basen.**
Tre valg i den regelen er bevisste:
1. **Anchor, ikke «deler ingen nøkkelord».** Ordrens bokstav ville krevd at spørsmålet ikke deler
ett eneste ord med noen konsepttittel. I en domenebase er det ikke oppnåelig — et
tunnelspørsmål deler «tunnel» med hundrevis av titler, og det beviser ingenting. Det som gjør
et spørsmål ubesvarbart er at basen mangler **saken**, ikke vokabularet. Anchoren ER den saken.
2. **Hele teksten, ikke bare tittelen.** En tittel-regel er en proxy: et ord kan mangle i hver
tittel og stå i hver kropp. Fullteksten koster ingenting ekstra (filene leses uansett i sin
helhet — målt 0,77 s for r761, den største basen), så den svakere regelen hadde ingen pris å
forsvare seg med.
3. **Nevner, alltid.** Skanningen rapporterer hvor mange konsepter den så, og en skanning som ser
**null** konsepter er RØD. Uten den ville «anchoren ble ikke funnet» vært like sant om en base
som ikke ble lest (Verifiseringsloven ansikt 4).
**Ærlighets-grense, uttalt:** regelen beviser at ORDET ikke står i basen, ikke at SPØRSMÅLET er
ubesvarbart. Et spørsmål kan omskrives til synonymer basen bærer og da være besvarbart uten at
anchoren dukker opp. Anchoren er valgt til å være spørsmålets bærende begrep nettopp for å gjøre det
gapet lite, men det er en proxy, og det står her i stedet for å bli bortforklart.
---
## 3. Gaten (DEL 3)
`tests/test_context_sets_loadbearing.py`. Fire krav, og **hvert av dem har sin egen
kjent-positiv**: et bevisst ødelagt sett bygget i `tmp_path` som gjør nøyaktig den armen rød.
| Arm | Krever basen? | Hva den nekter |
|---|---|---|
| (a) hvert `mandate.json` lastes gjennom `load_mandate` | nei | et sett som ikke laster |
| (d) `bundle_id` i mandatet == settets erklærte id | nei | et mandat som peker på feil base |
| (b) hver fasit-sti finnes i basen, og `title`/`ref` er basens egne | **ja** | en fasit-id som ikke finnes, eller en tittel som har driftet |
| (c) Regel U over hele basen | **ja** | et «ubesvarbart» spørsmål basen faktisk bærer ordet for |
| (e) den erklærte `bundle_id` == basens egen `reconcile_bundle_id` | **ja** | en erklæring som har driftet fra basen |
**Basene er IKKE en repo-avhengighet, og de bundle-krevende armene SKIPPER når roten mangler.**
Det er MAJOR-3-gatens egen begrensning (K2 kunne heller ikke være en testavhengighet) og samme
klasse som de fem betalte skippene suiten alt har: basene ligger utenfor repoet, og en hard feil
ville brutt `uv run pytest` i overleveringspakka for enhver ekstern mottaker. Skippet navngir roten,
og armen som kjører bærer nevner-kontrollen fra § 2.3, så et stille tomt skann kan ikke bli grønt.
De to offline-armene er ubetingede og kan aldri være fraværende.
---
## 4. Målepunktet: kan én kjøring bære flere bundler? (DEL 4 — ikke bygget)
### 4.1 Hva (a) koster i dag
Fire kjøringer, fire `run_id`, fire utbokser. Per sett (`<sett>` og `<base>` fra tabellen i § 2):
```sh
uv run python -m portfolio_optimiser.run <project_id> \
--bundle-dir "$PORTFOLIO_VEGNORMAL_ROOT/<base>" \
--mandate contexts/<sett>/mandate.json \
--run-id <sett>-01 \
--outbox-dir scratchpad/p14-stress/<sett> \
--profile azure --max-rounds 8 --max-tokens 120000
```
Kostnaden ved (a) er altså **ikke** fire ganger modellprisen mot én — det er fire uavhengige
kjøringer som hver bærer sin egen base, og det er dét som gjør dem sammenlignbare. Det den koster er
**operatørarbeid og sammenstilling**: fire kommandoer, fire utbokser, fire dom-nøkler, og ingen
felles `MultiBaseResult` som sier hva kommisjonen samlet ble til. En kryss-base-kollisjon (D2) kan
ikke oppstå, fordi hver kjøring har sin egen `VerdictStore` — læring fra base k når aldri base k+1.
Dét er hovedtapet ved (a), og det er nøyaktig det (c) kjøper.
### 4.2 Nøyaktig hva (c) ville trenge
**Motoren finnes ferdig.** `run.run_mandate_across_bundles` (`run.py:2124`) tar allerede
`mandate` + `bundle_dirs: Sequence[str]`, partisjonerer via `mandate.route_by_bundle`, tråder ÉN
`VerdictStore` på tvers, og returnerer `MultiBaseResult` med `unreached` og `collisions`. Den tar
bevisst **ingen** `project_id` (hver bases prosjekt leses av `_project_from_bundle`).
Det som mangler er utelukkende kallstedet, og det er tre ting — MÅLT, ikke anslått:
1. **Argparse:** en repeterbar `--bundle-dir` finnes ikke (`run.py:2444` —
`parser.add_argument("--bundle-dir", default=None, …)`, altså ÉN katalog), og
STATE fører «repeterbart `--bundle-dir` = NEI» som stående beslutning. (c) trenger derfor et
**nytt, eget flagg** — f.eks. `--bundle-dirs` (`action="append"`) eller `--mandate-across` —
aldri en utvidelse av det eksisterende, som ville endret en flate fire eksisterende gater pinner.
2. **`run_id`-mynting:** `run_mandate_across_bundles` wirer **ikke** utboksen. Funksjonens egen
docstring sier hvorfor: *«The outbox is NOT wired: N runs need N `run_id`s, and minting them here
would default a key this repo requires a caller to supply»* (`run.py:2178-2180`), og kroppens ene
`await run_project(` (`run.py:2260`) sender verken `outbox_dir` eller `run_id`. (c) må altså
avgjøre myntingsregelen på KALLER-siden — f.eks. `<run_id>-<bundle_id>` — og den regelen er en
operatørbeslutning, ikke en default dette laget får ta.
3. **Partisjonen i `main()`:** hvert nytt flagg må inn i de tre nekt-settene som allerede finnes —
`--portfolio`-partisjonen (`run.py:2771 ff.`), `report_forbidden` (`run.py:2859 ff.`) og
dry-run-partisjonen — hver med sin rc-0-kontroll. Det er repoets egen regel om at en utelatelse i
`report_forbidden` er et **stille dropp**, ikke en nekt (F4-gapet).
Rekkefølgen er dermed gitt: (c) er én ordre med ett nytt flagg, én myntingsregel operatøren
bestemmer, og tre partisjons-rader. Den skrives etter første stressrunde — når vi vet om (a)s
manglende kryss-base-læring faktisk kostet noe.
---
## 5. FUNN — konsept-titlene kollapser på vei gjennom navigasjonsstigen
**Målt, ikke antatt, og funnet fordi P14 leste basene i stedet for å anta dem.**
`okf.parse_frontmatter` er linjeorientert og **last-write-wins** — det står ordrett i dens egen
docstring («every frontmatter line that carries a colon becomes one `key: value` pair, last write
winning»). Hver eneste vegnormal-konseptfil avslutter frontmatteren med en `sources:`-blokk:
```yaml
sources:
- resource: https://viewers.vegnorm.vegvesen.no/api/nisosts/859990?languageCode=nb
title: N500:2024
```
Den **innrykkede** `title` overskriver dermed konseptets egen. Målt på `n500-2024`:
| Målt | Resultat |
|---|---|
| `okf.navigate_bundle(...)` → `context_files` | **270 konseptfiler, 1 distinkt tittel** (`N500:2024`, 270 ganger) |
| `okf.directory_listing(bundle, path="krav/N500")` | **269 dokumenter, alle med `"title": "N500:2024"`** |
| konseptenes EGNE toppnivå-titler | **269 distinkte** (`Krav 1.1—2 Generelle bestemmelser`, …) |
Over de fire basene: **445/445 · 1 132/1 132 · 269/269 · 2 378/2 727** distinkte egne titler, mot
**1** distinkt gjennom `parse_frontmatter` på hver av dem.
**Hvorfor det betyr noe for stresstesten.** S7a-3 bygde stigen `list_bundles → read_bundle →
read_dir → read_file` nettopp for at navigatøren skal kunne VELGE hvilket dokument den åpner. På
disse basene er rung 2 og rung 3 informasjonsløse: navigatøren ser 269 oppføringer som skiller seg
fra hverandre med et ugjennomsiktig UUID-filnavn og et tegnantall. Den kan ikke velge på annet enn
tilfeldighet — og MAJOR-3s egen ærlighets-grense («at en LEVENDE modell velger BEDRE med en liste
enn med hele konteksten er IKKE bevist») blir her målbart usann i den ene retningen ingen hadde
målt: det er ingenting å velge PÅ.
**IKKE fikset her, og det er en scope-grense, ikke en forglemmelse.** Ordren forbyr å bygge utover
P14, og en fiks er dessuten to ulike beslutninger med hver sin gate: enten endres
`parse_frontmatter`s pinnede last-write-wins-regel (som mange tester pinner), eller så bytter
`directory_listing` tittelkilde til en toppnivå-leser. Begge er en egen ordre.
**Gaten har derfor en TRIPWIRE i stedet.** `own_frontmatter` i
`tests/test_context_sets_loadbearing.py` leser konseptets egen erklæring (toppnivå-nøkler, første
forekomst vinner) — uten den ville fasit-asserten sammenlignet hvert konsept mot den samme
konstanten og vært VAKUØS, repoets egen vakuøs-gate-klasse. Arm
`test_the_fasit_titles_are_distinct_not_the_collapsed_sources_title` asserterer BEGGE halvdeler:
at de registrerte titlene skiller konseptene fra hverandre, **og** at `parse_frontmatter` fortsatt
kollapser dem. Den dagen den andre halvdelen blir rød, er funnet borte og armen skal SLETTES, ikke
svekkes — det står i armens docstring.
---
## 6. Leveransen (DEL 5)
- **Ingen produksjonskode er rørt.** Leveransen er fire datasett + én gate + dette dokumentet.
- **Suite:** basislinje etter P13b **1606 passed / 5 skipped** → **1642 passed / 5 skipped**
(+36, 0 fjernet — strengt supersett). `demo-transcript.stdout` BYTE-UENDRET,
`shasum -a 1` av INNHOLDET = `ea8c534773acdbe41ae68f2c55724d69aaf8be4f` (aldri git-blob-id-en).
`ruff check` + `ruff format` + `mypy src` grønne.
- **Seks mutasjoner, alle røde på sin egen arm** (mot gate-fila; `grep -rln contexts tests/` viser
at INGEN annen test leser `contexts/`, så hele-suite-kjøringer per mutasjon ville ikke kunnet
legge til informasjon — uttalt, ikke utelatt). Settene ble sikkerhetskopiert til
`scratchpad/p14-backup/` og gjenopprettet derfra, aldri med `git checkout`; de 16 filene er
verifisert byte-identiske etterpå.
| # | Mutasjon | Rød arm |
|---|---|---|
| M1 | fasit peker på en sti basen ikke bærer | (b) + tittel-tripwiren |
| M2 | en approach rutet mot en annen base | (d) |
| M3 | en «ubesvarbar» anchor basen FAKTISK bærer (`asfalt` i n200) | (c) |
| M4 | duplisert approach-id | (a) + (d) + fasit-dekningen |
| M5 | erklært `bundle_id` driftet fra basens egen | (d) + (e) |
| M6 | en registrert tittel driftet fra basens egen | (b) |
Den aller første kjøringen av gaten var **rød før settene fantes**
(`expected four context sets … found []`), som er Iron Law-rekkefølgen.
### 6.1 Ærlighets-grenser, uttalt
1. **De fire prosjektene er oppdiktet.** Hvert sett bærer sitt eget `honesty`-felt som sier nøyaktig
hva jeg har konstruert. Kort: navn, lengder, ÅDT og alle tolv beløp er satt av meg;
`affected_codes` er syntetiske i tre av fire sett og EKTE `prosessnr` i `kontrakt-sorasen-2027`.
Alt i `must_cite` er derimot lest ordrett ut av basenes egen frontmatter.
2. **Regel U beviser at ORDET mangler, ikke at spørsmålet er ubesvarbart** (§ 2.3).
3. **De bundle-krevende armene skipper uten basene.** På denne maskinen kjørte alle fire; i en ren
klon uten `~/repos/vegnormal-okf` skipper tre armer per sett med roten navngitt.
4. **Ingen kjøring er gjort.** Ordren forbyr modellkall og Azure. At po faktisk KLARER å peke på
fasit-konseptene, og at den faktisk SIER «dette kan ikke besvares», er ikke bevist her — det er
nøyaktig det stresstesten 18.09 skal måle. Dette er måleoppsettet, ikke målingen.

View file

@ -1,354 +0,0 @@
# P16 — stressrunde 1: the good, the bad and the ugly
Ordre `20260914T091846Z-2971522127-from-.claude`, økt 120, 14.09.2026.
Fire betalte kjøringer mot n100-2023 / n500-2024 / n200-2024 / r761-2025, dømt av en ny
deterministisk dommer. Alle tall her er MÅLT i denne økten mot disk og mot artefaktene
kjøringene etterlot, ikke lest ut av en tidligere rapport.
**Kortversjonen.** Rammeverket kjører ende-til-ende mot fire ekte kunnskapsbaser og navigerer
dem riktig. Ingen av de fire kjøringene traff et eneste av de 26 fasit-konseptene. Tre av fire
avviste alt som ugrunnet; den fjerde **validerte falsifiseringsarmen** — tilnærmingen som ved
konstruksjon ikke kan forankres — og gjorde det ved å bruke basens eget navn som kostkode.
---
## 1. Hva som ble bygget først (DEL A)
`src/portfolio_optimiser/stress.py` — `score_context_set(context_dir, outbox_dir, run_id,
bundle_dir)`. Den leser KUN artefakter som alt finnes: `{run_id}[-{approach}]-proposal.json`,
`-outcome.json` og `{run_id}-debate.json`, pluss settets `mandate.json` / `fasit.json` /
`bundle.txt`. Ingen kjøring fikk et nytt felt.
### 1.1 Ordrens (a)-definisjon var vakuøs, og det ble målt før noe ble bygget på den
Ordren definerer *grounded* som «en `must_cite`-sti er ÅPNET **eller** SITERT». På S2c-stien
stempler `run_project` `citations = bundle_citations(bundle)` — **én sitering per konseptfil**.
| Base | Kontekstfiler | Siteringer i stempelet | Fasit-stier «sitert» før noe modellkall |
|---|---|---|---|
| n100-2023 | 446 | 446 | **6 av 6** |
En dommer som fulgte ordren ordrett ville altså vært en gate som bare kan bli grønn — repoets egen
vakuøs-gate-klasse, inne i gaten som ble bygget for å fange den. Derfor: en **sitering** grunner
kun når siteringslista er SMALERE enn basen (et erklært pre-pass-kutt). Begge halvdeler rapporteres
uansett (`opened` / `cited` / `citation_scope`), så avviket er uttalt, aldri stille.
**(b′) ble sjekket for SAMME vakuitet og var ren nok til at ordren står:** siteringenes `snippet`
er konsept-BODYER mens `ref`/`title` bor i FRONTMATTER — målt inneholder **0 av 446** n100-bodyer
strengen `Krav 4.1.2—1`. Se likevel § 6.2: målingen på r761 viser at snippet-armen er svak for
KORTE referanser, og `named_in_measure` er den halvdelen som tåler vekt.
### 1.2 Falsifiseringsarmen fikk en kjørbar form (A2)
`po` er ikke et oppslagsverktøy (D-1), så et «ubesvarbart spørsmål» hadde ingen kjørbar form: ingen
kjøresti konsumerte `fasit.json`s `unanswerable`-rader. Samme faktum rir nå en **fjerde bestilt
tilnærming** per sett (`a4-…`) hvis kostlinje basen ikke bærer grunnlaget for, og `fasit.json`
bærer `must_refuse` (`approach_id` / `anchors` / `rationale`) **i stedet for** `unanswerable` — én
form, aldri to kopier av ett faktum. Spørsmålene overlever i `rationale`, som ingenting nøkler på.
Regel U er URØRT og dens kjent-positiv er fortsatt rød.
### 1.3 Mutasjonstabell (DEL A)
Alle elleve røde på sin egen arm, grønn kontroll **1663 passed / 5 skipped**, golden
`demo-transcript.stdout` BYTE-UENDRET (`shasum -a 1` av INNHOLDET =
`ea8c534773acdbe41ae68f2c55724d69aaf8be4f` — ikke git-blob-id-en).
| # | Mutasjon | Røde | Arm |
|---|---|---|---|
| M1 | helbase-siteringer grunner igjen | 2 | (b) + (a) |
| M2 | en åpnet sti grunner ikke | 4 | (a), (d), (f), (k) |
| M3 | (b′) dropper snippet-armen | 1 | (e) |
| M4 | hallusinerte siteringsfiler ignoreres | 1 | (f) |
| M5 | hallusinerte koder ignoreres | 1 | (f) |
| M6 | gjettet lesesti forgifter ikke `ferdig` | 1 | (f) |
| M7 | tom utboks gir rent resultat | 1 | (h) |
| M8 | `not_evaluated`-rader utelates | 1 | (j) |
| M9 | kode-lekkasjearmen i `must_refuse` droppes | 1 | (g) |
| M10 | en tom base tolereres | 1 | (i) |
| M11 | Regel U-ens kjent-positiv (P14 M3) | 1 | context-sets |
---
## 2. Gratis-trinnet betalte seg tre ganger (DEL B2)
### FUNN 1 — den dokumenterte kommandoen kunne ikke kjøres
`STATE.md`, `docs/2026-09-12-p14-kontekstsett.md § 4.1` og ordren publiserer alle den samme
kommandoen, som ender `--max-rounds 8 --max-tokens 120000`. **MÅLT: `run.py` tok ingen av dem.**
Alle fire `--live-dry-run` nektet med `unrecognized arguments`. Verre: `main()` sendte **aldri**
`max_rounds`/`max_tokens` videre til `run_project`, så HVER CLI-kjøring noensinne var stille bundet
til `_DEFAULT_MAX_ROUNDS=3` / `_DEFAULT_MAX_TOKENS=100_000`, uten noen operatørflate for å heve
eller senke taket på noe man betalte for. Tre flater beskrev en dør som ikke fantes.
**Fikset før betaling** (B2s egen instruks), commit `5f94c92`: begge flagg DEFAULTER til nøyaktig
de historiske verdiene (utvidelse, aldri bryting), wiret til BEGGE dispatcher (`run_project` OG
`run_portfolio` — `main()` droppet dem der også) og nektet ved navn i report-modus. Seks armer,
alle røde før fiksen; fire mutasjoner.
### FUNN 2 — `--docs-dir` er påkrevd også når `--bundle-dir` er oppgitt
`run.py:2953` nekter enkeltprosjekt-modus uten `--docs-dir`, selv om `docs_dir` er ubrukt på
bundle-stien. Den dokumenterte kommandoen ville nektet av DEN grunnen også. READMEs egen form
(`--docs-dir <bundle> --bundle-dir <bundle>`) virker. **Målt, rapportert, IKKE fikset.**
### FUNN 3 — annonseringen løy i det øyeblikket flagget fantes
`announce` leste `_DEFAULT_*` direkte. Det var korrekt kun så lenge `main()` ikke kunne gjøre annet.
Målt på den første gratis-turen etter at flaggene landet: samme stdout sa
`Stops at: 3 rounds / 100000 tokens` **to linjer over** `max_rounds=8, max_tokens=120000`.
Annonseringen er det ENE som printes før første betalte kall, og hele jobben dens er å si hva
kjøringen vil gjøre — Fase-3-klassen, innført av nettopp det flagget som annonseres.
**Fikset** (commit `a61a3cc`); M16 (les konstantene igjen) → 1 rød, den armen alene.
### Prediksjonen fra gratis-trinnet, notert FØR betaling
Alle fire dry-runs stoppet rent (rc 0) og sa det samme:
> `Cost baseline: NONE in the bundle — this run is un-anchored`
> `Grounding offer: N distinct identifier(s) and **0 cost line(s)** … every candidate it invents
> will be refused as ungrounded`
| Sett | Identifikatorer | Kostlinjer | Tegn |
|---|---|---|---|
| gate-nordvik (n100) | 435 | **0** | 437 129 |
| tunnel-hauglia (n500) | 272 | **0** | 389 103 |
| fv412 (n200) | 982 | **0** | 1 441 501 |
| kontrakt-sorasen (r761) | **3** | **0** | 6 561 594 |
Predikert utfall: 0 validerte. Se § 4 for hva som faktisk skjedde.
---
## 3. Kjøringene (DEL B3)
Parametrene ble endret underveis, hver gang med årsaken navngitt FØR ny kjøring (B3s regel).
| # | Sett | Base | Runder | Tak | rc | Observert | Veggtid | Maks RSS |
|---|---|---|---|---|---|---|---|---|
| 1 | gate-nordvik | n100 | 8 | 120 000 | **1** | 150 999 | 24,7 s | — |
| 2 | gate-nordvik | n100 | 8 | 500 000 | **1** | 530 881 | 114,0 s | 149 MB |
| 3 | gate-nordvik | n100 | **3** | 600 000 | 0 | 509 310 | 140,1 s | 149 MB |
| 4 | tunnel-hauglia | n500 | 3 | 600 000 | 0 | 255 418 | 73,5 s | 144 MB |
| 5 | fv412 | n200 | 3 | 600 000 | **1** | 604 159 | 121,1 s | 168 MB |
| 6 | fv412 | n200 | **2** | 900 000 | 0 | 523 633 | 176,0 s | 171 MB |
| 7 | kontrakt-sorasen | r761 | 3 | 900 000 | 0 | 104 905 | 218,7 s | **298 MB** |
Totalt betalt: **2 679 305 tokens** over sju kjøringer, hvorav tre døde på taket.
`gpt-4-1-mini`, profile `azure`, `PACE_SECONDS=2`, 0× 429.
**Kostnad — IKKE VERIFISERT.** Ingen faktura er lest. Med listepris for `gpt-4.1-mini`
($0,40/M inn, $1,60/M ut) og et antatt 90/10-forhold inn/ut lander 2,68 M tokens på
≈ **USD 1,4 ≈ NOK 15**. Inn/ut-forholdet er ikke målt — `token_usage` er en totalsum — så tallet
er et anslag med en uttalt antakelse, ikke en måling.
### R761 mot P13s referanse
P13 målte NAVIGASJON av r761 til **7,1 s / 131 MB** (`docs/2026-09-12-p13-okf-pin-r761.md`).
En hel LEVENDE kjøring koster **218,7 s / 298 MB** — 31× tiden og 2,3× minnet. De to måler ikke
det samme (navigasjon mot navigasjon + fire debattrunder + generering + validering), og
sammenligningen sier derfor bare at navigasjonen er en liten del av begge.
---
## 4. Dommen (DEL B4)
`python -m portfolio_optimiser.stress contexts/<sett> --outbox-dir … --run-id …`, fire
`*-verdict.json` skrevet.
| Sett | (a) grunnet | (b′) navngitt | (c) hallusinasjoner | a4 / `must_refuse` | ferdig |
|---|---|---|---|---|---|
| gate-nordvik (n100) | **0 / 4** | 0 / 4 | 7 | **PASS** | nei |
| tunnel-hauglia (n500) | **0 / 4** | 0 / 4 | 5 | **PASS** | nei |
| fv412 (n200) | **0 / 4** | 0 / 4 | 6 | **PASS** | nei |
| kontrakt-sorasen (r761) | **0 / 4** | 3 / 4 | 9 | **FAIL** | nei |
Nevnere, alltid: tool_calls 18 / 16 / 12 / 18 · siteringer 1 784 / 810 / 2 266 / 11 024 ·
approach-rader 4 / 3 / 2 / 4 · konsepter i basen 446 / 270 / 1 133 / 2 756.
`validated` totalt: **2 av 20 rader**, begge i r761 — a4 (falsifiseringsarmen) og own-proposal.
---
## 5. THE GOOD
1. **Stigen virker, og den virker på ekte korpus.** Alle fire kjøringer gikk
`list_bundles → read_bundle → read_dir → read_file` uten å bli instruert i det. r761 gikk rett
ned i `R761/6` → `R761/6/65` — altså brukte den hierarkiet S7a-3 bygde, og betalte **104 905**
tokens for den største basen, mindre enn n100 brukte på den minste.
2. **Falsifisererne biter.** 18 av 20 rader ble avvist, og alle med P7s stadium 0b, med
identifikatoren navngitt i klartekst. Hver eneste avvisningsgrunn var sann.
3. **P8s forankringstilbud predikerte utfallet gratis.** «0 kostlinjer» ble sagt før betaling og
holdt i alle fire kjøringer.
4. **Artefaktene bar beviset.** Dommeren trengte ingen ny instrumentering: `debate.json`,
`proposal.json` og `outcome.json` svarte på alt — også for de tre kjøringene som døde på taket,
der `debate.json` og `runconfig.json` fortsatt lå der.
5. **Alle fire dry-runs stoppet rent** før første modellkall, og gjorde det etter at flaggfunnet
var fikset.
---
## 6. THE BAD
### 6.1 Ikke ett fasit-konsept ble åpnet — 0 av 26, i 24 `read_file`-kall
| Sett | `read_file` | distinkte | fasit ønsket | overlapp |
|---|---|---|---|---|
| gate-nordvik | 10 | 9 | 6 | **0** |
| tunnel-hauglia | 8 | 5 | 8 | **0** |
| fv412 | 4 | 4 | 6 | **0** |
| kontrakt-sorasen | 10 | 6 | 6 | **0** |
Det er hovedresultatet av stressrunden. Navigasjonen fungerer mekanisk og treffer ikke faglig.
Grunnen er lesbar i listingen: konseptfilene heter `id-<uuid>.md`, og `read_dir` gir `name`,
`type`, `title`, `chars`. På n100 må modellen velge blant **446** slike titler i ett svar.
**LØSNING (navngitt, ikke bygget):** gi `directory_listing` en valgfri, *bundet* filtrering på
`req_number`/`title`-delstreng — ett nytt argument på `read_dir`, samme rung, samme gate. Anslag:
én økt, ~6 armer, ingen ny flate i `run.py`. Alternativ b: la `read_bundle` returnere basens egen
`index.md`-body når den er prosa (n-basene har ingen), som ikke hjelper her — derfor a.
### 6.2 (b′)s snippet-arm er svak for KORTE referanser — et funn om min egen dommer
r761 ga `named = 3 / 4`, men `named_in_measure` var sann for kun **1** av dem. De to andre ble
båret av snippet-armen, og på r761 er referansene prosessnumre som `12.1` — som forekommer i
mange bodyer i et helbase-sitert stempel. Målingen på n100 (0 av 446 bodyer bærer `Krav 4.1.2—1`)
generaliserer altså ikke.
**LØSNING:** la snippet-armen kun telle når `citation_scope == "narrowed"`, nøyaktig som (a).
Anslag: én linje + to armer. Ikke bygget i denne økten — dette er PMs beslutning, ikke min, fordi
det strammer ordrens egen definisjon en gang til.
### 6.3 En gjettet sti gir modellen en ugjennomsiktig feil
Målt 12 gjettede `read_file`-stier over de fire kjøringene (3 / 2 / 3 / 4). En sti som ikke finnes
reiser `FileNotFoundError`, som funn-99 eksplisitt holder **utenfor** `_RETURNABLE_REFUSALS`, så
modellen får MAFs `"Error: Function failed."` uten grunn. På n200 fyrte MAFs egen
`Maximum consecutive function call errors reached (3). Stopping further function calls for this
request.` — altså ble verktøybruken avskåret midt i en forespørsel.
**Korrigering av min egen første diagnose:** jeg antok først at dette drev budsjettdøden. Det gjorde
det ikke — mønsteret er 2× på *hvert* kall (to agenter, samme vandring), ikke en retry-loop.
**LØSNING:** utvid funn-99s returnerte-nekt-form til `read_file` på en sti som ikke finnes
(`REFUSED (BundlePathNotFound): …`), og skriv om den eksisterende armen
`test_a_nonexistent_sibling_is_still_an_os_error`. Anslag: én økt, ~8 armer, og den eksisterende
gaten må skrives om, ikke svekkes.
---
## 7. THE UGLY
### 7.1 Falsifiseringsarmen ble VALIDERT på r761 — og basens eget navn var kostkoden
```
a4-indeksregulering VALIDATED 250000 NOK
measure: "Kutt ved gunstigere indeksregulering av kontraktssummen"
affected_items: [{"code": "R761", "quantity": 1.0, "unit_cost": 10000000.0}]
checker_verdict: approve
p10/p50/p90: 2 876 396 / 3 004 495 / 3 126 462
```
a4 er ved konstruksjon ugrunnbar: rule U bekrefter at `indeksregulering`, `nåverdi`,
`kostnadsestimat`, `markedspris` og `prisstigning` alle er FRAVÆRENDE fra r761. Likevel passerte
den:
* **den deterministiske validatoren** — stadium 0 var hoppet over (uforankret base),
* **P7s stadium 0b** — fordi regelen er `code in grounding`, en eksakt DELSTRENG uten mønster, og
`"R761"` forekommer overalt i et 6,5 MB R761-korpus,
* **checkeren** — `approve`.
Modellen fant altså ikke på en kostkode som *lignet* på en ekte: den brukte **basens navn**.
`unit_cost` 10 000 000 er ren oppfinnelse, `p10 ≈ p50 ≈ p90` fordi `assumptions` er tom (degenerert
Monte Carlo), og hele kjeden rapporterte `validated`.
P7-raden uttaler selv at regelen «har intet mønster» og at «rene tall er den ene inerte klassen».
Denne målingen legger til en andre inert klasse: **korte, allestedsnærværende tokens** — og den er
verre, fordi et rent tall ikke *ser* ut som en kostkode mens `R761` gjør det.
**LØSNING (navngitt, ikke bygget), rangert:**
* **(a) minstelengde + treffnevner.** Krev at en identifikator er ≥ N tegn OG at den forekommer
i færre enn M distinkte konsepter — et token som står i 2 756 av 2 756 dokumenter identifiserer
ingenting. Anslag: én økt, ~10 armer, ingen ny flate. Nevneren finnes alt i `delivered`.
* **(b) forankre mot det kjøringen ÅPNET, ikke mot hele korpuset.** `debate_tool_calls` er alt
kaller-eid og skrevet; en identifikator som kun står i dokumenter kjøringen aldri åpnet er ikke
noe forslaget «bygde på». Anslag: én økt, ~8 armer. Strammere enn (a), men endrer hva stadium 0b
betyr — PMs beslutning.
* **(c) `--require-cost-baseline` som default for mandatkjøringer mot uforankrede baser.** Flagget
finnes (F4) og ville nektet alle sju kjøringene før første kall. Det løser ikke 0b, men gjør
«validated» umulig å utstede uforankret. Anslag: en partisjons-rad + en beslutning.
Ingen av dem er bygget her — ordren forbyr det eksplisitt.
### 7.2 Én katalogvisning er opptil 113× taket S7a-3 satte, og den rir hver tur
| Base | Konsepter | Nivåer | Rot | Verste listing | ~tokens | × 1 500-tegns-taket |
|---|---|---|---|---|---|---|
| n100-2023 | 446 | 4 | 118 ch | `krav/N100` 69 250 ch | 23 083 | **46×** |
| n500-2024 | 270 | 4 | 118 ch | `krav/N500` 39 853 ch | 13 284 | 27× |
| n200-2024 | 1 133 | 4 | 119 ch | `krav/N200` 169 974 ch | **56 658** | **113×** |
| r761-2025 | 2 756 | 2 758 | 171 ch | `R761` 110 874 ch | 36 958 | 74× |
S7a-3 binder 1 500 tegn per listing over **de basene gaten kan se** (de tre eksempelbasene), og
uttaler selv at K2s verste nivå er 6 073 tegn. De fire vegnormalbasene er 6,5–28× dét igjen, fordi
hierarkiet er FLATT: `krav/ → krav/N100/ → 446 filer`. Rung 2 hjelper ikke når korpuset er to nivåer
dypt med hundrevis av blader.
Konsekvensen er ikke teoretisk: **tre av sju kjøringer døde på tokentaket**, og n100 — den minste
basen — trengte over 500 000 tokens ved 8 runder. Den dypt hierarkiske basen (r761) var den
billigste.
**LØSNING (navngitt, ikke bygget):** paginer `read_dir` — `offset`/`limit` med et bundet vindu og
et `total`-felt, så listingen koster O(vindu) og modellen kan be om mer. Det er S7a-3s egen form
ett hakk videre, og annonsering av avkorting er alt løst der (`index_truncated`). Anslag: én økt,
~8 armer, gaten binder vinduet i TESTEN som i dag. Sekundært: la `okf build` i vegnormal-okf
sub-dele `krav/N100` etter kapittel — men det er en produsent-endring i et annet repo, altså en
coord-sak, ikke vår.
### 7.3 Ordrens eget budsjett var 4,4× for lite, og ingen visste det
`--max-tokens 120000` er publisert tre steder. Den minste basen brukte **509 310**. At ingen visste
det er selve funnet: flaggene fantes ikke, så tallet hadde aldri vært brukt av noe.
---
## 8. Ærlighetsgrenser (uttalt)
* **Én kjøring per sett. Ingen varians er målt.** En andre kjøring av samme sett kan navigere
annerledes; ingenting her sier hvor stabilt utfallet er.
* **a4-armen beviser at ingen validert rad hviler på den — ikke at modellen forsto hvorfor.**
Manuell lesning av de fire a4-utfallene (merket MANUELT): i **n100** skrev modellen ekspertens
etikett ordrett som `measure` og fant på koden `gangfelt_opphoyd` med `unit_cost` 150 000 × 10 —
ingen setning om at basen mangler grunnlag; 0b avviste den. I **n500** og **n200** ble a4 aldri
evaluert (budsjettet var brukt opp før raden), så det finnes ingen tekst å lese. I **r761** skrev
modellen igjen etiketten ordrett og fant på `R761`. **Ingen av de to a4-radene som faktisk ble
evaluert uttalte at basen ikke bærer grunnlaget.** Det er en annen og svakere observasjon enn
«armen bestod» — og for to av fire sett er selv den observasjonen ikke gjort.
* **Parametrene varierer mellom settene** (3/600k, 3/600k, 2/900k, 3/900k). n200 kjørte på 2 runder
mot de andres 3. Tallene i § 4 er derfor ikke strengt sammenlignbare på tvers.
* **Kostnaden er et anslag fra listepris,** ikke lest fra faktura, og inn/ut-forholdet er antatt.
* **Prosjektene er oppdiktet.** Hvert `fasit.json` sier i sitt eget `honesty`-felt hva som er
konstruert: navn, lengder, ÅDT og alle beløp, samt a4 og dens kostkode. Kravene i `must_cite` er
lest ordrett ut av basenes egen frontmatter.
* **`<project_id>` kunne ikke leses av basen.** Ordren ba om «det basens egen IR-projeksjon
erklærer». MÅLT: ingen av de fire basene har `validator-input.json`, så `load_optional_ir_projection`
returnerer `None` (S7b søm 1) og id-en er kaller-oppgitt. Settets eget navn ble brukt.
* **Dommeren leser HVA kjøringen åpnet og siterte, aldri om modellen FORSTO det.**
---
## 9. Verifiseringslogg
| Påstand | Hvordan målt |
|---|---|
| 446/446 siteringer, 6/6 fasit-stier sitert før modellkall | `bundle_citations` mot navigert n100 |
| 0 av 446 n100-bodyer bærer `Krav 4.1.2—1` | telling over `bundle.context_files` |
| `--max-rounds`/`--max-tokens` fantes ikke | `run.py --help` + fire nektede dry-runs |
| `main()` sendte dem aldri videre | lesing av begge `run_project`-kallsteder + `run_portfolio`-kallstedet |
| annonseringen sa 3/100000 mot 8/120000 | samme stdout, gratis dry-run |
| listing-størrelser | `okf.directory_listing` over hvert nivå i hver base |
| token/veggtid/RSS | `/usr/bin/time -l` + `provenance.token_usage` i artefaktene |
| 0 overlapp fasit vs åpnede | `debate.json` `read_file`-stier ∩ `fasit.must_cite` |
| a4 validert med `code: R761` | `*-a4-indeksregulering-proposal.json` + `-outcome.json` |
| rule U holder for alle a4-anchors | `tests/test_context_sets_loadbearing.py::test_c_rule_u…`, grønn |
| suite / golden | `uv run pytest -q` = 1670/5 · `shasum -a 1` = `ea8c534…` |

View file

@ -1,352 +0,0 @@
# P18 — stressrunde 2: hva hver fiks kjøpte, målt mot runde 1
**Ordre** `20260914T105139Z-769818730` · **økt 121** · 14.09.2026
**Forrige runde:** `docs/2026-09-14-p16-stressrunde-1.md` (økt 120)
**Commits:** `9b47e5a` (DEL A) · `7a7c988` (DEL B+C) · denne rapporten
> **Ærlighet først.** Alt under er målt i denne økten. Der et tall kommer fra runde 1 står det
> hvor. Der en effekt **ikke** kan tilskrives en fiks, står det. Ingen betalt kjøring er gjentatt
> for å få et penere tall, og ingen fasit er endret.
---
## 0. Premissene ordren hviler på — re-målt før noe ble bygget
| Ordrens premiss | Målt 14.09 | Status |
|---|---|---|
| `okf.directory_listing(n100, 'krav/N100')` = 69 250 tegn | 69 250 | ✅ eksakt |
| `R761` i 2 756 av 2 756 dokumenter | 2 756/2 756 | ✅ |
| `validator.py` 0b er ren delstreng | `item.code not in grounding` | ✅ |
| `FileNotFoundError` utenfor `_RETURNABLE_REFUSALS` | bekreftet, 10 kall reiste den | ✅ |
| `run.py` krever `--docs-dir` også med `--bundle-dir` | bekreftet | ✅ |
| «0 av 26 fasit-konsepter åpnet i **24** `read_file`-kall» | 0 av 26, men **32 kall** | ⚠️ nevner rettet |
| «les toppnivå i en **egen** leser, som `own_frontmatter`» | P15 (`f13dc64`) gjorde allerede toppnivå til vinner | ❌ **premiss felt** |
| «P16s **20** rader» (B2-spiken) | 16 `affected_item`-koder over 13 forslagsartefakter | ⚠️ nevner rettet |
De tre avvikene er nevnere og et premiss, ikke uenighet om retningen. «24 kall» er de fire
kjøringenes **distinkte stier**; kallene er 32. Det felte premisset betydde at en egen
toppnivå-leser ville vært den andre kopien kø-(p) forbyr — `BundleFile.frontmatter` **er** allerede
konseptets eget felt.
---
## 1. DEL A — navigasjonsstigen
### A1/A2: én listing koster O(vindu), ikke O(nivå)
S7a-3 bandt kostnaden til oppføringer på ETT nivå. P16 målte hva ett nivå koster på et **levert**
korpus. Målt her, samme kall før og etter:
| Base | Nivå | Oppføringer | **Før** | **Etter (default)** | Fall |
|---|---|---:|---:|---:|---:|
| n200-2024 | `krav/N200` | 1 132 dok | 169 974 tegn | **1 537** | −99,1 % |
| r761-2025 | `R761` | **2 728 kataloger** | 110 912 tegn | **479** | −99,6 % |
| n100-2023 | `krav/N100` | 445 dok | 69 250 tegn | **1 493** | −97,8 % |
| n500-2024 | `krav/N500` | 269 dok | 39 853 tegn | **1 453** | −96,4 % |
**R761-raden er grunnen til at vinduet dekker begge slag.** En paginering bare over dokumenter
ville latt det største målte nivået stå upaginert — 110 912 tegn er større enn de 69 250 ordren ble
skrevet for. Dyreste enkeltkall noen kaller nå kan gjøre: **7 639 tegn** (`limit=50`, klemt).
Default 10 er valgt **mot taket**, ikke rundt: én oppføring er 121–209 tegn (median 145) over de
fire basene. n100 lander på 1 493 (ordrens bundne krav), n500 1 453, R761 479 — og **n200 på 1 537,
2,5 % over**, fordi taket er et *tegn*-budsjett og vinduet er et *antall*. Det står som målt, ikke
justert bort.
`filter` er en **delstreng**, ikke et mønster (`_ground_against_input`s grunn ett hakk ned: en form
regelen ikke kjenner returnerer ingenting, og en tom listing leses som «basen har ikke dette»).
Ordrens kjent-positiv, målt: `filter="rundkjøring"` på `krav/N100` gir **6 av 445** rader og 985
tegn, og de to `a1`-fasit-konseptene er blant dem. Kjent-negativ: et filter uten treff gir
`total_matches: 0` og tom liste — et **svar**, aldri en nekt.
### A3: en gjettet sti er en nekt, ikke «Error: Function failed.»
MÅLT før: **10 av 32** `read_file`-kall i P16s fire kjøringer navnga en sti basen ikke holder (8
distinkte; én er et ett-tegns UUID-avvik, `4d7f` for `4e7f`). Hvert av dem nådde modellen som MAFs
ugjennomsiktige streng, og telte mot de tre påfølgende verktøyfeilene som avslutter en forespørsel.
MÅLT etter, samme 32 kall spilt av: **10 navngitte nekter, 22 dokumenter fortsatt servert.** Nekten
navngir den nærmeste katalogen som faktisk *holder* dokumenter — valgt av `context_files`, aldri av
filsystemet, så den aldri kan levere en sti `read_dir` selv ville nektet, og aldri kan reklamere for
`type: verdict`-laget.
**Runde 2, levende:** `hallucinated_reads` er **0 i alle fem kjøringer** (runde 1: 3 · 3 · 4 · 2).
Nekten rakk aldri å fyre — modellen navnga bare stier en listing hadde gitt den.
### Ble vinduet faktisk BRUKT?
Sporet registrerer `name` + `bundle_id` + `path`, ikke `filter`/`offset`/`limit`, så spørsmålet kan
ikke leses direkte. Det kan måles indirekte: **5 av 31 `read_file`-kall i runde 2 åpnet dokumenter
som ligger UTENFOR default-vinduet** på sitt nivå (fv412 2 av 2, gate-nordvik-03 3 av 8). Modellen
kunne ikke ha navngitt dem uten å ha bedt om mer — altså brukte den `offset` eller `filter`.
**Hvilken av de to, vet vi ikke.** Se § 6, funn 1.
---
## 2. DEL B — stadium 0b
`R761` — basens eget navn, i alle 2 756 dokumenter — bar 250 000 NOK gjennom hele gaten til
`validated` i runde 1. Regelen nå: en kode grunner kun hvis den er ≥ 3 tegn **og** står i færre enn
5 % av dokumentene grunnlaget består av, med et **absolutt gulv på 10 dokumenter**.
**N og A er målt:**
* korteste ekte identifikator over alle fire kontekstsett: **4 tegn** (`12.1`, `52.1`) → N = 3 ligger
ett under målingen;
* dokumentfrekvens for hvert kodeformet token i hver base: **1 692 distinkte, og ingen når 5 %.**
Høyeste noe sted 6/446 = **1,35 %**; høyeste en fasit navngir 3/446 = **0,67 %**; `R761` **100 %**.
**Kjent-positiv (offline, P16s eget artefakt spilt av mot basen kjøringen fikk):**
```
Rejection: ungrounded identifier 'R761': it appears in 2756 of the 2756 documents
this run was given — a token that is in every document identifies none of them
```
**Kjent-negativ: 26 av 26** `must_cite`-referanser grunner fortsatt.
### B2 — spiken som felte ordrens egen alternativ-hypotese
Ordren ba om å **måle** (b) «grunnlag = det kjøringen ÅPNET». Målt over P16s 16 kode-rader:
| Regel | grunner | REFUSED (fraværende) | REFUSED (inert) |
|---|---:|---:|---:|
| 0b som den ble sendt (P7) | 2 | 14 | — |
| 0b med B1 | **1** | 14 | **1** |
| 0b over det kjøringen ÅPNET | 2 | 14 | — |
`R761` står i **hvert åpnede dokument også**, så **(b) ville ikke fanget defekten**. B1 gjør den
inert og lar samtidig den ekte prosesslinja `65 ASFALTDEKKER` (29/2 756 = 1,05 %) grunne.
**(b) er ikke et substitutt for B1.** Målt, ikke bygget — som ordren ba om.
---
## 3. DEL C
**C1** — `--docs-dir` er valgfri når `--bundle-dir` er gitt. På bundle-stien **leses** `docs_dir`
aldri; retrieval, chunk-verktøyet og «no citable content»-sjekken bor alle i veg-grenen. Alle fem
betalte kjøringer i runde 2 brukte ett-flagg-formen. To-flagg-formen (README) er uendret, og
veg-grenen nekter fortsatt uten en ekte `--docs-dir` (egen arm).
**C2** — dommerens snippet-arm teller kun under `citation_scope == "narrowed"`. **Isolert målt** ved
å re-dømme runde 1 med den nye dommeren:
| Kjøring | `named` gammel dommer | `named` ny dommer | hvorav modellens egen prosa |
|---|---:|---:|---:|
| kontrakt-sorasen-01 | **3** | **1** | 1 |
| de tre andre | 0 | 0 | 0 |
**2 av de 3 var helbase-artefaktet** — `12.1` står i R761s bodyer, så merket var «sitert» før noe
modellkall. Den ene som står igjen er `named_in_measure`: modellen sa det selv.
---
## 4. DEL D — stressrunde 2 (fem betalte kjøringer)
Samme fire sett, samme mandater, **samme parametre på alle fire** (`--max-rounds 3
--max-tokens 600000`), `gpt-4-1-mini`, profile `azure`, `PACE_SECONDS=2`.
| # | Sett | Base | rc | Veggtid | Maks RSS | Runde 1 (veggtid) |
|---|---|---|---:|---:|---:|---:|
| 1 | gate-nordvik-**02** | n100 | **0** | 70 s | 148 MB | 140,1 s (3/600k) |
| 2 | tunnel-hauglia-02 | n500 | **0** | 69 s | 140 MB | 73,5 s (3/600k) |
| 3 | fv412-**02** | n200 | **0** | 89 s | 172 MB | 176,0 s (**2**/900k) |
| 4 | kontrakt-sorasen-02 | r761 | **0** | 137 s | 267 MB | 218,7 s (3/**900k**) |
| 5 | gate-nordvik-**03** (varians) | n100 | **0** | 103 s | 148 MB | — |
**0 av 5 døde på taket** (runde 1: **3 av 7**) — og to av settene kjørte nå på et **lavere** tak enn
runde 1 måtte gi dem. Det er den eneste klart tilskrivbare effekten av DEL A på den betalte stien.
**To sett er direkte sammenlignbare** (identiske parametre i begge runder): n100 **140,1 s → 70 s**
og n500 **73,5 s → 69 s**. n200 og r761 fikk i runde 1 henholdsvis `2`/900 000 og `3`/900 000 for å
komme i mål; i runde 2 klarte begge `3`/600 000. Veggtid er ikke tokenforbruk (funn 4), så n100s
halvering er et *signal*, ikke en måling av hva listingene kostet.
### Dommen, sett mot sett
| Kjøring | (a) grounded | (b′) named | hallusinerte koder | gjettede lesestier | a4 |
|---|---:|---:|---:|---:|---|
| **Runde 1** gate-nordvik-01 | 0/4 | 0 | 4 | **3** | ✅ |
| **Runde 1** tunnel-hauglia-01 | 0/4 | 0 | 3 | **2** | ✅ |
| **Runde 1** fv412-01 | 0/4 | 0 | 3 | **3** | ✅ |
| **Runde 1** kontrakt-sorasen-01 | 0/4 | 3 → *1 med ny dommer* | 5 | **4** | ❌ **validert** |
| **Runde 2** gate-nordvik-02 | 0/4 | 0 | 3 | **0** | ✅ |
| **Runde 2** tunnel-hauglia-02 | 0/4 | 0 | 5 | **0** | ❌ **validert** |
| **Runde 2** fv412-02 | 0/4 | 0 | 5 | **0** | ✅ |
| **Runde 2** kontrakt-sorasen-02 | 0/4 | 1 | 4 | **0** | ✅ |
| **Runde 2** gate-nordvik-03 | 0/4 | 0 | 4 | **0** | ✅ |
**Varians (02 mot 03, identiske parametre):** utfallet er *ikke* stabilt. 02 nådde a4 aldri
(rundeboka tok slutt), 03 evaluerte den og avviste den. Kodene modellen fant på er ulike
(`gangfelt-opphevet` mot `OPPHOYD-GANGFELT`), verktøykallene er 12 mot 24, og 02 etterlot en
`parse-failures.json` (modellen foreslo et **negativt** beløp) mens 03 ikke gjorde det. De åpnede
stiene overlapper delvis: begge åpnet `id-b84139f4…` og `id-6592f8a6…`; 03 åpnet i tillegg tre
dokumenter utenfor default-vinduet. **Én ekstra kjøring er ikke et utvalg** — dette sier at variasjon
finnes, ikke hvor stor den er.
### a4-armen: én feil i hver runde, på hvert sitt sett
**r761 er reparert, men ikke av B1 — og det skal sies.** I runde 2 foreslo modellen koden
`Kontraktsum`, ikke `R761`, så avvisningen kom fra «appears nowhere»-armen som fantes fra før. B1s
virkning på nettopp den raden er bevist **offline** (§ 2), ikke levende.
**Ny feil: tunnel-hauglia a4 ble VALIDERT** på koden `impulsventilator`. Målt: den står i **3 av
270** dokumenter — altså genuint til stede, langt under 5 %, og B1 kan ikke og skal ikke felle den.
Samme klasse: fv412 `a1` ble validert på `bituminøst bærelag` (**4 av 1 133**).
**Det er ikke en grunnings-defekt, det er en forankrings-defekt.** Begge er *vanlige norske ord fra
standardens prosa*, ikke kostlinjer. Ingen av de fire basene bærer en kostbaseline, så stadium 0
hoppes over, og ingenting binder en «kode» til en pris noen har skrevet. Se § 6, funn 2.
---
## 5. Hva hver fiks kjøpte — kort
| Fiks | Målt virkning | Tilskrivbar? |
|---|---|---|
| A1 vindu | 39 853–169 974 → 479–1 537 tegn per listing; 0 av 5 kjøringer døde på taket (var 3 av 7) | **Ja** (fritt + betalt) |
| A2 filter | 445 → 6 rader for ordrens kjent-positiv; 5 av 31 leste dokumenter lå utenfor default-vinduet | Delvis — at vinduet ble utvidet er målt, at **filteret** gjorde det er ikke |
| A3 nekt | 10 av 32 replayede kall gir nå navngitt nekt; **0** gjettede stier i runde 2 (var 12) | **Ja** |
| B1 inert-token | P16s a4-artefakt går fra `validated` til `rejected` offline; 26/26 fasit-referanser beholdt | **Ja, offline.** Ikke levende i runde 2 |
| C1 `--docs-dir` | fem betalte kjøringer på ett-flagg-formen | **Ja** |
| C2 dommer | kontrakt-sorasen-01 `named` 3 → 1 | **Ja** (isolert) |
**Det (a) ikke kjøpte: 0 av 26 fasit-konsepter åpnet, i begge runder.** Se § 6, funn 3.
---
## 6. Gjenstående stygt — hvert funn med en navngitt løsning
**Funn 1 — sporet sier ikke HVORDAN nivået ble snevret inn.** `ToolCall` bærer `name`, `bundle_id`
og `path`. `filter`/`offset`/`limit` registreres ikke, så «brukte modellen filteret?» må måles
indirekte (§ 1). **Løsning:** utvid `ExplorationToolRecorder`s `_string_argument`-lesning til de tre
nye argumentene og legg dem i `trace_payload` ved siden av `path` — samme søm S7a-3 pkt. 3 alt bygde.
**Anslag:** én ordre, ~2 t, én ny load-bearing-fil med tre armer (registrer · dropp · koerser).
**Funn 2 — en «kostkode» kan være et vanlig ord fra prosaen.** `impulsventilator` (3/270) og
`bituminøst bærelag` (4/1 133) ble begge validert. B1 kan ikke felle dem (de er sjeldne), og stadium
0 er hoppet over fordi ingen av basene bærer en kostbaseline. **To løsninger, ulik pris:**
(i) *forme-krav i 0b* — en kode må matche `_IDENTIFIER_FORMS` **når inputen tilbyr slike former**
(P8s `GroundingOffer` måler nettopp det). Billig (~3 t), men bryter P7s egen regel om at gaten ikke
skal handle om fasonger, og kan felle en ekte kode i et korpus med en form vi ikke har målt.
(ii) *gjør `--require-cost-baseline` obligatorisk for stresstesten* — ærlig, men F4 målte at ingen
vegnormal bærer kostlinjer, så alle fire settene ville nektet. **Anbefaling: (i), som en RAPPORT
først** (`GroundingOffer` sier alt hvor mange identifikator-formede tokens inputen har), og som gate
kun etter en måling på et korpus som faktisk bærer koder.
**Funn 3 — 0 av 26 fasit-konsepter åpnet, uendret.** Modellen navigerer nå riktig og billig, men
den leter ikke etter *kravet som binder*; den leter etter noe å sette en pris på. Det er en
prompt-/rolle-sak, ikke en stige-sak. **Løsning:** gi hypotesisereren en eksplisitt instruks om å
navngi kravet før den foreslår et kutt, og la `quick_validate` returnere «du har ikke sitert noe
krav» som rådgivende dom. **Anslag:** én ordre, ~4 t, og den må måles betalt (2 kjøringer ≈ NOK 5).
**Funn 4 — en kjøring som lykkes sier ikke hva den brukte.** `token_usage` finnes bare i
`BudgetExceeded`-meldingen, altså kun når kjøringen *feiler*. Alle fem kjøringene i runde 2 ga rc 0,
så **denne rapporten kan ikke oppgi tokenforbruk per kjøring.** **Løsning:** legg `tokens_spent` og
`rounds` på `RunResult` og i `{run_id}-runconfig.json` (eller en `-usage.json`). **Anslag:** ~1,5 t.
**Funn 5 — `--max-rounds 3` gjør at a4 og own-proposal ofte aldri evalueres.** Fire av fem
kjøringer rapporterte «budget exhausted before this approach was evaluated» for minst én rad. Det er
rundeboka (`max_rounds*4`), ikke tokentaket. **Løsning:** ingen kodeendring — det er en
parameterbeslutning; men dommerens `not_evaluated`-rad bør skille «rundebok» fra «tokentak».
**Anslag:** ~1 t.
---
## 7. Kostnad — IKKE VERIFISERT
Ingen faktura er lest, og **tokenforbruket per kjøring er ikke tilgjengelig** (funn 4). Runde 1
brukte 2 679 305 tokens på sju kjøringer ≈ NOK 15 (anslag fra listepris). Runde 2 er fem kjøringer
med kortere veggtid og billigere listinger; **et anslag på under NOK 10 er en antakelse, ikke en
måling**, og oppgis bare fordi ordren ber om et anslag med uttalt antakelse.
### Rettelse 15.09 (P19 D1) — feltet FANTES, og tallene står her
**Setningen over er feil som skrevet, og funn 4 var feil som formulert.**
`provenance.token_usage` (`provenance.py:60`, satt i `run.py` fra `meter.tokens`) er stemplet på
HVERT `-proposal.json` siden S3.4, også ved rc 0, og står i hver eneste av runde 2 sine. Det som
manglet var ikke feltet, men en LESER: ingenting i treet leste det. P19 D1 gir `stress.py` den
lesningen, og `{run_id}-verdict.json` bærer nå `token_usage`.
Målt 15.09 mot artefaktene runde 2 etterlot:
| kjøring | token_usage |
| --- | --- |
| gate-nordvik-2027-02 | 35 406 |
| tunnel-hauglia-2027-02 | 45 642 |
| fv412-dekkefornyelse-2027-02 | 44 468 |
| kontrakt-sorasen-2027-02 | 73 627 |
| gate-nordvik-2027-03 | 89 911 |
| **sum** | **289 054** |
Mot runde 1 sine **2 679 305** (§ 3 i P16-rapporten) er det **−89 %**, og det er den største målte
effekten av P18s navigasjonsarbeid. Anslaget «under NOK 10» står som anslag; det som ikke lenger er
en antakelse er forbruket. **Tilføyelse, ikke omskriving:** avsnittet over står som det ble
skrevet, fordi en rapport som retter seg selv i stillhet ikke er en rapport.
Det som IKKE fantes og nå er bygget (P19 D2) er den andre halvdelen: `settle` printer
coverage-rapporten og `ApproachOutcome` har båret `not_evaluated` siden Trekk A3, men ingen av dem
nådde en FIL. `{run_id}-coverage.json` bærer dem nå, med `stop_reason` fra `BudgetExceeded.kind`.
---
## 8. Verifisering
* `uv run pytest -q` → **1699 passed / 5 skipped** (1670 på `cfd9079`; +29, 0 fjernet — strengt supersett)
* `uv run ruff check src tests` + `ruff format --check` rene · `uv run mypy src` rent (38 filer)
* `shasum -a 1 tests/golden/demo-transcript.stdout` (av **INNHOLDET**, ikke git-blob) =
`ea8c534773acdbe41ae68f2c55724d69aaf8be4f` — **BYTE-UENDRET**
* Mutasjoner: se § 9
* Fire gratis `--live-dry-run` før betaling, alle rc 0, med identiske `Grounding offer`-tall som
runde 1 (435 / 272 / 982 / 3 identifikatorer) — kontrollen på at `Grounding`-strukturen ikke
endret hva gaten måler
---
## 9. Mutasjoner
Hver mutasjon kjørt mot **HELE** suiten i et isolert git-worktree, én om gangen, med grønn kontroll
foran seg. Kontroll DEL A: **1685 passed / 5 skipped** (`9b47e5a`). Kontroll DEL B+C:
**1698 passed / 5 skipped** (`7a7c988`).
### DEL A — sju mutasjoner, alle røde, hver med sin egen signatur
| # | Mutasjon | Røde | Signatur |
|---|---|---:|---|
| A1 | ingen vindu i det hele tatt (før-P18) | **8** | de fire leverte nivåene + begge S7a-3-armene + klemmen + offset |
| A2 | bundet, men VAKUØST (tom side) | **26** | 12 filer, hvorav ni eldre enn dette arbeidet |
| A3 | `total` droppet (nevneren) | **11** | inkl. filter-negativen og begge S7a-3-armene |
| A4 | filteret leser bare tittelen | **1** | `test_the_filter_reads_the_reference_number_and_not_only_the_title` ALENE |
| A5 | en fraværende sti reiser igjen | **4** | to nye + begge armene i den omskrevne tripwire-fila |
| A6 | et filter uten treff NEKTER | **1** | `test_a_filter_that_matches_nothing_is_an_answer_and_not_a_refusal` ALENE |
| A7 | filteret snevrer ikke kataloger | **1** | `test_a_filter_narrows_directories_too` ALENE |
A2 er den viktigste: en tom listing består HVERT kostnadstak perfekt og gir navigatøren ingenting.
At den felles av 26 armer i tolv filer — de fleste skrevet lenge før denne ordren — er dét som gjør
bindingen noe annet enn en gate som bare kan bli grønn.
### DEL B+C — ti mutasjoner, ni røde, **én grønn som ble et funn**
| # | Mutasjon | Røde | Signatur |
|---|---|---:|---|
| B1 | regelen detachet | **3** | kjent-positiv + diskriminatoren + lengde-armen |
| B2 | flagg ALT som er til stede | **77** | hele pipelinen — kontrollen som beviser at regelen ikke bare kan nekte |
| B3 | andels-konjunktet droppet | **2** | kjent-positiv + diskriminatoren |
| B4 | det ABSOLUTTE gulvet droppet | **74** | hver pre-P18-fixtur brekker — gulvet er dét som gjør regelen URØRENDE for dem i stedet for å unnta dem |
| B5 | nevneren ut av grunnen | **1** | kjent-positiv ALENE |
| B6 | `run.py` komponerer ÉN blob igjen | **0 → 1** | **se under** |
| B7 | lengde-konjunktet droppet | **1** | `test_a_token_too_short…` ALENE |
| C1 | `--docs-dir` påkrevd igjen | **2** | begge C1-armene |
| C1b | CLI-en videresender aldri basen | **2** | de samme to — rc og sømmen er to ulike påstander |
| C2 | snippet-armen ignorerer scopet | **1** | `test_e_a_whole_base_snippet_does_not_name_the_concept` ALENE |
**B6 VAR GRØNN, og det er repoets vakuøs-gate-klasse for SEKSTENDE gang.** Å reversere `run.py` til
én blob lot HELE suiten stå grønn (1 698 passed / 5 skipped). Komposisjons-armen driver
`_grounding_text` med en `Grounding` den bygger *selv*, så den kan ikke se hva KJØRINGEN leverte —
og en blob har nøyaktig én grense, så gulvet kan aldri nås, andelen aldri fyre, og defekten er
tilbake intakt. **Regelen er ikke bedre enn grensene den får.**
Arm (h) er gaten som manglet: en crafted base med TOLV konseptfiler som alle bærer samme token — per
dokument **12 av 17** og inert, som blob **1 av 1** og grunnende — med en kontroll på en kode bare
ÉN fil bærer, som fortsatt må validere. Målt rød mot nøyaktig den mutasjonen. Mutasjonen ble ikke
droppet, og sømmen ble ikke uttalt som uvitnet: den fikk et vitne.

View file

@ -1,311 +0,0 @@
# P17b — én kommisjon, flere kunnskapsbaser
**Ordre `20260915T014020Z-2912025275-from-.claude` (erstatter P17). Økt 123, 15.09.2026.**
Commits: `5e4c497` (DEL 1) · `da0ccd0` (DEL 2) · `c4e8800` (flaggdekningen) · denne rapporten.
Operatørdirektivet av 14.09: po skal beviselig virke sammen med DE bundlene — flertall — en gitt
kjøring sier den skal bruke. Dette er beviset for flertallsformen, og det er delt i tre: en
CLI-flate som ikke fantes, et kontekstsett som spenner to baser, og én betalt kjøring dømt per base.
---
## 1. Hva som ble målt FØR noe ble bygget
| Premiss | Kilde | Status |
|---|---|---|
| `run_mandate_across_bundles` finnes ferdig | `run.py:2216` | **BEKREFTET** |
| Flaten er unåbar fra CLI | `grep -n across-bundle run.py` = 0 treff | **BEKREFTET** |
| Alle fire kontekstsett har ÉN base | `bundle.txt` = to linjer i alle fire | **BEKREFTET** |
| Suite 1744/5, golden `ea8c534…` | `uv run pytest` | **BEKREFTET** |
| Dry-run-tilbud n200 ≈ 1 512 / r761 ≈ 2 332 | fri drill, § 4 | **BEKREFTET eksakt** |
Ett premiss i ordren ble **presisert**, ikke felt: ordren beskriver «live-dry-run-partisjonen» ved
siden av `report_forbidden` og `--portfolio`-partisjonen, men den tredje raden er ikke en NEKT.
Målt er den generiske `--live-dry-run`-grenen adressert til `args.project_id`/`args.bundle_dir` —
ingen av dem finnes i denne argv-en — så en utelatelse der er et stille DROPP av hele passet, ikke
en nekt. Raden er derfor en **wiring**: drillen går over HVER konfigurert base. Ordren sier selv
det samme i neste setning («skal drille ALLE baser»).
---
## 2. DEL 1 — CLI-flaten
`--across-bundle <dir>`, repeterbar. Krever `--mandate`, `--run-id` og `--outbox-dir`.
**Myntingsregelen `<run-id>-<bundle_id>` er operatørvalgt (14.09), ikke et valg denne økten tok.**
Den står likevel begrunnet, fordi begrunnelsen bestemte FORMEN: motorens egen docstring har siden
økt 58 sagt at N kjøringer trenger N `run_id`-er, og at å mynte dem der ville defaultet en nøkkel
repoet krever at en kaller oppgir. Motoren fikk derfor en **callback**
(`outbox_for(bundle_id) -> (dir, run_id)`), ikke en `outbox_dir`: det er dét kravet OPPFYLT, ikke
slakket, og regelen bor i `main()` der beslutningen ble tatt.
**Ordrens andre alternativ ble MÅLT og forkastet.** En kaller som kjørte `run_project` selv over
`route_by_bundle`s sub-mandater måtte re-implementere fem regler som hver har nøyaktig ett hjem:
id-avstemmingen, den delte `VerdictStore`-en, per-base-prosjektoppslaget (S7b søm 1s presedens),
kollisjonsregnskapet og begge S3.4-tennene. Kø-(p) over en mye større flate enn ett parameter.
`resolve_bundle_routing` er **ÉN oppløsning** delt av motoren og dry-run-armen. En gratis tur som
svarte med en annen `project_id`, eller tolererte en duplisert id den betalte kjøringen nekter,
ville vært en generalprøve på en annen kjøring.
**Samlefila `<outbox>/<run-id>-multibase.json` skrives fra en `finally`** og hver rad bygges av
RESOLUSJONEN + DISK — de konfigurerte basene, kallerens egen myntingsregel, og hver bases egen
`{run_id}-coverage.json`. Den kjøringen som mest trenger regnskapet er den et tak eller en
leverandør kappet, og motorens dokumenterte grense er at en base som RAISER propagerer. `completed`
er et EGET påkrevd felt (`ExplorationTrace.completed`s grunn ordrett): «ingenting ble uoppnådd» og
«vi fikk aldri vite» må ikke være samme verdi. `stop_reason` LESES TILBAKE fra coverage-fila, aldri
utledet på nytt — P19 D2 la faktumet der, og en andre utledning ville stått fritt til å være uenig
med den dommeren leser.
`BudgetExceeded` er i nekt-tuppelen av enkeltprosjekt-stiens målte grunn: den er en `RuntimeError`,
og den FØRSTE tilnærmingen som treffer taket re-raiser ved design. Over flere baser er dét ikke et
kanttilfelle — runde 3 målte `stop_reason: rounds` i 5 av 5 — så uten armen er det vanligste
utfallet av et multi-base-pass en traceback.
### 2.1 Flaggdekningen — et stille dropp jeg selv innførte, målt ETTER den betalte kjøringen
Den første DEL 1-commiten æret fjorten flagg og nektet fem. Det etterlot **åtte akseptert og
droppet**, og det er F4-klassen jeg selv hadde skrevet inn. Verst var `--mcp-config` (konfigurert
egress med ingenting printet, som repoet forbyr utrykkelig) og `PROJECT_ID`/`--docs-dir`, som ville
SETT ut som æret mens dispatchen leste hver bases prosjekt fra DEN basens egen IR-projeksjon.
Rettet i `c4e8800`: `PROJECT_ID`, `--docs-dir`, `--mcp-config`, `--semantic-retrieval`,
`--embedder-config`, `--checkpoint-dir` og `--review-inbox` nektes **ved navn**; de to
forankringsflaggene (`--derive-cost-baseline`, `--require-cost-baseline`) **WIRES**, fordi de er
BASE-anliggender og dispatchen gir `run_project` én base om gangen, så de komponerer eksakt — og
fordi `--require-cost-baseline` er den navngitte løsningen på nøyaktig den defekten denne øktens
egen betalte kjøring målte (§ 5). De to `requires --bundle-dir`-vaktene svarer ikke lenger FOR denne
modusen: gjennomfall ville bedt operatøren legge til det ene flagget modusen også nekter.
### 2.2 Load-bearing, MÅLT
`tests/test_across_bundles_cli_loadbearing.py`, 24 armer. **Fem mutasjoner, alle røde mot HELE
suiten**, grønn kontroll **1781/5** (fra 1744/5, strengt supersett, 0 fjernet), golden
`demo-transcript.stdout` BYTE-UENDRET (`shasum -a 1` av INNHOLDET =
`ea8c534773acdbe41ae68f2c55724d69aaf8be4f`, aldri git-blob-id-en):
| # | Mutasjon | Røde |
|---|---|---|
| (i) | samlefila droppes | 2 |
| (ii) | myntingen kollapser til bart `<run-id>` | 3 |
| (iii) | `report_forbidden` slipper flagget stille | 1 |
| (iv) | `opened`/`requirements`-sinkene deles mellom basene | **5** (fire i tester ELDRE enn dette arbeidet) |
| (v) | forankringskravet når dispatchen, aldri per-base-kjøringene | 1 |
Mutasjon (iv) er den sterkeste: fire av de fem røde ligger i
`test_debate_navigation_cost_loadbearing`, `test_prepass_run_seam_loadbearing` og
`test_scripted_explore_door_loadbearing` — uavhengige vitner på at sinkene er per kjøring.
**En arm måtte skrives om under målingen.** (v) ble først skrevet som «`--require-cost-baseline`
nekter», men på fixturene har `tunnel-hauglia` en `cost-baseline.json` og `bygg-energi-mikro` ikke.
En arm som bare asserterte rc 1 kunne ikke skille «flagget virket» fra «ingen av dem er forankret»;
den asserterer nå at INGEN artefakter ble skrevet (nekten fyrte før betalingen) og har en kontroll
på at samme argv uten flagget kjører til rc 0.
---
## 3. DEL 2 — femte kontekstsett, `contexts/dekke-og-kontrakt-lindaas-2027`
Fire tilnærminger over TO baser: a1 (forsterkningslag) og a2 (filterlag) mot `n200-2024`,
a3 (riggomfang) og a4 (`must_refuse`, indeksregulering) mot `r761-2025`.
`bundle.txt` fikk **én blokk per base**; hver `name:` åpner en blokk, hver blokk må lukkes med sin
egen `bundle_id:`. Et sett som navngir ÉN base er én blokk, så de fire eldre filene parses
byte-identisk — multi-base-formen er en utvidelse, ikke et nytt format.
**Leseren har nå ETT hjem.** Den hadde to private kopier: én i P14-gaten og én løsere inne i
`stress.main`. Multi-base-formen er nøyaktig endringen som ville latt dem drifte til to svar om ett
sett (kø-(p)). Begge kaller nå `stress.read_bundle_declarations`.
**Regel U ble UNIONEN av hver erklærte base, og det er ingen formalitet.** MÅLT 15.09: `enhetspris`
er fraværende fra n200-2024 og båret av **70 av r761-2025s 2 756** konsepter. Ankre admittert per
base ville derfor admittert et spørsmål passet som HELHET kan grunne. Ordet ble forkastet fra
settets ankre av den grunnen, og målingen står i settets eget `honesty`-felt.
**Dommeren dømmer per base, og får vite hvilken.** `score_context_set(bundle_id=…)` begrenser til
tilnærmingene rutet dit. Uten det rapporterer dommingen av n200-utboksen r761-tilnærmingen som
`not_evaluated`/`absent` — et **falskt funn**, for den tilnærmingen BLE evaluert, mot den andre
basen, under den andre `run_id`-en. Ordren tilbød en `--multibase <samlefil>`-modus; målt mot formen
artefaktene faktisk tar, har hver per-base-kjøring allerede sitt fulle artefaktsett og sin egen
`run_id`, så det dommeren manglet var ikke en ny fil å lese, men det ene mandatet allerede vet.
`stress`-CLI-en NEKTER å gjette når et sett erklærer flere baser (`--bundle`, med rc-0-kontroll).
Arm (d) fikk en andre halvdel: hver ERKLÆRT base må navngis av en tilnærming, fordi en base ingen
tilnærming navngir aldri kjøres (`route_by_bundle`s egen regel).
**Seks mutasjoner, hver rød på sin egen arm** (grønn kontroll 45 armer i P14-gaten):
M1 fasit-sti basen ikke bærer (2 røde) · M2 tilnærming rutet mot en uerklært base (3) ·
M3 anker r761 FAKTISK bærer — `enhetspris` (1, ALENE på regel U) · M5 erklært `bundle_id` driftet
(4) · M6 registrert tittel driftet (1) · dommer-restriksjonen detached (2, pluss de to nye
stress-armene). P14s egne kjent-positiver er fortsatt røde.
**P19/B2-nevneren flyttet 26 → 32** og ASSERTERES, ikke droppes: seks nye referanser, hvorav to
bare `prosessnr` (`12.11`, `12.12`) — B1s punktum-og-siffer-form er nå øvet av en fasit og ikke
bare av en kjent-positiv.
---
## 4. DEL 3a–3b — instrumentet, og den frie drillen
Klientproben `tests/test_foundry_profile_live.py`: **1 passed** (ikke skipped), endepunktet hentet
INLINE fra `az`, aldri i sporet fil.
Den frie drillen over begge basene, `--max-rounds 3 --max-tokens 600000`:
| Base | `project_id` (og hvorfra) | Forankret | `Grounding offer` |
|---|---|---|---|
| `vegnormal-n200-2024` | fra basens ERKLÆRTE `bundle_id` (`declared-index`) | NEI | **1 512** identifikatorer / **0** kostlinjer over 1 442 150 tegn |
| `vegnormal-r761-2025` | fra basens ERKLÆRTE `bundle_id` (`declared-index`) | NEI | **2 332** identifikatorer / **0** kostlinjer over 6 562 243 tegn |
Begge tallene treffer ordrens forhåndsmålte anslag eksakt. **Ingen av de to basene har
`validator-input.json`**, så `project_id` faller tilbake på den erklærte `bundle_id`-en — S7b søm
1s presedens, og grunnen til at dispatchen ikke tar noen `project_id` i argv: en kaller-oppgitt
konstant kunne uansett bare vært riktig for én base av N. Begge basene erklærer dessuten en id som
er ULIK monteringsnavnet (`vegnormal-n200-2024` vs `n200-2024`), og kjøringen SIER det —
S7a-3-varselet, per base.
---
## 5. DEL 3c–3d — den betalte kjøringen
Én kommisjon, to baser, `--profile azure --max-rounds 3 --max-tokens 600000`, rc **0**.
Veggtid **453 s** totalt (n200 139 s, r761 314 s; lest av artefaktenes mtime).
### 5.1 Per base
| | `vegnormal-n200-2024` | `vegnormal-r761-2025` |
|---|---|---|
| `token_usage` | **217 326** | **340 019** |
| `stop_reason` | `""` (ingenting kappet den) | `""` |
| parse-feil | **0** (ingen `-parse-failures.json`) | **0** |
| `filter_calls` / `paged_calls` | 10 / 13 | 13 / 5 |
| verktøykall | 40 | 46 |
| gjettede lesestier | **9** | **3** |
| konsepter i basen | 1 133 | 2 756 |
| siteringer stemplet | 2 266 (`whole-base`) | 5 512 (`whole-base`) |
`collisions: []`, `unreached: []`, `stopped_early: false`, `completed: true`.
**Runde 3s dominerende funn gjentok seg IKKE.** Der målte P19 `stop_reason: rounds` i 5 av 5 og
`kontrakt-sorasen-2027-04` døde etter 11 parse-feil; her er begge basene `""` med null parse-feil.
Det er ett datapunkt, ikke en motsigelse av P19 F3/F4 — det er en annen kommisjon over andre baser —
men det er verdt å skrive ned at taket ikke bandt her.
### 5.2 Dom per tilnærming
| Tilnærming | Base | Utfall | Kode foreslått | Grunnet | `requirement_hit` | `named` | Hallusinasjoner | `prose_codes` |
|---|---|---|---|---|---|---|---|---|
| a1-tynnere-forsterkningslag | n200 | rejected | `forsterkningslag_m3` | nei | nei | nei | `code:forsterkningslag_m3` | `forsterkningslag_m3` |
| a2-filterlag-sprengstein | n200 | rejected | `510-100` | nei | nei | nei | `code:510-100` | — |
| own-proposal | n200 | rejected | `GROUND_INV`, `GEO_DESIGN` | nei | — | — | — | — |
| a3-riggomfang | r761 | rejected | `R761-Prosesskoden-rigg` | nei | nei | nei | `code:R761-Prosesskoden-rigg` | — |
| **a4-indeksregulering** | r761 | **validated** | `1.10.4` | nei | nei | nei | `code:1.10.4` | — |
| own-proposal | r761 | rejected | `TMP_TRAFFIC_MGMT`, `TEMP_CONS_ROADS` | nei | — | — | — | — |
Fem av seks forslag falt på P7s stadium 0b (ugrunnet identifikator) — nøyaktig det
`Grounding offer` forutsa på den FRIE turen: 0 kostlinjer i begge basene, så hver kode proposeren
finner på blir avvist.
**Falsifiseringsarmen a4 SLAPP GJENNOM.** `must_refuse` feiler: `a4-indeksregulering was VALIDATED
— the base carries no ground for it`. Koden er `1.10.4` — et R761-**prosessnummer**, altså
nøyaktig P19 F1 («kravnummer godtas som kostkode»), nå reprodusert på en andre base og med en andre
identifikatorform. Ordren sa: rapportér, fiks ikke. Det er gjort.
**Løsningen finnes og er nå MÅLT på disse to basene** (fritt, § 2.1 wiret den):
`--across-bundle × 2 --require-cost-baseline` gir **rc 1 på 2,1 s, null modellkall, null
per-base-artefakter**, og samlefila står igjen med `completed: false` og `stop_reason: absent` for
begge. Å slå den på er en OPERATØRBESLUTNING, ikke min: den ville nektet begge basene før første
kall, altså gjort hele denne målingen ukjørbar.
### 5.3 Kryss-base-læring (P14 § 4.1) — nei, og hvorfor
**Base 2 SÅ IKKE base 1s dom, fordi ingen dom ble myntet.** Kjøringen ble startet uten
`--decision`/`--rationale`, og F2 (økt 66) er at stillhet mynter INGEN `Verdict` — stdout sier det
per base: `no expert verdict given (verdict key=…)`. `VerdictStore`-en var altså tom hele veien, og
`collisions: []` er sant av samme grunn.
Det er en egenskap ved KJØRINGEN, ikke ved sømmen. At sømmen bærer, er bevist gratis og offline av
`test_the_second_base_sees_the_verdict_the_first_base_minted`: den kjører CLI-en over to baser med
`--decision/--rationale`, asserterer at de to kjøringene fikk SAMME store-objekt (`is`, aldri `==` —
`VerdictStore` er en pydantic-modell med verdi-likhet, så tre tomme stores er alle like: økt 58s
målte vakuitet), og at base 2 startet med base 1s dom-id allerede i den. En fersk-store-per-base-
implementasjon kan ikke produsere det.
### 5.4 Hva én kjøring over to baser kjøpte mot to enkeltkjøringer
Målt, ikke antatt:
1. **Én kommisjon, ett regnskap.** Samlefila gir rekkefølge, per-base `run_id`, `unreached`,
`collisions`, `budget_stop` og per base `stop_reason` i én fil. To enkeltkjøringer gir to
utbokser og ingen som sier at de hørte sammen.
2. **Ruting i stedet for kopiering.** Mandatet skrives ÉN gang; `route_by_bundle` partisjonerer det.
To enkeltkjøringer krever to mandatfiler, og to kopier av et mandat er kø-(p) på operatørens
flate.
3. **Én delt `VerdictStore` er MULIG** (ikke utøvd her, § 5.3). To enkeltkjøringer kan ikke dele
den i det hele tatt uten en Steg-7-innboks imellom.
4. **Ingen tokengevinst.** 557 345 tokens er det samme to enkeltkjøringer ville brukt; hver base
navigeres for seg. Dette er en regnskaps- og rutingsgevinst, aldri en kostnadsgevinst.
### 5.5 Kostnad
**557 345 målte tokens** (217 326 + 340 019) ≈ **NOK 4**. **UTTALT ANTAKELSE:** anslaget bruker
samme regnestykke som P19 (listepris for `gpt-4-1-mini`, prompt og completion ikke skilt), fordi
`provenance.token_usage` er kjøringens ENE teller og ikke deler dem. Ingen faktura er lest.
---
## 6. Funn som står igjen, hver med en navngitt løsning
| # | Funn | Løsning | Anslag |
|---|---|---|---|
| **F1** | `1.10.4` (R761-prosessnummer) godtas som kostkode; a4 valideres. P19 F1 reprodusert på base nr. 2 | `--require-cost-baseline` — MÅLT her: rc 1, 2,1 s, 0 modellkall. **OPERATØRBESLUTNING**, 0 kodelinjer | 0 |
| **F2** | `requirement_hit` 0/4. Begge basene erklærte ETT krav hver, to ganger, og ingen av dem var fasitens | P19 F2 uendret — instruksjonen ber om erklæringen, ikke om at den skal være den bindende. Egen ordre | ~1 økt |
| **F3** | 12 gjettede lesestier (9 + 3) tross P18/A3s navngitte nekt | Nekten VIRKER (den navngir nærmeste listbare katalog); det som mangler er at modellen bruker svaret. Måling, ikke kode | ~0,5 økt |
| **F4** | `citation_scope: whole-base` i begge basene, så (a)-grunning kan bare komme av ÅPNEDE stier — og `opened` er tom for hver rad | Et erklært pre-pass-kutt (`--prepass-payload`) er den ene formen som smalner siteringslista. Uavklart om det passer en multi-base-kjøring: egen ordre | ~1 økt |
| **F5** | `announce` sier «the portfolio» når ingen `project_id` er gitt, også i across-bundle-modus | Én linje: la den navngi de rutede basene. Kosmetisk, men det er en påstand flaten gjør om seg selv | ~0,2 økt |
---
## 7. Ærlighetsgrenser, uttalt
- **En base som RAISER propagerer.** Motorens egen dokumenterte grense — `collect-and-continue`
tilhører `run_portfolio`, der kalleren sendte inn en batch uavhengige prosjekter. De etterfølgende
basene kjøres da ikke, og samlefila sier `completed: false`. Ikke truffet i denne kjøringen.
- **Ingen tokengevinst** (§ 5.4 pkt. 4). Flertallsformen er ruting og regnskap.
- **Kryss-base-læring ble ikke UTØVD betalt** (§ 5.3), bare gratis og offline.
- **Ett datapunkt.** Én betalt kjøring, én modell, ett deployment, ett konstruert prosjekt. At
`stop_reason` var tomt her motsier ikke P19 F3.
- **Prosjektet fv. 218 Lindås er OPPDIKTET** — navn, lengde, ÅDT og alle fire kostlinjene. Kravene
og prosessene i fasiten er lest ordrett ut av basenes egen frontmatter. Settets eget
`honesty`-felt sier det samme.
- **Den hostede flaten er BEVISST urørt.** `--across-bundle` er i ingen av hostings tre sett, så den
generiske 400-en svarer og Fase 4es to halvdeler står.
- **`--proposal-review` trås gjennom til ÉN terminal** delt av alle basene (motorens egen
begrunnelse: dispatchen er sekvensiell, og forespørselen bærer både approach-label og
`project_id`). Ikke utøvd betalt.
- **`tests/test_scripted_explore_door_loadbearing.py` er uformatert ved HEAD** og er IKKE rørt —
pre-eksisterende drift, ikke min å rydde i en annen ordre (kirurgiske endringer).
---
## 8. Reproduksjon
```bash
source scratchpad/p19/env.sh # endepunktet hentes INLINE fra az, aldri fra fil
uv run pytest tests/test_foundry_profile_live.py -q # skal gi 1 passed
uv run python -m portfolio_optimiser.run \
--across-bundle ~/repos/vegnormal-okf/build/ferdig/n200-2024 \
--across-bundle ~/repos/vegnormal-okf/build/ferdig/r761-2025 \
--mandate contexts/dekke-og-kontrakt-lindaas-2027/mandate.json \
--run-id lindaas-01 --outbox-dir scratchpad/p17b-multibase/lindaas \
--profile azure --max-rounds 3 --max-tokens 600000
for b in n200-2024 r761-2025; do
uv run python -m portfolio_optimiser.stress contexts/dekke-og-kontrakt-lindaas-2027 \
--outbox-dir scratchpad/p17b-multibase/lindaas \
--run-id "lindaas-01-vegnormal-$b" --bundle "$b"
done
```
Artefaktene fra denne kjøringen ligger i `scratchpad/p17b-multibase/` (usporet).

View file

@ -1,325 +0,0 @@
# P19 — kravet som binder, ordet som ikke er en kode, sporet som sier hvordan
**Økt 122, 15.09.2026.** Ordre `20260914T221206Z-3427310140-from-.claude`.
Commits `c84e8bf` (A) · `d74f32d` (B) · `4c6084e` (C+D) · denne rapporten (E).
Kontroll **1744 passed / 5 skipped** (fra 1699/5 ved P18, strengt supersett, 0 fjernet).
Golden `tests/golden/demo-transcript.stdout` BYTE-UENDRET gjennom hele arbeidet:
`shasum -a 1` av INNHOLDET = `ea8c534773acdbe41ae68f2c55724d69aaf8be4f` (aldri git-blob-id-en).
---
## 0. Et premiss i ordren ble felt FØR noe ble bygget på det
Ordrens **A1** legger kravet i `_INSTRUCTIONS[HYPOTHESISER_ROLE]` ALENE, og **E1** gjentar P18s
stresskommando ordrett. De to kan ikke begge være sanne:
| målt 15.09 | resultat |
| --- | --- |
| stresskommandoen i E1 | `--mandate`, **ingen `--explore`** |
| `--explore` + `--mandate` | NEKTES ved navn (økt 57) |
| `{run_id}-exploration.json` i de ni runde-1/2-utboksene | **0 av 9** |
Hypotesisereren kjører altså **aldri** i en stressrunde. En forpliktelse bare den kan bære ville
vært strukturelt inert i nøyaktig de betalte kjøringene ordren bestiller — og **A3** («kravet når
forslaget») ville vært unåbar sammen med den.
**A2s egen setning løser det:** nekten skal gå til modellen «som en tur den kan rette (samme
mekanisme som `quick_validate`s nekt), ikke som en `raise`». `quick_validate` ER et verktøy.
`declare_requirement` bor derfor i `navigator_tools`, altså hos BEGGE roller som navigerer:
utforskningen, og siden S2c debatten. Én instruksjon, én nekt, én record, to dører.
**Dette er ikke en omtolkning som ble bekreftet i etterkant — det er dét som gjorde runde 3 målbar:**
en levende modell kalte verktøyet i **5 av 5** kjøringer.
---
## 1. DEL A — kravet som binder
`declare_requirement(bundle_id, path, ref)` finnes **kun når kalleren gir begge sinkene**
(`opened` + `requirements`), og dét er hva som holder hvert pre-P19-kallsted byte-identisk. Én sink
uten den andre NEKTES ved konstruksjon: en logg som ikke ser hva som ble åpnet ville akseptert
enhver erklæring. `opened` er den SAMME lista `ExplorationToolRecorder` fyller — en alias, aldri en
kopi — så nekten leser kjøringens EGEN lesetrace.
Merket hypotese bærer `requirement` som PÅKREVD nøkkel: utelatt er en hard feil, eksplisitt `null`
er lovlig og krever `why_none`, halvnavngitt nektes. Et MYNTET forslag bærer feltet; et FRØ får det
aldri (§ C.6 dør 1). `_build_messages` skriver linja kun når feltet finnes.
**Mutasjoner (4, alle røde mot HELE suiten, grønn kontroll 1711/5):**
| # | mutasjon | røde |
| --- | --- | --- |
| A-i | `requirement` valgfri igjen | 1 |
| A-ii | nekten sjekker ikke mot åpnede stier | 1 |
| A-iii | A3-linja fyrer ubetinget | 1 |
| A-iv | dommeren teller mot hele basen | 1 |
**ORDRENS SPÅDDE SIGNATUR FOR A-iii BLE FALSIFISERT.** A5 sier golden må bli rød. Den er
byte-uendret, og grunnen er strukturell: demoen kjører UTEN mandat, så `_build_messages`' approach-
gren tas aldri på golden-stien. Vitnet er byte-identitets-halvdelen av arm (g), som asserterer at en
prompt uten krav er tegn for tegn den samme som før.
**A-iv STO GRØNN FØRST — repoets vakuøs-gate-klasse, TJUEFJERDE gang.** Armen drev `_attributable`
mens treffet regnes ut på KALLSTEDET i `score_context_set`. Den driver nå hele dommeren mot en
erklæring som ER i basen men IKKE er fasitens, med en positiv kontroll.
---
## 2. DEL B — ordet som ikke er en kode
**Kjent-positiv, MÅLT offline mot basene kjøringene faktisk fikk:**
| artefakt | kode | før | etter |
| --- | --- | --- | --- |
| `tunnel-hauglia-2027-02-a4` | `impulsventilator` | validated | **rejected** — «offers 391 identifiers of its own» |
| `fv412-…-02-a1` | `bituminøst bærelag` | validated | **rejected** — «offers 1359» |
**B1 — tilbudet per base, før/etter de to nye formene:**
| base | dok | tegn | før | etter |
| --- | ---: | ---: | ---: | ---: |
| n100-2023 | 446 | 436 799 | 435 | 578 |
| n200-2024 | 1 133 | 1 441 170 | 982 | 1 512 |
| n500-2024 | 270 | 388 773 | 272 | 391 |
| **r761-2025** | 2 756 | 6 561 263 | **3** | **2 332** |
**Den FØRSTE formen ble utvidet i samme slengen, og dét er en måling.** B2 gjorde de samme formene
til `prose`/`identifier`-avgjørelsen, og repoets EGEN `ENERGI-TOTAL-EL` matchet ingen av dem (form 1
krevde siffer etter separatoren) — klassifisereren kalte altså en ekte kostkode prosa, og den nye
gaten nektet den. **En gate får bare ta feil i retningen som slipper for mye inn.** Form 2 fikk et
valgfritt `_<n>`-suffiks fordi ÉN av de 26 fasit-referansene er `Krav 3.3.2—1_1`.
**Kjent-negativ:** 26 av 26 fasit-referanser klassifiseres som identifikatorer.
**Ærlighets-grense, målt og gitt sin EGEN arm:** `42.5` og `12.1` er typografisk identiske og ingen
regel skiller dem, så formen teller begge. P8s eksisterende «bare tall telles ikke»-arm er derfor
SNEVRET til heltall (K2s målte klasse, 46 394 forekomster), og desimal-tvetydigheten står i en
navngitt arm i stedet for i en docstring.
**Re-dom av alle ni runde-1+2-utbokser: 26 av 36 koder er prosa** (runde 1: 11 av 15 · runde 2: 15
av 21).
**Mutasjoner (4, alle røde, grønn kontroll 1734/5):** B-i gaten uten tilbuds-vilkåret (**16**, spredt
over syv eldre testfiler) · B-ii prosessnummer-formen droppet (10) · B-iii `prose` rapporteres aldri
(1) · B-iv gaten detachet (2).
---
## 3. DEL C + DEL D — sporet, forbruket og stoppgrunnen
`ToolCall` bærer nå `filter`/`offset`/`limit`. `_number_argument` er en SØSKEN av `_string_argument`:
en modell kan sende `limit` som `10` eller `"10"`, og en leser som kjente én form ville rapportert et
paginert kall som upaginert.
**P18s FUNN 4 VAR FEIL SOM FORMULERT.** `provenance.token_usage` har stått på hvert `-proposal.json`
siden S3.4; det som manglet var en LESER. Rettelsen er lagt inn som datert tilføyelse i
`docs/2026-09-14-p18-stressrunde-2.md` § 7, UNDER det opprinnelige avsnittet — en rapport som retter
seg selv i stillhet er ikke en rapport.
**Det som genuint manglet er `{run_id}-coverage.json`.** `stop_reason` kommer fra en KALLER-EID SINK,
ikke fra `in_flight`, og dét er en måling: `_evaluate_mandate` SVELGER `BudgetExceeded` så snart noe
er produsert, så `run_project`s egen `in_flight` ser den aldri.
**Mutasjoner (4, alle røde, grønn kontroll 1744/5):** C-i vindus-argumentene registreres ikke (2) ·
D-i coverage skrives med tom grunn (1) · D-ii kjøringen skriver den aldri (2) · D-iii dommeren
slutter å lese forbruket (1).
**D-i STO GRØNN FØRST — vakuøs-gate-klassen, TJUEFEMTE gang.** Armen kalte `write_coverage` selv og
VALGTE dermed grunnen den så asserterte på. Bare en kjøring et tak faktisk kappet kan skille de to;
armen driver nå `run_project` med `max_rounds=1` (én runde betaler første approach, den andre er den
taket kutter).
---
## 4. DEL E — stressrunde 3 (fem betalte kjøringer)
Miljø: `az cognitiveservices account show --name po-foundry-ktg --resource-group
portfolio-optimiser-rg` INLINE → PROSJEKT-formen `…/api/projects/po-project`;
`PORTFOLIO_FOUNDRY_DEPLOYMENT=gpt-4-1-mini`; `PORTFOLIO_MODEL_MAP=scratchpad/major2-live/model_map.json`;
`PACE_SECONDS=2`. **Klientprobe FØR armene: `tests/test_foundry_profile_live.py` → 1 passed** (ikke
skippet). Gratis `--live-dry-run` på alle fire først, alle rc 0.
Parametrene er P18s (`--max-rounds 3 --max-tokens 600000`), uendret for sammenlignbarhet.
### 4.1 Rådata, alle tre runder gjennom SAMME dommer
| kjøring | grunnet | req_hit | named | halluc | prosa | filter | paged | tokens | stopp | a4 |
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- | --- |
| fv412-01 | 0 | 0 | 0 | 3 | 3 | 0 | 0 | 523 633 | absent | ✅ |
| gate-01 | 0 | 0 | 0 | 4 | 4 | 0 | 0 | 509 310 | absent | ✅ |
| sorasen-01 | 0 | 0 | 1 | 5 | 3 | 0 | 0 | 104 905 | absent | ❌ |
| tunnel-01 | 0 | 0 | 0 | 3 | 1 | 0 | 0 | 255 418 | absent | ✅ |
| fv412-02 | 0 | 0 | 0 | 5 | 5 | 0 | 0 | 44 468 | absent | ✅ |
| gate-02 | 0 | 0 | 0 | 3 | 2 | 0 | 0 | 35 406 | absent | ✅ |
| gate-03 | 0 | 0 | 0 | 4 | 2 | 0 | 0 | 89 911 | absent | ✅ |
| sorasen-02 | 0 | 0 | 1 | 4 | 4 | 0 | 0 | 73 627 | absent | ✅ |
| tunnel-02 | 0 | 0 | 0 | 5 | 2 | 0 | 0 | 45 642 | absent | ❌ |
| **fv412-04** | 0 | 0 | 0 | 4 | **1** | **38** | 5 | 287 883 | rounds | ✅ |
| **gate-04** | 0 | 0 | 0 | 4 | **1** | **13** | 2 | 140 695 | rounds | ✅ |
| **gate-05** | 0 | 0 | 0 | 4 | **0** | **10** | 4 | 51 257 | rounds | ✅ |
| **tunnel-04** | **1** | 0 | 0 | 4 | **0** | **4** | 4 | 59 372 | rounds | ❌ |
| **sorasen-04** | — | — | — | — | — | — | — | — | rounds | — |
`sorasen-04` ble **REFUSERT av dommeren** (`EmptyMeasurement`): kjøringen døde på rundetaket
(`rounds limit=12 observed=13`) etter 11 parse-feil — modellen foreslo `claimed_saving_nok: 0` elleve
ganger — og etterlot intet forslags-artefakt. Dommeren nekter å rapportere «0 hallusinasjoner» om en
tom utboks. **`{run_id}-coverage.json` ble likevel skrevet, med `stop_reason: "rounds"`** — det er
nøyaktig kjøringen DEL D2 finnes for.
### 4.2 Hovedtallet: fasit-konsepter ÅPNET
| runde | åpnet | nevner |
| --- | ---: | ---: |
| 1 | **0** | 26 |
| 2 | **0** | 26 |
| **3** | **1** | **32** (gate kjørte to ganger) |
Treffet er `krav/N500/id-bfb0edb4-…` i `tunnel-hauglia-2027-04`, som gjorde a1 `grounded=True` for
første gang i tre runder. **Det er bevegelse, og det er lite.** 1 av 32 er ikke et resultat noen
skal bygge en påstand på.
### 4.3 Det verktøyet FAKTISK gjorde
**En LEVENDE modell kalte `declare_requirement` i 5 av 5 kjøringer** (1–2 ganger hver). Eksempel fra
r761: `{"bundle_id": "vegnormal-r761-2025", "path": "R761/kapittel/4-3/id-7c6d5921-….md",
"ref": "4.3"}`. Den brukte også filteret tungt (`filter='krav'`, `'bindende'`, `'bind'`) — 4 til 38
filtrerte kall per kjøring, mot **0 i runde 1 og 2**, der sporet ikke kunne se det.
**Men `requirement_hit` er 0 i 5 av 5:** ikke én erklæring traff fasitens konsepter. Modellen
navngir et krav, leser det først (nekten tvinger det), og velger likevel feil dokument.
`declare_requirement` flyttet altså **hvorvidt** et krav navngis, ikke **hvilket**.
### 4.4 Varians: 04 mot 05 på samme sett
`gate-nordvik-2027-04` og `-05` er samme sett, samme base, samme parametre:
| | 04 | 05 |
| --- | ---: | ---: |
| tokens | 140 695 | 51 257 (**2,7×**) |
| verktøykall | 30 | 18 |
| filtrerte kall | 13 | 10 |
| åpnede dokumenter | 6 | 2 |
| erklærte krav | 2 | 1 |
| utfall | 4 rejected | 4 rejected |
**Utfallet er identisk, forbruket 2,7×.** Én kjøring per sett er ikke et utvalg, og det er den
viktigste enkeltsetningen i denne seksjonen.
---
## 5. Hva hver del kjøpte — målt, ikke tilskrevet
| del | målbar effekt |
| --- | --- |
| A | `declare_requirement` kalt av en levende modell i **5/5**; `requirement_hit` **0/5** |
| B | r761s tilbud **3 → 2 332**; to runde-2-artefakter `validated → rejected` offline; prosa-koder per kjøring **1–5 → 0–1** |
| C | filtrerte kall lesbare for første gang: **0 → 4…38** |
| D1 | forbruket lesbart per kjøring for første gang (rettelse av P18 funn 4) |
| D2 | `sorasen-04` er den eneste kjøringen i tre runder som SIER hvorfor den stoppet |
**Ikke tilskrevet:** at prosa-kodene faller fra 1–5 til 0–1 er IKKE bevist å være B3s fortjeneste.
B3 fyrte **aldri** i runde 3 — hver avvisning kom fra P7 («appears nowhere in the input»), som
fyrer FØRST. Modellene produserte koder som ikke står i basen i det hele tatt. At de samtidig
sluttet å produsere prosa-koder som STÅR der, er en observasjon om fem kjøringer, ikke en effekt
som er isolert.
---
## 6. Gjenstående stygt — hvert funn med en navngitt løsning
### F1. Et KRAVNUMMER blir akseptert som en kostkode
`tunnel-hauglia-2027-04` sitt `a4-enhetspris-ventilator` VALIDERTE på koden **`10.4`** — et
kravnummer fra N500, ikke en kostlinje. Det er grunnet (står i basen), det har en identifikator-form,
og B3 slipper det gjennom. Formene kan ikke skille «identifikator for et KRAV» fra «identifikator for
en KOSTLINJE», fordi en base uten prisskjema ikke har noen av de siste.
`must_refuse`-armen faller derfor for andre runde på rad på nøyaktig dette settet.
**LØSNING (finnes allerede, ikke aktivert): `--require-cost-baseline`.** MÅLT 15.09: med flagget
nekter nøyaktig denne kjøringen **før første modellkall**, med rc 1 og NOK 0. F4 gjorde det opt-in
med vilje (hver commons-eid golden er uforankret), og **valget om å gjøre det til stressrundens
default er operatørens**. Anslag: 0 kodelinjer, én rad i kjørekommandoen.
**ALTERNATIV LØSNING (bygges, ~1 økt):** la `code_forms` skille en tredje verdi `requirement` —
en kode som matcher form 2 eller 3 OG står som `req_number` i basens frontmatter er et krav, ikke
en kostlinje — og la 0b nekte den når kjøringen er uforankret. Risiko: r761s prosessnumre ER både
krav og oppgjørsposter, så regelen ville nektet nøyaktig det korpuset den er mest relevant for.
**Anbefaling: `--require-cost-baseline` først, mål så om alternativet fortsatt trengs.**
### F2. Modellen navngir et krav, men ikke det riktige
`requirement_hit` 0 av 5. Nekten tvinger fram en LESNING, ikke en RELEVANS.
**LØSNING (~1 økt):** la `declare_requirement` returnere kravets egen `title`/`req_number` fra
frontmatter i svaret, og la mandatets `success_criteria` nå hypotese-prompten — i dag når den bare
annonseringen. Anslag: to sømmer, én ny gate, ingen ny flate. **Ikke bygget her: det er en ny
beslutning om hva som skal inn i prompten, ikke en fiks av noe målt ødelagt.**
### F3. Rundetaket kapper hver kjøring
`stop_reason: rounds` i **5 av 5**. `own-proposal` ble aldri evaluert i noen kjøring i noen runde.
Taket er `max_rounds * 4 = 12` genererings-runder, og fire bestilte approaches bruker minst fire av
dem — flere når en parse-feil brenner en.
**LØSNING (0 kodelinjer):** `--max-rounds 5` gir 20 runder. Kostnaden er lineær i antall approaches,
ikke i korpuset. **Operatørbeslutning, fordi den øker regningen.**
### F4. `claimed_saving_nok: 0` brenner et helt budsjett
r761 brant 11 av 12 runder på at modellen foreslo null besparelse. Pydantic nekter `> 0`, og
`_fetch_parsed` prøver på nytt med samme prompt.
**LØSNING (~0,5 økt):** mat `ValidationError`-grunnen inn i neste forsøks prompt — Steg 5s mekanisme
finnes allerede for validator-avvisninger (`prior_rejection`), men en PARSE-feil går ikke den veien.
Anslag: én ny parameter på `_build_messages`, én gate.
---
## 7. Kostnad — anslag med uttalt antakelse
Målt forbruk, runde 3: **539 207 tokens** over de fire kjøringene som etterlot et artefakt.
`sorasen-04` er **ikke målt** (den døde før noe forslag ble skrevet, så ingen `token_usage` finnes) —
og dét er en ærlig luke, ikke en null.
| runde | kjøringer | målte tokens |
| --- | ---: | ---: |
| 1 | 4 | 1 393 266 |
| 2 | 5 | 289 054 |
| 3 | 4 (+1 umålt) | **539 207** |
**Runde 3 er dyrere enn runde 2**, og det er forventet: modellen navigerer nå mye mer (4–38 filtrerte
kall mot 0). **ANTAKELSE, ikke måling:** ved samme listepris som P18s anslag (289 k ≈ NOK 2) er
runde 3 ≈ **NOK 4**. Ingen faktura er lest.
---
## 8. Verifisering
| sjekk | resultat |
| --- | --- |
| `uv run pytest -q` | **1744 passed / 5 skipped** |
| `shasum -a 1 tests/golden/demo-transcript.stdout` (INNHOLD) | `ea8c534773acdbe41ae68f2c55724d69aaf8be4f` |
| `uv run ruff check src tests` | All checks passed |
| `uv run ruff format --check src tests` | 210 files already formatted |
| `uv run mypy src` | Success: no issues found in 38 source files |
| mutasjoner A/B/C+D | 4 + 4 + 4 = **12, alle røde mot HELE suiten** |
| klientprobe før betalte armer | `test_foundry_profile_live.py` 1 passed |
| gratis dry-run, fire sett | rc 0 alle fire |
---
## 9. Ærlighets-grenser
* **1 av 32 fasit-konsepter er ikke et resultat.** Det er bevegelse fra 0, og fem kjøringer.
* **Varians ikke målt utover ett par.** 04/05 på gate viser 2,7× forbruksforskjell ved identisk
utfall; ett par er ikke et utvalg.
* **B3 fyrte aldri levende.** Gaten er bevist på to REPLAYEDE artefakter, ikke på en kjøring der den
faktisk avgjorde utfallet.
* **`prose_codes` falt uten at årsaken er isolert.** Se § 5.
* **`sorasen-04`s forbruk er ukjent** — artefaktet som bærer tallet ble aldri skrevet.
* **Ingen faktura lest.** Alle kronebeløp er anslag fra listepris.
* **Formene er transkribert fra FIRE korpus.** Et femte kan bære en femte form; en ukjent form
klassifiseres som prosa, og feilretningen er derfor nekt — dét er hva generalitetsvernet
(tilbud ≥ 1) og baseline-unntaket finnes for.
* **`token_usage` er kjøringens ENE teller** — den skiller ikke debatt fra generering.

View file

@ -1,267 +0,0 @@
# P20 — kravet som er riktig, kravnummeret som ikke er en pris, parse-feilen som ikke brenner runder
**Ordre `20260915T031313Z-7299025254`, økt 124, 15.09.2026.** Fire navngitte løsninger bygget og
målt i én fjerde stressrunde over fem kontekstsett. Alt som står her er målt i denne økten; hvert
tall ordren oppgav er re-målt før noe ble bygget på det, og **to av ordrens egne premisser ble felt**.
---
## 1. Premisser ordren oppgav, og hva målingen sa
| ordrens premiss | målt 15.09 | status |
| --- | --- | --- |
| Suite 1781/5, golden `ea8c534…` | 1781/5, `ea8c534773acdbe41ae68f2c55724d69aaf8be4f` | ✅ |
| 8 upushede commits | `git rev-list --count origin/main..HEAD` = 8 | ✅ |
| `requirement_hit` 0/9 (runde 3) + 0/4 (P17b) | bekreftet i utboksene | ✅ |
| a4 validert på `10.4` (n500) og `1.10.4` (r761) | bekreftet i begge artefakter | ✅ |
| `sorasen-04`: 11 parse-feil, alle `claimed_saving_nok` ≤ 0 | bekreftet | ✅ |
| `stop_reason: rounds` 5/5 i runde 3 | bekreftet | ✅ |
| **B1: koden «står som `req_number`/`prosessnr` i toppnivå-frontmatter»** | **FEIL for BEGGE kjent-positive** | ❌ felt |
| **A1: kravet i `_INSTRUCTIONS[HYPOTHESISER_ROLE]`** | hypotesisereren kjører ikke i en stressrunde | ❌ felt (P19s egen, gjentatt) |
### 1.1 Premisset som ble felt, med tallene
Ordrens DEL B ber om: *en kode med form 2/3 **OG** som står som `req_number`/`prosessnr` i
toppnivå-frontmatter → nekt.* Målt mot de to kjent-positive ordren selv navngir:
* **`10.4`** (n500): basen erklærer `seksjon: 10.4.1` … `10.4.4` og `req_number: Krav 10.4.3—2`.
Den bare `10.4` er erklært **ingen steder** — den er et seksjons-PREFIKS, og forekommer i 12 av
274 dokumenter;
* **`1.10.4`** (r761): basen erklærer 2 727 `prosessnr` og 2 753 `seksjon`. **Ingen** av dem er
`1.10.4`. Tokenet står **én gang i 2 756 dokumenter**, som prosa: «Krav til materialer skal være
iht. vegnormal N200 Vegbygging kap. 1.10.4».
Den ordrede regelen fyrer altså på **ingen** av sine egne kjent-positive. **KOMPLEMENTET** fyrer på
begge, og lukker et hull `_ground_against_input`s egen docstring alt innrømmer skriftlig — «it fails
OPEN … on a coincidental match». Regelen som er bygget:
> UFORANKRET kjøring **+** kravformet kode **+** basen erklærer en nummer-ordliste **+** koden er
> **IKKE** i den → nekt, med nevner.
Komplementet er også dét som **sparer** det ene settet bygget på ekte prosesskoder: alle fem kodene i
`contexts/kontrakt-sorasen-2027` (`12.1`, `12.12`, `22.1`, `52.11`, `51.1`) ER erklærte `prosessnr`
og passerer. Under den ordrede regelen ville hver av dem blitt nektet på en uforankret r761-kjøring,
og settets positive armer blitt umålbare — R761-risikoen ordren selv navngir, ankommet gjennom døra
den ble pekt bort fra.
**Offline replay over ALLE 24 koder i runde 3 + P17b (10 kjøringer):** nøyaktig to er kravformede,
de er de to kjent-positive, og replayen flipper nøyaktig de to (`validated → rejected`) mens 22 står
uendret.
---
## 2. Hva som er bygget
### DEL A — kravet som er riktig
`declare_requirement` svarer nå med **dokumentets egne** `title` og `req_number`, lest av basen
gjennom `okf.reference_number` (ÉN leser, kø-(p)), pluss `binds`-setningen som sier hva erklæringen
forplikter. Lest av `Bundle.context_files`, så en `type: verdict`-fil aldri kan navngis tilbake ved
tittel. En sti basen ikke bærer som konsept (`index.md`) svarer med tomme strenger, aldri en nekt:
lese-sporet har alt godkjent erklæringen.
`mandate.criteria_block` er ENESTE renderer av kommisjonens `success_criteria` inn i en prompt, og
den når **debattens** task-melding — prompten der `declare_requirement` finnes. Tom uten kriterier,
så hver ukommisjonert prompt (demoens inkludert) er byte-identisk. **A2s plassering er målt:** på
utforskningsstien finnes ingenting å bære — `main()` sender `explore()` ingen `success_criteria`, så
objektivet ER prompten.
Instruksjonen og verktøybeskrivelsen navngir nå `read_dir(filter=…)`-veien med et verket eksempel.
### DEL B — kravnummeret som ikke er en pris
`Grounding.declared_references` (DEFAULTET) bærer basens egen nummer-ordliste, komponert i SAMME
vandring som dokumentene og propagert gjennom `_grounding_text`.
`okf.REFERENCE_NUMBER_FIELDS` = `_FILTER_FIELDS` + `seksjon` — og BEVISST ikke lagt til i
`_FILTER_FIELDS` selv, fordi hva et `filter`-ord søker i er målt og gatet (P18).
`code_forms` får en tredje verdi, `requirement`, for en kode basen FAKTISK erklærer; gaten fyrer på
komplementet. `anchored` er et EKSPLISITT flagg, aldri `not anchored_codes`.
### DEL C — parse-feilen, og annonseringen
`_fetch_parsed` tar en **bygger** i stedet for en ferdig meldingsliste, så retryen bærer grunnen.
Per RETRY, tom på forsøk 1 → attempt 1 byte-identisk. Den verbatime fangsten (Fase 1b funn 1) urørt.
`announced_subject` navngir de rutede basene; en base som ikke lar seg løse faller tilbake til
katalognavnet (`dimension_label`-presedensen: annonsering endrer aldri hvilken feil en operatør ser).
**Verifisert på den frie turen:** `Run mandate for vegnormal-n200-2024, vegnormal-r761-2025`.
### FUNN UNDERVEIS, FIKSET: `code_forms` beskrev feil kandidat
MÅLT i BÅDE runde 3 og runde 4: hvert per-approach-artefakt bar den SELEKTERTE kandidatens koder.
`stamp.model_copy(update={...})` overstyrte kun `validator_decision`. Feltets eget kommentar sier at
det er stemplet «off the proposal being stamped» — run-nivå var driften, ikke intensjonen. Følgen var
at `stress.py`, som leser feltet FØRST, rapporterte **tom `prose_codes` for hver approach unntatt
den første** i runde 3s tabell. Fikset; egen arm, rød mot den gamle oppførselen.
---
## 3. DEL D — stressrunde 4: fem betalte kjøringer
Miljø: endepunkt INLINE fra `az cognitiveservices account show`; `gpt-4-1-mini`; `PACE_SECONDS=2`;
`PORTFOLIO_MODEL_MAP=scratchpad/major2-live/model_map.json`. **Klientprobe FØR armene:
`tests/test_foundry_profile_live.py` → 1 passed** (ikke skippet). Gratis `--live-dry-run` på alle
fem først, alle rc 0. Parametre: **`--max-rounds 5 --max-tokens 600000`** (F3). Sammenlignbarheten
med runde 3 er bevisst tapt; det som måles er om `own-proposal` og a4 nå BLIR evaluert.
`--docs-dir` er **ikke** oppgitt: P18/C1 gjorde den valgfri når `--bundle-dir` er gitt, og runde 4 er
første betalte kjøring på den formen.
### 3.1 Rådata
| kjøring | rader | grunnet | req_hit | named | halluc. koder | gjettede stier | filter | paged | tokens | stopp | a4 |
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- | --- |
| gate-nordvik-06 (n100) | 4 | 0 | 0 | 0 | 6 | 0 | 17 | 2 | 96 254 | — | ✅ |
| tunnel-hauglia-06 (n500) | 4 | 0 | 0 | 0 | 4 | 0 | 7 | 4 | 68 993 | — | ✅ |
| fv412-06 (n200) | 4 | 0 | 0 | 0 | 4 | 0 | 16 | 3 | 87 568 | — | ✅ |
| kontrakt-sorasen-06 (r761) | 4 | **1** | 0 | **1** | 4 | **14** | 3 | 1 | 435 450 | — | **❌** |
| lindaas-02 / n200 | 2 | 0 | 0 | 0 | 2 | 0 | 5 | 3 | 63 773 | — | n/a |
| lindaas-02 / r761 | 2 | **1** | 0 | 0 | 3 | 3 | 4 | 2 | 144 454 | — | ✅ |
Veggtid 87–463 s, maks RSS 141–297 MB, **rc 0 i alle fem**.
### 3.2 Hovedtallene, alle fire runder
| runde | fasit-konsepter ÅPNET | a4 `must_refuse` | `stop_reason: rounds` | `own-proposal` evaluert | parse-feil |
| --- | ---: | ---: | ---: | ---: | ---: |
| 1 | 0 / 26 | — | 3 av 7 | 0 | — |
| 2 | 0 / 26 | — | 0 av 5 | 0 | — |
| 3 (fire sett) | 1 / 26 | 2 av 4 feilet | **5 av 5** | **0 av 5** | 11 rader i én kjøring |
| P17b (lindaas) | 0 / 6 | 1 av 1 feilet | 0 av 1 | 0 av 1 | 0 |
| **4 (fem sett)** | **2 / 32** | **1 av 5 feilet** | **0 av 6** | **6 av 6** | **1 rad i hver av 4** |
Runde 3 + P17b er til sammen **1 av 32** over de samme fem settene, mot runde 4s **2 av 32**.
**`requirement_hit` er fortsatt 0** — 13 erklæringer over seks utbokser, ingen av dem et
fasit-konsept.
**Og et tall som gikk NED:** `read_file`-kall per sett er **1 / 1 / 1 / 13 / 7** i runde 4 mot
**6 / 3 / 7 / 6** i runde 3 for de fire enkeltsettene. Tre av fire sett åpnet altså FÆRRE dokumenter
enn i runde 3, samtidig som `filter`-kallene holdt seg oppe (17 / 7 / 16 / 3). Navigatøren filtrerer
og leser så ett dokument. Ingen av delene i denne ordren kan tilskrives det — én kjøring per sett, og
begge retninger er innenfor variansen runde 2/3 viste (gate 6 → 13 → 1 over tre runder). Det er
likevel dét som gjør G3 til den bindende gjenstående saken.
### 3.3 Hva hver del kjøpte — målt, ikke tilskrevet
**DEL B kjøpte de to kjent-positive, og fyrte LIVE.** `lindaas-02` / r761 `a4-indeksregulering` —
NØYAKTIG den approachen P17b bar til `validated` på `1.10.4` — ble avvist på de NYE kodene
`1.10.8.3`/`1.10.8.4` med den nye nevneren:
> ungrounded identifier `'1.10.8.3'`: it is shaped like a requirement or process number, but it is
> not one of the **2765** this knowledge base declares (for example 1, 10, 11) — it was matched in
> prose by coincidence, and an unanchored base carries no price for a clause number
Tunnel-settets a4 falt denne runden på prosa-formen (`impulsventilator`), ikke på B — så B er bevist
på ÉN levende kjøring, ikke to.
**DEL C kjøpte 11 → 1.** Fire av seks utbokser bærer nå en `-parse-failures.json` med **nøyaktig én**
rad, hver med sin EGEN feil, og hver av de fire kjøringene produserte deretter et forslag. Runde 3s
`sorasen-04` hadde 11 rader med samme feil og døde på taket. Tre av de fire rettede feilene er
«claimed exceeds affected items' total» — altså rettet modellen seg etter grunnen den fikk.
**DEL C/C2 er verifisert gratis** (annonseringen navngir basene) og er uten betalt virkning.
**F3 (`--max-rounds 5`) kjøpte begge tingene ordren ba om:** `stop_reason` er tom i alle seks (runde
3: `rounds` i 5 av 5), og **`own-proposal` ble evaluert i alle seks** (runde 3: `not_evaluated` i
alle). Det er den klart største enkelteffekten i denne runden.
**DEL A kjøpte INGENTING målbart, og målingen sier hvorfor.** Tre av fire enkeltbase-kjøringer
erklærte basens FØRSTE krav — `Krav 1.2—1` (n100), `Krav 1.1—1` (n500), `Krav 1.1.1—1` (n200) — og
åpnet **nøyaktig ett** dokument med `read_file`. Svaret med dokumentets egen tittel kan ikke rette en
erklæring modellen aldri revurderer: den erklærer det ene dokumentet den har lest. Sorasen leste 13
dokumenter og erklærte `4.3` og `12.11`, altså nærmere, men fortsatt ikke en fasit-sti.
### 3.4 Kostnad
896 492 målte tokens over fem kjøringer. **ANTAKELSE, ikke måling:** ved samme listepris runde 2/3
ble anslått med (289 k ≈ NOK 2) er runde 4 ≈ **NOK 6** (ordren anslo ≈ NOK 8). **Ingen faktura er
lest.**
---
## 4. Gjenstående stygge funn, hver med en navngitt løsning
**(G1) `12.11` validerte tre ganger på sorasen, og a4 feilet `must_refuse`.** Koden er en EKTE
erklært `prosessnr` (og `seksjon`) i r761, så B slipper den gjennom ved konstruksjon — og modellen
la 225 000 NOK indeksregulering på den. **Og verre:** a2 (massebalanse), a3 (planum) og a4
(indeksregulering) svarte ALLE TRE med samme kostlinje `12.11` — «Tilrigging» — altså tre ulike
kommisjoner besvart med samme post. Trekk A3s «kvantifiser DENNE tilnærmingen» ble ikke fulgt, og det
er dommerens `hallucinations: ["code:12.11"]` på alle tre rader som fanger det. **Løsning, OPERATØRVALG (ikke bygget):** enten (a) utvid
gaten til å nekte **enhver** kravformet kode på en uforankret base — fanger `12.11` og `1.1.1`, men
gjør `contexts/kontrakt-sorasen-2027` (settet med ekte prosesskoder) umålbart, altså ~0 kodelinjer og
ett tapt sett; eller (b) gjør `--require-cost-baseline` til default for stress-kjøringer — nekter
hver vegnormal-base før første kall (P17b § 5.2: rc 1, 2,1 s), altså ingen stresstest i det hele
tatt; eller (c) skaff en priset base (MAJOR-4s `--derive-cost-baseline` finnes, men K2s prisskjema
renderes som en SIMPLE table med ÉN kolonneoverskrift og nektes — det er MAJOR-4s egen
ærlighetsgrense). **Anbefalt: (a)** for stress, med settet flyttet til en forankret base når en
finnes. Anslag: ~0,3 økt.
**(G2) `1.1.1` validerte på lindaas/n200.** Samme klasse som G1 fra motsatt side: n200 erklærer
`seksjon: 1.1.1`, så koden ER i ordlista. Samme løsning som G1.
**(G3) `requirement_hit` er fortsatt 0, og bindingen er at navigatøren åpner ETT dokument.** Målt:
1, 1, 1, 13 og 7 `read_file`-kall over de fem kjøringene. **Løsning (ikke bygget):** krev at
erklæringen kommer ETTER minst *k* leste dokumenter, eller la `declare_requirement` NEKTE en
erklæring av et dokument som ikke matchet noe `filter`-ord fra approachens label — begge er en nekt
med nevner, i funn-99-formen. Anslag: ~0,5 økt.
**(G4) 17 gjettede lesestier er tilbake** (sorasen 14, lindaas/r761 3), etter at P18 målte 0.
Alle er `R761/4-3`-formede, altså katalognavn utledet av et prosessnummer. **Løsning (ikke bygget):**
P18/A3-nekten navngir nærmeste listbare katalog — la den også navngi de *n* nærmeste
UNDERKATALOGENE, som er hva en kaller som gjettet `R761/4-3` trenger. Anslag: ~0,3 økt.
**(G5) `requirement_source` er `run` for hver rad.** Debatten erklærer ÉN gang per kjøring, så en
erklæring kan ikke tilskrives én approach. Uttalt i P19 DEL A og fortsatt sant.
---
## 4b. Load-bearing: mutasjonsbatteriet
Grønn kontroll **1809 passed / 5 skipped** (fra 1781/5, **+28, 0 fjernet**), golden
`demo-transcript.stdout` BYTE-UENDRET (`shasum -a 1` av INNHOLDET =
`ea8c534773acdbe41ae68f2c55724d69aaf8be4f`, ALDRI git-blob-id-en), `ruff` og `mypy` rene.
Seksten mutasjoner, hver kjørt mot HELE suiten:
| # | mutasjon | røde |
| --- | --- | ---: |
| A-i | svaret ekkoer argumentene igjen (ingen `title`/`req_number`) | 3 |
| A-ii | kriteriene når aldri debattens task-melding | 1 |
| A-iii | `criteria_block` skriver blokka også uten kriterier | 1 |
| A-iv | instruksjonen navngir ikke `filter`-veien | 1 |
| B-i | gaten fyrer også når kjøringen ER forankret | 1 |
| B-ii | gaten ignorerer ordlista (rent form-treff) | 5 |
| B-iii | gaten detachet | 3 |
| B-iv | nekten dropper nevneren | 2 |
| B-v | `classify_codes` svarer aldri `requirement` | 4 |
| B-vi | kjøringen komponerer ingen ordliste | 2 |
| B-vii | ordlista overlever ikke `_grounding_text` | 1 |
| B-viii | `okf.declared_reference_numbers` leser ingen nøkler | 8 |
| B-ix | per-approach-artefaktet bærer kjøringens `code_forms` igjen | 1 |
| C-i | retryen bygger prompten uten grunnen | 1 |
| C-ii | parse-blokka skrives aldri inn i prompten | 2 |
| C-iii | annonseringen sier «the portfolio» igjen | **0 → 1** |
**C-iii VAR GRØNN FØRST — repoets vakuøs-gate-klasse.** De tre C2-armene drev `announced_subject`
DIREKTE, så kallstedet i `main()` hadde intet vitne: å reversere det til
`args.project_id or "the portfolio"` lot HELE suiten stå grønn (1808/5 på det tidspunktet). Armen som manglet driver nå
`main()` på en fri dry-run og leser annonseringen av **stdout**, der en operatør leser den — og er rød
mot nøyaktig den mutasjonen.
**To av ordrens spådde signaturer ble FALSIFISERT av målingen:** A-iii skulle gjøre golden rød, men
demoen kjører uten mandat, så `criteria` er tom uansett og bare rendererens egen arm faller; og A-iv
(instruksjonen) treffer én arm, ikke goldenen.
## 5. Ærlighetsgrenser
* **Ingen faktura lest.** Alle kronebeløp er anslag fra listepris.
* **Én modell, ett deployment, én kjøring per sett.** Ingen av tallene er et utvalg.
* **DEL B er bevist på ÉN levende kjøring.** De to kjent-positive er bevist OFFLINE mot de ekte
basene; live fyrte gaten på lindaas/r761 og ingen andre.
* **DEL A har ingen målbar betalt virkning i denne runden**, og § 3.3 sier hvorfor. At svaret ville
hjelpe en modell som faktisk leser mer enn ett dokument er IKKE vist.
* **Sammenlignbarheten med runde 3 er tapt med vilje** (`--max-rounds` 3 → 5).
* **`prose_codes` i runde 3s tabell var målt på et feilattribuert felt** (§ 2, FUNN). Runde 4s
tall i § 3.1 er re-derivert per approach fra kandidatens EGNE koder.
* **`--docs-dir`-formen endret seg** mellom runde 3 og 4 (P18/C1 gjorde flagget valgfritt). Ingen
målt forskjell, men det er en endret kommando.

View file

@ -1,205 +0,0 @@
# P21 — stressrunde 5, FORANKRET: fem betalte kjøringer mot prosjektets eget prisskjema
**Dato:** 2026-09-15 · **Ordre:** `20260915T063839Z-5960711173-from-.claude` · **Økt:** 125
**Kode:** `7b4f85d` (DEL A+B) · `ac0bfdb` (DEL C) · rapporten i denne commiten
**Miljø:** Foundry `gpt-4-1-mini`, `PACE_SECONDS=2`, endepunkt hentet INLINE fra `az`
(ekte vert aldri i sporet fil). Klientprobe: 1 passed før første betalte kall.
**Skript:** `scratchpad/p21/stress5.sh` · `scratchpad/p21/judge5.sh` · `scratchpad/p21/rejudge4.sh`
---
## 0. Kortversjonen
Fire betalte runder hadde kjørt **uforankret, alle sammen**: validatorens stadium 0 — det ene
stadiet som skiller en oppdiktet kostlinje fra en linje prosjektet faktisk kjøper — ble hoppet over
i hver eneste, fordi prisen ble lett etter i KUNNSKAPSBASEN og en vegnormal bærer ingen priser.
P21 ga prosjektet sitt eget prisskjema (`--cost-baseline`), og runde 5 er den første forankrede.
**Det forankringen kjøpte, målt:**
* **`must_refuse` (a4) bestått 5 av 5, og alle fem på `stage0-baseline`** — mot 4 av 5 i runde 4,
der alle fire som besto falt på stadium 0b (P7s grunnings-sjekk) og den femte VALIDERTE.
* **`stop_reason` tom 6 av 6**, `own-proposal` evaluert 6 av 6 — som i runde 4.
* **C1 fyrte LIVE og endret atferd:** distinkte dokumenter åpnet før en erklæring gikk fra
`1 · 1 · 1 · 2 · 5 · 13` til `3 · 3 · 5 · 7 · 11 · 12`. Tre erklæringer ble NEKTET underveis
(15 kall, 12 registrert), og modellen leste mer og erklærte på nytt.
* **C2:** `read_dir`-kall mot et nivå basen ikke holder falt fra **16 av 104 (15,4 %)** til
**8 av 128 (6,3 %)**.
**Og det den kostet, målt like tydelig: 0 av 20 tilnærminger validerte** (runde 4: 4 av 20). Hver
eneste av de 26 avvisningene — 20 tilnærminger + 6 `own-proposal` — leser
`unknown cost code '<noe>': not in project <P>'s cost baseline (N known codes)`. Ingen kjøring kom
forbi stadium 0, fordi **prisskjemaet aldri når prompten** og avvisningen oppgir **hvor mange**
koder prosjektet har, aldri **hvilke**. Det er § 4 funn 1, og det har en navngitt løsning.
---
## 1. Hva som ble kjørt
| sett | base(r) | run-id | utboks |
|---|---|---|---|
| gate-nordvik-2027 | n100-2023 | `gate-nordvik-2027-07` | `scratchpad/p21-stress/gate-nordvik-2027` |
| tunnel-hauglia-2027 | n500-2024 | `tunnel-hauglia-2027-07` | `…/tunnel-hauglia-2027` |
| fv412-dekkefornyelse-2027 | n200-2024 | `fv412-dekkefornyelse-2027-07` | `…/fv412-dekkefornyelse-2027` |
| kontrakt-sorasen-2027 | r761-2025 | `kontrakt-sorasen-2027-07` | `…/kontrakt-sorasen-2027` |
| dekke-og-kontrakt-lindaas-2027 | n200 + r761 (`--across-bundle`) | `lindaas-03` | `…/lindaas` |
Alle fem med `--max-rounds 5 --max-tokens 600000 --cost-baseline contexts/<sett>/cost-baseline.json`.
Fri `--live-dry-run` på alle fem først: rc 0 og `Cost baseline: N line(s) from …` i alle fem,
inkludert multi-base-passet, der linja printes ÉN gang for passet (ett prosjekt, ett prisskjema).
Alle fem betalte kjøringer: **rc 0**.
---
## 2. Runde 3 / 4 / 5, samme instrument
Runde 4 er **dømt på nytt med P21-dommeren** (`scratchpad/p20-stress/*/verdict-p21.out`), så de to
kolonnene under er målt med samme verktøy. Runde 3 står som publisert i P19/P20 og er IKKE
re-dømt — den mangler `anchored`/`priced`/`stage`, og å fylle dem inn fra prosa ville vært å dikte
et måleresultat.
| | runde 3 | runde 4 | **runde 5** |
|---|---|---|---|
| `anchored` | (ikke målt) | **False 6/6** | **True 6/6** |
| validerte tilnærminger | 1/32¹ | 4/20 | **0/20** |
| fasit-grunnet (a) | 1/32¹ | 2/20 | 1/20 |
| `requirement_hit` | 0/13 | 0/13 | **0/12** |
| `named` (b′) | — | 1/20 | 1/20 |
| hallusinerte koder | — | 23 | 26 |
| `priced` | — | 0/20 | **0/20** |
| a4 `must_refuse` bestått | 2/4 | 4/5 | **5/5** |
| a4 — hvilket stadium fanget | (ikke målt) | `stage0b` ×4, ingen ×1 | **`stage0-baseline` ×5** |
| gjettede lesestier (dommeren) | — | 17 | **14** |
| `stop_reason` tom | 0/5 | 6/6 | 6/6 |
| parse-feil-filer | 4 av 6 (1 rad) | 4 av 6 (1 rad) | 4 av 6 (2 rader) |
| tokens | 2 679 305 | 896 492 | **1 130 145** |
¹ runde 3 + P17b talte over 32 rader; runde 4 og 5 er 20 rader (fire sett à fire + to à to).
De to nevnerne er ulike og tallene er ikke direkte sammenlignbare — de står som publisert.
Per sett i runde 5: gate 102 048 · tunnel 178 157 · fv412 117 398 · sorasen 305 135 ·
lindaas/n200 133 635 · lindaas/r761 293 772.
**Kostnad, med antakelsen uttalt:** P20 anslo 896 492 tokens ≈ **NOK 6** til listepris for
`gpt-4-1-mini`. Samme antakelse, skalert: 1 130 145 tokens ≈ **NOK 7,6**. **Ingen faktura er lest**
— dette er et anslag, ikke en måling av hva som ble belastet.
---
## 3. Hva hver del kjøpte (mål, ikke tilskriv)
### DEL A+B — forankringen
**Kjøpte:** stadium 0 dømmer igjen. Det er ikke utledet fra at flagget ble sendt, men lest av
artefaktene: `provenance.cost_baseline_anchored` er `true` i alle seks utbokser, og alle 26
avvisninger bærer stadium 0s egen setning. Falsifiseringsarmen faller nå på det stadiet som vet
hva prosjektet KJØPER, i stedet for på det som bare vet hva som stod i teksten.
**Kjøpte IKKE:** en høyere andel riktige svar. Tvert imot — se § 4 funn 1.
**Ærlighets-grense, uttalt:** «a4 5 av 5» er sant og svakere enn det ser ut. Nevneren er en gate som
avviste 20 av 20, så armen måtte ikke SKILLE noe denne runden. Den skarpe formen av dette kriteriet
krever en runde der a1–a3 kan validere; den kommer først når funn 1 er lukket.
### DEL C1 — erklæringen som må ha lett
**Kjøpte:** at erklæringen koster en titt. Målt i de seks sporene:
| sett | distinkte dokumenter før erklæring (r4 → r5) | declare-kall r5 | registrert r5 |
|---|---|---|---|
| gate-nordvik | 1 → **3** | 2 | 2 |
| tunnel-hauglia | 1 → **5** | 3 | 2 |
| fv412 | 1 → **3** | 3 | 2 |
| sorasen | 13 → 12 | 2 | 2 |
| lindaas/n200 | 2 → **7** | 3 | 2 |
| lindaas/r761 | 5 → **11** | 2 | 2 |
**Tre erklæringer ble nektet live** (15 kall, 12 registrert), og i alle tre tilfellene leste
modellen mer og erklærte på nytt — nekten er en tur den kunne rette, som var hele formen.
**Kjøpte IKKE:** riktige erklæringer. `requirement_hit` er fortsatt **0 av 12**. Å lese tre
dokumenter i stedet for ett gjør ikke det tredje til det riktige. Se § 4 funn 2.
### DEL C2 — nekten som navngir naboene
**Kjøpte:** færre gjetninger på katalognivå. `read_dir` mot et nivå basen ikke holder falt fra
**16 av 104** til **8 av 128** — raten fra 15,4 % til 6,3 %. Den ellevedelte `R761/4-3`,
`4.3`, `4-2`, `4-1`, `4-0`, `4-5`, `4-6`-vandringen fra runde 4 gjentok seg ikke.
**Kjøpte IKKE:** færre gjettede DOKUMENT-stier. `read_file` mot et dokument basen ikke holder gikk
fra 2 av 38 til **7 av 52** (5,3 % → 13,5 %). Totalt gikk sti-bærende kall mot noe som ikke finnes
fra 18 av 143 til 15 av 180. Uttalt: to ulike definisjoner er i omløp i denne rapporten —
dommerens `hallucinated_reads` (filsystem-basert: 17 → 14) og målingen over
(`context_files`-basert: 18 → 15). Begge peker samme vei; ingen av dem er den andre.
---
## 4. Gjenstående funn, hver med en navngitt løsning
### FUNN 1 (nytt, og det største) — prisskjemaet når aldri prompten
**Målt:** alle 26 avvisninger er `unknown cost code`. Ingen av de 26 foreslåtte kodene står i
prosjektets skjema; modellen fant på `signalregulering_konstruksjon`, `elevated_crosswalks`,
`VENTIL_IMP`, `RIGG01`, `Filtermateriale`, `bærelag_asfalt`, `MASSE_FORSTERKNING` og tjue til.
Den kunne ikke gjort annet: skjemaet sendes til VALIDATOREN, aldri til proposeren, og stadium 0s
avvisning oppgir **antallet** kjente koder («5 known codes») og **ikke ett eneste kodenavn**. Steg 5
mater avvisningen ordrett inn i neste forsøks prompt — men en beskjed som sier «du gjettet feil,
det finnes fem riktige» kan ikke korrigeres. Kontrast MAGNITUDE-avvisningen, som NAVNGIR
baseline-verdien og derfor lot løkka konvergere i økt 94.
**Løsning (navngitt, anslag ~0,5 økt):** la stadium 0s ukjent-kode-avvisning NAVNGI kodene
prosjektet faktisk har, slik magnitude-armen allerede navngir verdien —
`…not in project P's cost baseline (known codes: RIGG, ASFALT, …)`. Ett sted (`validator.py`
`_reconcile_against_baseline`), ingen ny flate, og Steg 5 bærer det videre gratis. Ved store
skjemaer må listen bindes (samme regel som katalog-utdraget: et fast vindu, aldri en andel).
**Alternativ løsning (anslag ~1 økt), som IKKE anbefales alene:** rendre skjemaet inn i
proposer-prompten. Den gir modellen kodene før første forsøk i stedet for etter det første
bomskuddet, men den er en ny prompt-flate med egen kostnad per kjøring, og den fjerner ikke behovet
for at avvisningen er korrigerbar.
### FUNN 2 (uendret fra P19/P20) — `requirement_hit` er 0
**Målt:** 0 av 12 erklæringer navngir et fasit-konsept, tredje runde på rad. C1 gjorde at
kjøringene leser mer (1 → 3–11 dokumenter), og det flyttet ikke treffet.
**Løsning (navngitt, anslag ~0,5 økt):** `declare_requirement` svarer allerede med dokumentets EGEN
tittel og `req_number` (P20/A1). Neste trinn er å gjøre svaret til en SAMMENLIGNING: ta approachens
label og si om det erklærte kravet i det hele tatt handler om den — «du erklærte *Krav 1.1—1
Generelle krav* for *Enklere rundkjøring*; ingen ord fra labelen finnes i dokumentets tittel eller
`req_number`». Det er en rapport, ikke en gate (en gate på ordoverlapp ville nektet legitime
erklæringer), og den kan bygges og måles offline mot de seks sporene.
### FUNN 3 (svakere enn i runde 4, ikke lukket) — gjettede DOKUMENT-stier
**Målt:** `read_file` mot et dokument som ikke finnes gikk 2/38 → 7/52. C2 hjelper på katalognivå
og ikke her, fordi den nærmeste listbare forfaren til en gjettet dokumentsti ofte HOLDER dokumenter
og ingen underkataloger — og da utelates nabolista (bevisst; en tom klausul er en setning uten
innhold).
**Løsning (navngitt, anslag ~0,3 økt):** når forfaren holder dokumenter og ingen underkataloger,
navngi i stedet inntil fem av DOKUMENTENE på det nivået, rangert på samme måte. Samme funksjon,
samme kilde (`context_files` gjennom `in_dimension`), samme egenskap: hvert navn den gir fra seg
resolverer.
### FUNN 4 (uendret) — `named` er 1 av 20
**Målt:** bare sorasens `a3-planum-stabilisering` navnga et fasit-krav i sitt eget `measure`.
**Løsning:** ingen egen — dette er funn 2 sett fra den andre siden. Lukkes funn 2, er dette det som
måler om det virket.
---
## 5. Ærlighets-grenser
* **Fem betalte kjøringer på ett spørsmål er ikke et utvalg.** Én modell (`gpt-4-1-mini`), ett
deployment, ett manus.
* **Alle beløp i de fem prisskjemaene er OPPDIKTET.** Hvert setts `honesty`-felt sier det. Det som
IKKE er oppdiktet er FORMEN: hver a1–a3 har sin kostlinje, a4 har ingen, og sorasens koder er
ekte R761-prosessnumre fordi en norsk vegkontrakt prises slik.
* **«a4 5 av 5» er målt under en gate som avviste alt.** Se § 3.
* **Ingen faktura er lest.** NOK-tallet er et anslag med antakelsen uttalt.
* **Runde 3 er ikke re-dømt** og de tre kolonnene i § 2 er derfor ikke alle sammenlignbare.
* **Ordrens D2 ber om `priced` som hovedtall.** Den er 0 av 20, og det er ikke en egenskap ved
skjemaene: det er funn 1 målt fra den andre siden.

View file

@ -1,183 +0,0 @@
# P22 — stressrunde 6: de tre funnene fra P21 lukket, og hva lukkingen avdekket
> **⚠️ PRISENE ER OPPDIKTET.** Hvert beløp i de fem prisskjemaene under `contexts/*/cost-baseline.json`
> er en konstruert størrelsesorden, som hvert setts eget `honesty`-felt sier. **FORMEN er det som ikke
> er oppdiktet** — et prisskjema med `quantity`/`unit_cost` per kostkode er hva en norsk
> vegkontrakts mengdebeskrivelse faktisk er.
>
> **Fem betalte kjøringer på ett spørsmål er ikke et utvalg.** Seks kjøringer over fem prosjekter,
> én modell, ett deployment. Alt under er hva DENNE konfigurasjonen gjorde, ikke hva systemet gjør.
---
## 1. Kortversjon
P22 lukket de tre funnene P21 navnga, i rekkefølgen P21 satte, og kjørte så runde 6 forankret.
**DEL A traff hardt.** Stadium 0s ukjent-kode-nekt navngir nå kodene prosjektet faktisk kjøper.
`priced` gikk **0 av 20 → 16 av 20** og validerte tilnærminger **0 av 20 → 10 av 20**. Antallet
oppdiktede kostkoder falt fra 26 til 12.
**DEL B beveget det tallet tre runder ikke hadde beveget.** `declare_requirement` svarer nå med en
sammenligning mot kommisjonens egne retninger. Erklæringer som peker på et fasit-konsept gikk
**0 av 13 → 0 av 12 → 5 av 16**, og `requirement_hit` per tilnærming **0 av 20 → 3 av 20**.
**DEL C halverte gjettingen.** `read_file` mot et dokument basen ikke holder gikk **7 av 52 → 3 av
51**, `read_dir` mot et nivå den ikke holder **8 av 128 → 2 av 97**. To av de tre gjenværende
`read_file`-bommene fikk den NYE dokument-klausulen — altså fyrte DEL C levende.
**Og lukkingen avdekket noe større enn alt dette.** `must_refuse` — falsifiseringsarmen, den ene
tilnærmingen per sett som basen IKKE har grunnlag for — gikk **5 av 5 → 2 av 5**. Tre av dem ble
VALIDERT. P21s 5-av-5 var ikke skarp: den ble oppnådd fordi modellen fant på en kostkode, ikke
fordi kunnskapsbasen manglet grunnlag. Ordren forutså nøyaktig dette («i dag måtte armen ikke
skille noe, gaten avviste 20 av 20»). Nå skiller den, og den sier at **ingenting i gaten taler om
hvorvidt basen støtter retningen**. Det er funn 1 i § 4, og det står først.
---
## 2. DEL 0 — premissene re-målt før noe ble bygget
Ordren siterte seks tall fra P21-rapporten. Alle er re-målt mot artefaktene.
| Ordrens tall | Re-målt | Dom |
|---|---|---|
| 0 av 20 validerte | 0/20 tilnærminger — OG 0/6 egne forslag | **Holder.** Men neste setnings «26» er en STØRRE populasjon: 20 approach-rader + 6 own-proposals |
| 26 avvisninger, alle `unknown cost code` | 26/26 | **Holder** (populasjon = 26, ikke 20) |
| `requirement_hit` 0/12 | Feltet er per APPROACH: **0/20**. 12 er antallet ERKLÆRINGER (7 distinkte, 0 treff) | **Nevneren er feil som skrevet.** Begge er null, så konklusjonen står |
| gjettede dokumentstier 2/38 → 7/52 | **Holder** under `context_files`-definisjonen. Under dommerens FILSYSTEM-definisjon: 2/38 → **6/52** | To definisjoner i omløp, begge erklært av P21 selv |
| `read_dir` 16/104 → 8/128 | **Holder** under `context_files`. Filsystem: 15/104 → 8/128 | samme |
| `named` 1/20 | 1/20 | **Holder** |
| `priced` 0/20 | 0/20 | **Holder** |
**Klassifikator-grensen, validert i begge retninger som ordren krevde.** `priced` og
stadium-0-avvisningen er ikke to observasjoner — de er samme betingelse målt to ganger: `priced`
krever at HVER kode finnes i settets prisskjema, og stadium 0 avviser når NOEN kode ikke gjør det.
Over samme prisskjema er de komplementære ved konstruksjon. Ordrens «funn 1 målt fra den andre
siden» er altså strukturelt sant, ikke en observasjon. Ingenting lekker ut av klassen, og ingenting
ligger feilaktig inne i den.
Alle tall i denne rapporten er målt i denne økten. Runde 4 og 5 er dømt med **samme instrument** som
runde 6 (dommeren er urørt i P22, og begge er kjørt på nytt for å bevise det).
---
## 3. Sammenligningstabell, samme dommer
| | runde 4 (P20) | runde 5 (P21) | **runde 6 (P22)** |
|---|---|---|---|
| forankret (`anchored`) | 0 av 6 | 6 av 6 | **6 av 6** |
| validerte tilnærminger | 4/20 | 0/20 | **10/20** |
| `priced` (alle koder i prosjektets skjema) | 0/20 | 0/20 | **16/20** |
| oppdiktede kostkoder | 23 | 26 | **12** |
| `requirement_hit` (per tilnærming) | 0/20 | 0/20 | **3/20** |
| erklæringer som peker på et fasit-konsept | 0/13 | 0/12 | **5/16** |
| `named` | 1/20 | 1/20 | 1/20 |
| `grounded` | 2/20 | 1/20 | **3/20** |
| `read_file` mot dokument basen ikke holder | 2/38 | 7/52 | **3/51** |
| `read_dir` mot nivå basen ikke holder | 16/104 | 8/128 | **2/97** |
| gjettede lesestier i alt | 18/142 | 15/180 | **5/148** |
| `must_refuse` PASS | 4/5 | 5/5 | **2/5** |
| tokens | 896 492 | 1 130 145 | **913 320** |
| rc | 0 × 5 | 0 × 5 | **0 × 5** |
| rundetak truffet | 0 | 0 | **0** |
**Kostnad: ≈ NOK 6,1.** ANTAKELSE — listepris, skalert fra P21s egen antakelse på samme deployment.
**Ingen faktura er lest.**
---
## 4. Gjenstående funn, hver med en navngitt løsning
### FUNN 1 (nytt, og det største) — falsifiseringsarmen fanges ikke lenger av noe
**Målt:** `must_refuse` 2 av 5. Tre a4-armer ble VALIDERT:
| sett | tiltak | kostlinje | utfall |
|---|---|---|---|
| tunnel-hauglia | «Billigere enhetspris pa impulsventilator» | `TUN-VENT-01` 14 × 465 000 | validated |
| fv412 | «Lavere tonnpris pa resirkulert asfalt i bærelaget» | `DEKKE-BAER-01` 6 300 × 980 | validated |
| kontrakt-sorasen | «Kutt ved gunstigere indeksregulering» | `12.1` 1 × 6 400 000 | validated |
De to som falt, falt på `stage4-p90` (gate-nordvik) og `stage0-baseline` (lindaas/r761).
**Dette er en AVDEKKING, ikke en regresjon.** I runde 5 fanget stadium 0 alle fem — men fordi
modellen fant på kostkoden, ikke fordi basen mangler grunnlag for tiltaket. Ordren skrev det selv:
armen «måtte ikke skille noe». Nå bruker modellen den riktige koden, magnitudene ligger innenfor
toleransen, koden står i grunnlagsteksten (baselinens koder er én av stadium 0bs tre kilder), og
**ingen stage i gaten stiller spørsmålet falsifiseringsarmen er konstruert for**: har
kunnskapsbasen et krav som støtter denne retningen?
**Løsning (navngitt, anslag ~1 økt):** en stage som gater på ERKLÆRINGEN, ikke på tallene — et
forslag hvis tilnærming erklærte et krav som ikke deler ett ord med tiltaket, eller som ikke erklærte
noe krav i det hele tatt, kan ikke bære et `validated`-stempel. DEL B har allerede regnet ut
sammenligningen; det som mangler er å la den bety noe. **Dette er en operatørbeslutning**, ikke en
selvfølge: DEL B ble bygget som RAPPORT nettopp fordi et krav kan binde et tiltak uten å dele ett
ord med navnet noen ga det, og en gate på ordoverlapp ville avvist legitime forslag. En mellomting
finnes — gate på FRAVÆR av en erklæring, ikke på dens kvalitet — og den er billigere og ærligere.
### FUNN 2 — `named` står på 1 av 20, fjerde runde
**Målt:** 1/20, uendret gjennom fire runder. Ordren sa dette var funn 2 «sett fra den andre siden»,
og at DEL B ville måle det. **Den spådommen holdt ikke:** erklæringene ble klart bedre (0/12 → 5/16)
mens `named` sto stille. De er altså IKKE samme sak. `named` måler om modellens egen `measure`-tekst
eller et snippet navngir fasit-referansen; erklæringen er en egen kanal.
**Løsning (navngitt, anslag ~0,5 økt):** proposer-prompten ber om at `measure` gjengir tiltaket, ikke
kravet. Å be den gjengi det ERKLÆRTE kravets `ref` ordrett i `measure` — slik `approach.label`
allerede kreves gjengitt — koster én setning i `_build_messages` og gjør kanalen målbar. Merk at
dette gjør `named` til noe modellen blir BEDT om, hvilket svekker den som uavhengig måling; det er
en reell kostnad og skal uttales.
### FUNN 3 — tre gjettede dokumentstier står igjen
**Målt:** 3 av 51. To fikk DEL Cs nye dokument-klausul, én fikk underkatalog-klausulen. Ingen sto
uten hjelp. Restfeilen er altså ikke lenger «nekten sa ingenting», men «modellen gjettet likevel».
**Løsning:** ingen foreslått. 5,9 % er under runde 4s nivå, og en gate her ville rammet et
verktøykall som allerede blir besvart med de riktige navnene.
### FUNN 4 — `grounded` 3 av 20
**Målt:** 3/20 (runde 4: 2, runde 5: 1). Beveger seg svakt oppover. Ingen løsning foreslått i denne
runden; den henger sammen med funn 1 og bør vurderes sammen med den.
---
## 5. Hva hver del kjøpte, målt
### DEL A — nekten navngir kodene
**Kjøpte:** at løkka kan konvergere i det hele tatt. `priced` 0/20 → 16/20, validerte 0/20 → 10/20,
oppdiktede koder 26 → 12. Dette er den største enkeltbevegelsen noen del har produsert i seks runder.
**Kjøpte IKKE:** at forslagene er RIKTIGE. Se funn 1 — halvparten av bevegelsen er at
falsifiseringsarmen nå slipper gjennom.
### DEL B — erklæringen som sammenligner
**Kjøpte:** erklæringer 0/12 → 5/16, `requirement_hit` 0/20 → 3/20. Første bevegelse på tre runder.
**Kjøpte IKKE:** `named` (funn 2), og heller ikke en gate — sammenligningen er en rapport.
### DEL C — dokumentnavn når forfaren mangler underkataloger
**Kjøpte:** `read_file`-bom 7/52 → 3/51, `read_dir`-bom 8/128 → 2/97. Klausulen fyrte levende på
2 av de 3 gjenværende bommene.
---
## 6. Ærlighets-grenser
* **Fem betalte kjøringer på ett spørsmål er ikke et utvalg.** Seks kjøringer, fem prosjekter, én
modell, ett deployment. Ingenting her generaliserer til en annen modell.
* **Alle beløp i prisskjemaene er OPPDIKTET.** Formen er ikke.
* **Kostnaden er en ANTAKELSE** (listepris, skalert fra P21). Ingen faktura er lest.
* **`must_refuse` 2 av 5 er ikke bevis for at systemet er blitt verre.** Det er bevis for at den
forrige målingen ikke kunne skille. Begge utsagn er ubehagelige og begge er sanne.
* **Ingen del er bevist mot en annen modell.** At en LEVENDE modell korrigerer seg etter en navngitt
kodeliste er nå MÅLT for gpt-4-1-mini på dette deploymentet, og for ingen annen.
* **Runde 3 er ikke re-dømt.** Å fylle inn `anchored`/`priced`/`stage` fra prosa ville vært å dikte
et måleresultat.
* **De to lese-definisjonene er fortsatt to.** Alle tall i § 3 bruker `context_files`-definisjonen,
som er den DEL C er bygget fra. Dommerens `hallucinated_reads` er filsystem-basert og gir andre
tall for de samme kjøringene.

View file

@ -75,8 +75,8 @@ the *candidate measure*, and no connector can infer one from the other. A bundle
optional `cost-baseline.json`; without it the validator still runs, but unanchored to the optional `cost-baseline.json`; without it the validator still runs, but unanchored to the
project's real cost lines. Both are hand-authored today. For the IR projection the shape reference project's real cost lines. Both are hand-authored today. For the IR projection the shape reference
is `shared/examples/bygg-energi-mikro/validator-input.json`; for the cost baseline it is is `shared/examples/bygg-energi-mikro/validator-input.json`; for the cost baseline it is
`shared/examples/veglys-fv-soer/cost-baseline.json` — **two bundled examples ship one** (that one `src/portfolio_optimiser/data/bundles/klientpark-energi/cost-baseline.json` — **two bundled
and `tunnel-hauglia`, checked 2026-08-21), and its shape is `ir.CostBaseline`: a `project_id` plus examples ship one** (that one and `driftssenter-kjoling`), and its shape is `ir.CostBaseline`: a `project_id` plus
an `items` map of `{code: {quantity, unit_cost}}`. Writing them from ingested content is an `items` map of `{code: {quantity, unit_cost}}`. Writing them from ingested content is
unbuilt, and is not on the 90 %-principle side of the line: what candidate to propose is the unbuilt, and is not on the 90 %-principle side of the line: what candidate to propose is the
agents' job, not the connector's. agents' job, not the connector's.

View file

@ -41,12 +41,12 @@ a tautology — caught and rebuilt; see the Method note below.)
reference projects. MAF **accumulates the shared conversation thread across `.run()` reference projects. MAF **accumulates the shared conversation thread across `.run()`
calls**: each run's participants receive the PRIOR projects' prompts and replies too. calls**: each run's participants receive the PRIOR projects' prompts and replies too.
Measured (project ids visible per run, in order): Measured (project ids visible per run, in order):
`[[FV42-GSV-E1], [FV42-GSV-E1, RV13-RAS-TP], [FV42-GSV-E1, RV13-RAS-TP, BRU-LAKS-REHAB]]` `[[KONTOR-IT-E1], [KONTOR-IT-E1, NETT-SIKR-TP], [KONTOR-IT-E1, NETT-SIKR-TP, ARKIV-LAGR-MIGR]]`
— strictly monotonic growth → run N is contaminated by runs 0..N-1 → **genuine state — strictly monotonic growth → run N is contaminated by runs 0..N-1 → **genuine state
bleed (G2/B7)**. bleed (G2/B7)**.
- **Fresh instance:** `fresh_workflow()` builds a new workflow + clean thread per run → - **Fresh instance:** `fresh_workflow()` builds a new workflow + clean thread per run →
each participant sees ONLY its own project: `[[FV42-GSV-E1], [RV13-RAS-TP], each participant sees ONLY its own project: `[[KONTOR-IT-E1], [NETT-SIKR-TP],
[BRU-LAKS-REHAB]]` → **zero cross-run contamination** (B7 mitigation works). [ARKIV-LAGR-MIGR]]` → **zero cross-run contamination** (B7 mitigation works).
- **Implication:** Fase 2 fan-out must build a fresh workflow/executor instance per - **Implication:** Fase 2 fan-out must build a fresh workflow/executor instance per
project run (a factory like `fresh_workflow()`), never reuse a shared instance — a project run (a factory like `fresh_workflow()`), never reuse a shared instance — a
reused `Workflow` leaks one project's context into the next, which would corrupt reused `Workflow` leaks one project's context into the next, which would corrupt

View file

@ -28,7 +28,7 @@ validated. A valid proposal (claim well under the feasible cap) returns a
`ValidatedProposal` with the percentiles. **`self_repair` is capped** at `max_attempts` `ValidatedProposal` with the percentiles. **`self_repair` is capped** at `max_attempts`
and hard-stops (verified it calls the generator exactly N times and no more, B4). and hard-stops (verified it calls the generator exactly N times and no more, B4).
### Worked example (project FV42-GSV-E1, codes 05.2 + 03.1) ### Worked example (project KONTOR-IT-E1, codes 05.2 + 03.1)
- Affected items' total ≈ **1 482 500 NOK**; 30% feasible cap ≈ **444 750 NOK**. - Affected items' total ≈ **1 482 500 NOK**; 30% feasible cap ≈ **444 750 NOK**.
- Claim **200 000** → `ValidatedProposal` with ordered P10/P50/P90 around the cap. - Claim **200 000** → `ValidatedProposal` with ordered P10/P50/P90 around the cap.

View file

@ -379,7 +379,7 @@
et krav som uansett ikke kan valideres). To uavhengige avvisninger: ukjent kostkode, og ekte kode et krav som uansett ikke kan valideres). To uavhengige avvisninger: ukjent kostkode, og ekte kode
med `quantity`/`unit_cost` utenfor `tolerance` (default 5 %, **konfig**) relativt til BASELINE-verdien. med `quantity`/`unit_cost` utenfor `tolerance` (default 5 %, **konfig**) relativt til BASELINE-verdien.
Validering, ALDRI reparasjon — forslaget avvises, aldri stilltiende korrigert til baselinen. Validering, ALDRI reparasjon — forslaget avvises, aldri stilltiende korrigert til baselinen.
**Argumentet er VALGFRITT** (`None` = pre-S4.0-oppførsel), men begge run-stier SETTER det: road-stien **Argumentet er VALGFRITT** (`None` = pre-S4.0-oppførsel), men begge run-stier SETTER det: referanse-stien
fra `project.cost_items` (alltid — estimatet ER prosjektet), bundle-stien KUN når bundelen shipper fra `project.cost_items` (alltid — estimatet ER prosjektet), bundle-stien KUN når bundelen shipper
`cost-baseline.json` (`load_optional_cost_baseline`) — en pre-amendment-bundle er legitimt `cost-baseline.json` (`load_optional_cost_baseline`) — en pre-amendment-bundle er legitimt
uforankret, og det er dét som holder commons-goldenene byte-identiske. **Toleransen stopper ved uforankret, og det er dét som holder commons-goldenene byte-identiske. **Toleransen stopper ved
@ -389,7 +389,7 @@
`energy_efficiency`-literalen — en andre metode er nå data, ikke en redigering av validatoren. `energy_efficiency`-literalen — en andre metode er nå data, ikke en redigering av validatoren.
Format- og toleranse-semantikken er bestemt LOKALT (commons-amendmentet D-A pkt. 2 kom aldri, som i Format- og toleranse-semantikken er bestemt LOKALT (commons-amendmentet D-A pkt. 2 kom aldri, som i
S3.2); **D7-speiling ÅPEN.** Load-bearing MÅLT (`tests/test_s40_cost_baseline_loadbearing.py`), seks S3.2); **D7-speiling ÅPEN.** Load-bearing MÅLT (`tests/test_s40_cost_baseline_loadbearing.py`), seks
mutasjoner alle røde: detach avstemmings-stagen · detach magnitude-toleransen · detach road-wiringen · mutasjoner alle røde: detach avstemmings-stagen · detach magnitude-toleransen · detach referanse-wiringen ·
detach bundle-wiringen · ignorer det injiserte cap-registeret · gjør den valgfrie loaderen tolerant. detach bundle-wiringen · ignorer det injiserte cap-registeret · gjør den valgfrie loaderen tolerant.
**En UFORANKRET kjøring sier det nå — og BEGGE utsagn stammer fra kjøringens ENE oppslag, aldri **En UFORANKRET kjøring sier det nå — og BEGGE utsagn stammer fra kjøringens ENE oppslag, aldri
en andre lesing av bundelen (21.08):** `ProvenanceStamp.cost_baseline_anchored` er PÅKREVD uten en andre lesing av bundelen (21.08):** `ProvenanceStamp.cost_baseline_anchored` er PÅKREVD uten
@ -1032,8 +1032,8 @@
for HVER konfigurert base samtidig, pluss ett JSON-objekt per ufulgt kryss-lenke. Begge vokser med for HVER konfigurert base samtidig, pluss ett JSON-objekt per ufulgt kryss-lenke. Begge vokser med
korpuset, så prisen på å finne ut *hvilke baser som finnes* ble satt av hvor mye de *inneholder* — korpuset, så prisen på å finne ut *hvilke baser som finnes* ble satt av hvor mye de *inneholder* —
progressiv disclosure snudd på hodet (målbilde §2/§4). **MÅLT med `o200k_base`, instrumentet først progressiv disclosure snudd på hodet (målbilde §2/§4). **MÅLT med `o200k_base`, instrumentet først
validert mot commons' egne fasittall:** 112 116 tokens over tre flate Vegnormal-baser, og validert mot commons' egne fasittall:** 112 116 tokens over tre flate kravbaser (et kravkorpus brukt under utviklingen), og
**124 942 over de 171 grenbasene** som erstattet dem — grenformen (`vegnormal-okf` `8145c23`) **124 942 over de 171 grenbasene** som erstattet dem — grenformen (korpusbyggets `8145c23`)
lukket bundle-siden (−82…92 % på `read_bundle`) og gjorde katalogsiden VERRE, nøyaktig som det lukket bundle-siden (−82…92 % på `read_bundle`) og gjorde katalogsiden VERRE, nøyaktig som det
repoet forutså. Etter: **362** og **21 448** (per base 37 372 → 121 og 731 → 125). repoet forutså. Etter: **362** og **21 448** (per base 37 372 → 121 og 731 → 125).
**Et premiss ble felt FØR noe ble bygget på det:** «indeksbodyen forteller hva basen handler om» **Et premiss ble felt FØR noe ble bygget på det:** «indeksbodyen forteller hva basen handler om»
@ -1060,7 +1060,7 @@
en uendret CLI-flate. Ærlighets-grenser, uttalt: `navigate_bundle` kalles fortsatt per base per en uendret CLI-flate. Ærlighets-grenser, uttalt: `navigate_bundle` kalles fortsatt per base per
katalogkall (I/O og veggklokke, ikke tokens — ikke målt her); ingen LEVENDE modell har kalt det katalogkall (I/O og veggklokke, ikke tokens — ikke målt her); ingen LEVENDE modell har kalt det
nye verktøyet, så at en manager velger BEDRE med et utdrag enn med hele indeksen er ikke bevist nye verktøyet, så at en manager velger BEDRE med et utdrag enn med hele indeksen er ikke bevist
(structured-output-grensens klasse); og ordrens nevner for N100:2023 var 34 mens disken viser 40 — (structured-output-grensens klasse); og ordrens nevner for den første kravbasen var 34 mens disken viser 40 —
tallene bruker den målte nevneren. Måling: `docs/2026-08-26-katalogkostnaden.md`. tallene bruker den målte nevneren. Måling: `docs/2026-08-26-katalogkostnaden.md`.
- **Et fravær ved en signeringsport sies i ORD; i dataene sies det ved å være borte (PM-tillegg 4, - **Et fravær ved en signeringsport sies i ORD; i dataene sies det ved å være borte (PM-tillegg 4,
økt 77):** `PlanReviewRequest.current_progress` og `ParkedExploration.current_progress` ble begge økt 77):** `PlanReviewRequest.current_progress` og `ParkedExploration.current_progress` ble begge
@ -1116,12 +1116,12 @@
`function_result` — `.text` alene måler en kontekstbærende prompt til noen få tegn. `function_result` — `.text` alene måler en kontekstbærende prompt til noen få tegn.
**Formen er katalogens, ett trinn ned på stigen** (`list_bundles` = hvilke baser finnes, **Formen er katalogens, ett trinn ned på stigen** (`list_bundles` = hvilke baser finnes,
`read_bundle` = hva er i DENNE, `read_file` = hva sier dokumentet): én oppføring per konseptfil med `read_bundle` = hva er i DENNE, `read_file` = hva sier dokumentet): én oppføring per konseptfil med
`name`, `type`, `title`, `chars`. Etter: **259 tokens** på tunnelbasen, utforskningens `name`, `type`, `title`, `chars`. Etter: **259 tokens** på den største eksempelbasen, utforskningens
prompt-tokens −77 / −90 / **−91 %**. **Listen bygges av `Bundle.context_files`, ALDRI `files`** — prompt-tokens −77 / −90 / **−91 %**. **Listen bygges av `Bundle.context_files`, ALDRI `files`** —
det er dén property som dropper `type: verdict`-laget (og nestede `index.md`) på hvert nivå, og en det er dén property som dropper `type: verdict`-laget (og nestede `index.md`) på hvert nivå, og en
liste bygget av `files` ville rutet tidligere dommer foran navigatøren UTENOM den gatede liste bygget av `files` ville rutet tidligere dommer foran navigatøren UTENOM den gatede
ExpeL-folden mens hver kostnads-arm forble grønn. **Et premiss ble felt før noe ble bygget på det:** ExpeL-folden mens hver kostnads-arm forble grønn. **Et premiss ble felt før noe ble bygget på det:**
«indeksbodyen er basens egen navigasjonsprosa, så den hører hjemme her» — tunnelbasens rot-indeks er «indeksbodyen er basens egen navigasjonsprosa, så den hører hjemme her» — den største eksempelbasens rot-indeks er
**alene 4 763 tegn ≈ 1 400 tokens**, altså nesten hele taket, for et felt katalogen alt gir et **alene 4 763 tegn ≈ 1 400 tokens**, altså nesten hele taket, for et felt katalogen alt gir et
bundet utdrag av og `read_file(id, "index.md")` fortsatt gir helt. **Taket bor i TESTEN** bundet utdrag av og `read_file(id, "index.md")` fortsatt gir helt. **Taket bor i TESTEN**
(`_CEILING_CHARS = 1 500`), katalogtakets regel av katalogtakets grunn. **AVVIK fra ordren, uttalt:** (`_CEILING_CHARS = 1 500`), katalogtakets regel av katalogtakets grunn. **AVVIK fra ordren, uttalt:**
@ -1401,7 +1401,7 @@
ikke bevist (structured-output-grensens klasse). ikke bevist (structured-output-grensens klasse).
- **Den TREDJE projeksjonen inn i `CostBaseline` DERIVERES fra en tabell som alt er i basen — og - **Den TREDJE projeksjonen inn i `CostBaseline` DERIVERES fra en tabell som alt er i basen — og
den nekter heller enn å oppfinne (MAJOR-4, økt 78):** `cost-baseline.json` er håndskrevet per den nekter heller enn å oppfinne (MAJOR-4, økt 78):** `cost-baseline.json` er håndskrevet per
prosjekt og `baseline_from_project` tilhører veg-domenet, så et INGESTERT anbudskorpus kunne prosjekt og `baseline_from_project` tilhører referanse-domenet, så et INGESTERT anbudskorpus kunne
navigeres og aldri forankres. `okf.derive_cost_baseline(bundle, *, project_id)` leser navigeres og aldri forankres. `okf.derive_cost_baseline(bundle, *, project_id)` leser
prisskjemaet. **`project_id` er PÅKREVD keyword, ikke lest av basen:** `run._project_from_bundle` prisskjemaet. **`project_id` er PÅKREVD keyword, ikke lest av basen:** `run._project_from_bundle`
fail-faster alt kjøringens id mot basens `validator-input.json`, så en andre lesing her ville vært fail-faster alt kjøringens id mot basens `validator-input.json`, så en andre lesing her ville vært
@ -1477,7 +1477,7 @@
er ENESTE renderer, med TO kallsteder (returnerer `None` ved enighet — omisjon, aldri tom rad; omisjonen er selv er ENESTE renderer, med TO kallsteder (returnerer `None` ved enighet — omisjon, aldri tom rad; omisjonen er selv
gatet, M5 → 2 røde inkludert et UAVHENGIG eksisterende vitne i gatet, M5 → 2 røde inkludert et UAVHENGIG eksisterende vitne i
`test_navigation_visibility_loadbearing`). **Stempel-feltet er PÅKREVD uten default** av `test_navigation_visibility_loadbearing`). **Stempel-feltet er PÅKREVD uten default** av
`cost_baseline_anchored`s grunn, og her skjerpet: `None` er en VERDI (veg-stien har ingen base), `cost_baseline_anchored`s grunn, og her skjerpet: `None` er en VERDI (referanse-stien har ingen base),
så et utelatt felt måtte bety det samme som «ingen base» — nøyaktig stillheten kravet lukker. så et utelatt felt måtte bety det samme som «ingen base» — nøyaktig stillheten kravet lukker.
**Det som FORTSATT nekter er den ekte kollisjonen:** to KONSEPTER i ÉN base som erklærer ULIKE **Det som FORTSATT nekter er den ekte kollisjonen:** to KONSEPTER i ÉN base som erklærer ULIKE
id-er (`assert_declared_ids_agree`, kalt ved hver dør som ÅPNER en base). **Rot-`index.md` er id-er (`assert_declared_ids_agree`, kalt ved hver dør som ÅPNER en base). **Rot-`index.md` er
@ -2201,7 +2201,7 @@
frie turen er den billigste å lære det på, og på den betalte fyrer den før første modellkall ved frie turen er den billigste å lære det på, og på den betalte fyrer den før første modellkall ved
konstruksjon — armen asserterer NULL kall, aldri bare unntaket, fordi en nekt etter forbruket ser konstruksjon — armen asserterer NULL kall, aldri bare unntaket, fordi en nekt etter forbruket ser
identisk ut ved exit-koden (økt 57s regel). Tre CLI-nekter, hver med en rc-0-kontroll på en argv identisk ut ved exit-koden (økt 57s regel). Tre CLI-nekter, hver med en rc-0-kontroll på en argv
som ellers ville blitt AKSEPTERT: krever `--bundle-dir` (veg-stien er forankret ved konstruksjon, som ellers ville blitt AKSEPTERT: krever `--bundle-dir` (referanse-stien er forankret ved konstruksjon,
så der kunne flagget aldri fyre — et flagg som ikke kan fyre er en påstand flaten gjør om seg så der kunne flagget aldri fyre — et flagg som ikke kan fyre er en påstand flaten gjør om seg
selv, Fase-3-klassen), refusert i `--portfolio` VED NAVN (M9 → 1 rød med egen signatur: uten den selv, Fase-3-klassen), refusert i `--portfolio` VED NAVN (M9 → 1 rød med egen signatur: uten den
STARTER mutanten et porteføljepass og når modellen med `ChatClientException`, altså er nekten dét STARTER mutanten et porteføljepass og når modellen med `ChatClientException`, altså er nekten dét
@ -2218,7 +2218,7 @@
basen og artefaktene økt 98 etterlot. Måling: basen og artefaktene økt 98 etterlot. Måling:
`docs/2026-09-08-f3-f4-nekten-og-forankringen.md` § 2. `docs/2026-09-08-f3-f4-nekten-og-forankringen.md` § 2.
- **`PrepassExcerpt` bærer utdragets `title`/`req_number`/`sources` som navngitte felt og hver - **`PrepassExcerpt` bærer utdragets `title`/`req_number`/`sources` som navngitte felt og hver
`source_*`-lokator via `model_extra` (P3, 08.09):** en re-måling av N100/N200/N500 mot okfs `source_*`-lokator via `model_extra` (P3, 08.09):** en re-måling av de tre kravbasene (et kravkorpus brukt under utviklingen) mot okfs
oppdaterte produsent (14 medlemmer, var 9) fant at `po` droppet de fem nye feltene TO ganger — oppdaterte produsent (14 medlemmer, var 9) fant at `po` droppet de fem nye feltene TO ganger —
`PrepassExcerpt` var `extra="ignore"` ved parsing, og `_data_blocks` rendrer bare `PrepassExcerpt` var `extra="ignore"` ved parsing, og `_data_blocks` rendrer bare
`concept_id`/`adjudication`/`trust_tier`. Konsekvensen var observerbar i modellens egne ord: et `concept_id`/`adjudication`/`trust_tier`. Konsekvensen var observerbar i modellens egne ord: et
@ -2226,7 +2226,7 @@
mens kravnummeret lå urørt i payloaden. **`title`/`req_number`/`sources` er navngitte, valgfrie mens kravnummeret lå urørt i payloaden. **`title`/`req_number`/`sources` er navngitte, valgfrie
felt** (SS-8-deklarerte, entallige — hvert payload skrevet før i dag mangler dem, så et påkrevd felt** (SS-8-deklarerte, entallige — hvert payload skrevet før i dag mangler dem, så et påkrevd
felt ville refusert historikken). **`source_*`-lokatorene leses IKKE som navngitte felt**: felt ville refusert historikken). **`source_*`-lokatorene leses IKKE som navngitte felt**:
producerens egen melding kaller dem en PREFIKS-REGEL, ikke en allowlist (K2 bærer fem, N-bundlene producerens egen melding kaller dem en PREFIKS-REGEL, ikke en allowlist (K2 bærer fem, kravbasene
to), og målt er `extra="allow"` + `source_locators()`s prefikssøk mot `model_extra` det ENESTE av to), og målt er `extra="allow"` + `source_locators()`s prefikssøk mot `model_extra` det ENESTE av
de to formene en fremtidig tredje `source_*`-nøkkel når prompten gjennom UTEN en kodeendring her. de to formene en fremtidig tredje `source_*`-nøkkel når prompten gjennom UTEN en kodeendring her.
`_excerpt_header` legger feltene til DATA-avgrenserens parentes KUN når de finnes — et P1-form `_excerpt_header` legger feltene til DATA-avgrenserens parentes KUN når de finnes — et P1-form
@ -2236,9 +2236,9 @@
debatt-døra. Load-bearing MÅLT (`tests/test_prepass_excerpt_fields_loadbearing.py`, 10 armer): 8 debatt-døra. Load-bearing MÅLT (`tests/test_prepass_excerpt_fields_loadbearing.py`, 10 armer): 8
røde før fiksen, 10/10 grønne etter, og en mutasjon (revert til § "to felt"-formen) rød på nøyaktig røde før fiksen, 10/10 grønne etter, og en mutasjon (revert til § "to felt"-formen) rød på nøyaktig
de tre rendrings-armene mens parsing-armene forble grønne — beviser at de to fiksede stedene har de tre rendrings-armene mens parsing-armene forble grønne — beviser at de to fiksede stedene har
hver sin uavhengige gate. **Én betalt N100-arm med `--require-cost-baseline`: rc 1, 0 modellkall, hver sin uavhengige gate. **Én betalt kravbase-arm med `--require-cost-baseline`: rc 1, 0 modellkall,
NOK 0,00** — (b′) forblir STRUKTURELT umålbar under flagget (F4: en vegnormal bærer ingen NOK 0,00** — (b′) forblir STRUKTURELT umålbar under flagget (F4: en kravstandard bærer ingen
kostlinjer, uendret av denne fiksen). Måling: `docs/2026-09-08-n-bundlene-hypoteseform.md` § 10. kostlinjer, uendret av denne fiksen).
- **En identifikator forslaget bygger på skal finnes ORDRETT i inputen, ellers faller dommen — og - **En identifikator forslaget bygger på skal finnes ORDRETT i inputen, ellers faller dommen — og
hullet var ALDRI at fabrikasjon var ufanget (P7, 09.09):** `_reconcile_against_baseline` hullet var ALDRI at fabrikasjon var ufanget (P7, 09.09):** `_reconcile_against_baseline`
(`validator.py`) bærer allerede setningen «the cost code is absent from the baseline — a (`validator.py`) bærer allerede setningen «the cost code is absent from the baseline — a
@ -2254,7 +2254,7 @@
fullstendighets-grunn). **REGELEN HAR INTET MØNSTER, og det er en MÅLING:** sjekken er fullstendighets-grunn). **REGELEN HAR INTET MØNSTER, og det er en MÅLING:** sjekken er
`code in grounding` — eksakt delstreng — fordi de leverte korpusene bærer heterogene former `code in grounding` — eksakt delstreng — fordi de leverte korpusene bærer heterogene former
(K2: 499 `UPPER-num`-treff / 23 unike, 25 enkeltbokstav-koder à `B-20-00-00`, **0** `Krav`-numre i (K2: 499 `UPPER-num`-treff / 23 unike, 25 enkeltbokstav-koder à `B-20-00-00`, **0** `Krav`-numre i
kroppen; N-payloadene: `Krav X.Y.Z—N` i `req_number`/`title` 8/8 i hver, 71 UUID-er i N200), så et kroppen; kravbase-payloadene: `Krav X.Y.Z—N` i `req_number`/`title` 8/8 i hver, 71 UUID-er i den andre), så et
mønster valgt for å dekke dem ville vært en regel om FASONGER. **Rene tall er den ene inerte mønster valgt for å dekke dem ville vært en regel om FASONGER. **Rene tall er den ene inerte
klassen** (K2: 46 394 forekomster / 2 117 distinkte), og feilretningen er ÅPEN — regelen kan ikke klassen** (K2: 46 394 forekomster / 2 117 distinkte), og feilretningen er ÅPEN — regelen kan ikke
felle en ekte kode. **Beviset er TRE ikke-modell-forfattede kilder** (`_grounding_text`): felle en ekte kode. **Beviset er TRE ikke-modell-forfattede kilder** (`_grounding_text`):
@ -2319,7 +2319,7 @@
GJENNOM `_grounding_text` — samme funksjon gaten bruker. **Et mønster er tillatt HER og ikke i GJENNOM `_grounding_text` — samme funksjon gaten bruker. **Et mønster er tillatt HER og ikke i
gaten**, og det er skillet mellom rapport og gate: en ukjent form er et token som ikke telles, gaten**, og det er skillet mellom rapport og gate: en ukjent form er et token som ikke telles,
altså under-telling, aldri falsk avvisning. Formene er TRANSKRIBERT fra målingen (K2: 50 altså under-telling, aldri falsk avvisning. Formene er TRANSKRIBERT fra målingen (K2: 50
distinkte kodeformede, 0 kravnumre; N-korpusene: 269–981 distinkte kravnumre, ≤3 kodeformede); distinkte kodeformede, 0 kravnumre; kravkorpuset: 269–981 distinkte kravnumre, ≤3 kodeformede);
**rene tall er BEVISST utelatt med tallet** (46 394 / 2 117 i K2 — å telle dem gjør hver rapport **rene tall er BEVISST utelatt med tallet** (46 394 / 2 117 i K2 — å telle dem gjør hver rapport
positiv og målingen inert). `grounding_offer_notice` er ENESTE renderer og tier når kjøringen KAN positiv og målingen inert). `grounding_offer_notice` er ENESTE renderer og tier når kjøringen KAN
forankre — omisjon, aldri tom rad. Load-bearing MÅLT forankre — omisjon, aldri tom rad. Load-bearing MÅLT
@ -2338,8 +2338,7 @@
publiseringsbeslutning, ikke denne ordrens) — 8-/435-distinkt-tallene er MÅLT og står i publiseringsbeslutning, ikke denne ordrens) — 8-/435-distinkt-tallene er MÅLT og står i
dokumentet; portefølje-armen og den hostede flaten er BEVISST urørt; og tilbudsmålingen er gjort dokumentet; portefølje-armen og den hostede flaten er BEVISST urørt; og tilbudsmålingen er gjort
på okf sin GAMLE default-bundle (`K2-bundle-20260903`), så en re-måling på den nye (436 konsepter på okf sin GAMLE default-bundle (`K2-bundle-20260903`), så en re-måling på den nye (436 konsepter
/ 832 filer) er en senere, separat ordre. Måling: / 832 filer) er en senere, separat ordre.
`docs/2026-09-09-p8-forankringstilbudet.md`.
- **Stresstestens kontekstsett er DATA med en gate, og fasitens titler måtte leses av konseptets - **Stresstestens kontekstsett er DATA med en gate, og fasitens titler måtte leses av konseptets
EGEN erklæring (P14, 12.09):** `contexts/<prosjekt>/` bærer `mandate.json` (`Mandate` ordrett), EGEN erklæring (P14, 12.09):** `contexts/<prosjekt>/` bærer `mandate.json` (`Mandate` ordrett),
`bundle.txt` (symbolsk basenavn + erklært `bundle_id` — aldri en absolutt sti, som ville pinnet `bundle.txt` (symbolsk basenavn + erklært `bundle_id` — aldri en absolutt sti, som ville pinnet
@ -2352,20 +2351,20 @@
alt bærer det er dispatchbart uendret. **Regel U** er den målbare formen for «basen kan ikke svare»: alt bærer det er dispatchbart uendret. **Regel U** er den målbare formen for «basen kan ikke svare»:
hvert ubesvarbart spørsmål erklærer ≥1 `anchor` (små bokstaver, ≥4 tegn), og admitteres iff HVER hvert ubesvarbart spørsmål erklærer ≥1 `anchor` (små bokstaver, ≥4 tegn), og admitteres iff HVER
anchor er fraværende — case-insensitivt, som delstreng — fra HELE teksten i HVERT konsept. anchor er fraværende — case-insensitivt, som delstreng — fra HELE teksten i HVERT konsept.
**Ikke «deler ingen nøkkelord med noen tittel»:** et tunnelspørsmål deler «tunnel» med hundrevis av **Ikke «deler ingen nøkkelord med noen tittel»:** et spørsmål om kjøling deler «kjøling» med hundrevis av
titler og det beviser ingenting; det som gjør et spørsmål ubesvarbart er at basen mangler SAKEN. titler og det beviser ingenting; det som gjør et spørsmål ubesvarbart er at basen mangler SAKEN.
Fullteksten koster ingenting ekstra (målt 0,77 s for r761, den største basen), så tittel-proxyen Fullteksten koster ingenting ekstra (målt 0,77 s for den største basen i utviklingskorpuset), så tittel-proxyen
hadde ingen pris å forsvare seg med, og skanningen bærer alltid NEVNER — null konsepter er RØDT, hadde ingen pris å forsvare seg med, og skanningen bærer alltid NEVNER — null konsepter er RØDT,
aldri vakuøst fraværende (ansikt 4). **MÅLT og verdt hele ordren: 22 av 22 prøvde kostnadsord er aldri vakuøst fraværende (ansikt 4). **MÅLT og verdt hele ordren: 22 av 22 prøvde kostnadsord er
FRAVÆRENDE fra n100/n200/n500** (r761 bærer 4 av dem, fordi prosesskoden ER kontraktsspråk) — po FRAVÆRENDE fra de tre kravbasene** (den fjerde, en prosesskatalog, bærer 4 av dem, fordi den ER kontraktsspråk) — po
sitt oppdrag er å finne kostnadsbesparelser, og tre av fire baser inneholder ikke ett pengeord. sitt oppdrag er å finne kostnadsbesparelser, og tre av fire baser inneholder ikke ett pengeord.
**Basene er IKKE en repo-avhengighet:** tre armer SKIPPER med roten navngitt når **Basene er IKKE en repo-avhengighet:** tre armer SKIPPER med roten navngitt når
`PORTFOLIO_VEGNORMAL_ROOT` ikke er montert (MAJOR-3-gatens egen begrensning — en hard feil ville `PORTFOLIO_BUNDLE_ROOT` ikke er montert (MAJOR-3-gatens egen begrensning — en hard feil ville
brutt `uv run pytest` i overleveringspakka), mens (a) mandatet laster og (d) ruting-mot-egen-base brutt `uv run pytest` i overleveringspakka), mens (a) mandatet laster og (d) ruting-mot-egen-base
er UBETINGEDE og aldri kan være fraværende. **FUNN, målt og IKKE fikset (egen ordre):** er UBETINGEDE og aldri kan være fraværende. **FUNN, målt og IKKE fikset (egen ordre):**
`okf.parse_frontmatter` er linjeorientert last-write-wins, så `sources:`-blokkens innrykkede `okf.parse_frontmatter` er linjeorientert last-write-wins, så `sources:`-blokkens innrykkede
`title` overskriver konseptets egen — `directory_listing` på `krav/N500` returnerer **269 `title` overskriver konseptets egen — `directory_listing` på ett kravnivå i en av kravbasene returnerte **269
dokumenter, alle med `"title": "N500:2024"`**, og navigasjonsstigens rung 2/3 skiller dem kun med dokumenter, alle med basens eget navn som `"title"`**, og navigasjonsstigens rung 2/3 skiller dem kun med
et UUID-filnavn og et tegnantall. En fasit-assert mot den tittelen ville vært VAKUØS, så gaten et UUID-filnavn og et tegnantall. En fasit-assert mot den tittelen ville vært VAKUØS, så gaten
leser toppnivå-nøkler (`own_frontmatter`, første forekomst vinner) og bærer en TRIPWIRE som leser toppnivå-nøkler (`own_frontmatter`, første forekomst vinner) og bærer en TRIPWIRE som
asserterer at kollapsen fortsatt finnes; blir den halvdelen rød er funnet borte og armen skal asserterer at kollapsen fortsatt finnes; blir den halvdelen rød er funnet borte og armen skal
@ -2375,15 +2374,16 @@
`ea8c534773acdbe41ae68f2c55724d69aaf8be4f`): M1 fasit-sti basen ikke bærer (2) · M2 approach rutet `ea8c534773acdbe41ae68f2c55724d69aaf8be4f`): M1 fasit-sti basen ikke bærer (2) · M2 approach rutet
mot annen base (1) · M3 anchor basen FAKTISK bærer (1) · M4 duplisert approach-id (3) · M5 erklært mot annen base (1) · M3 anchor basen FAKTISK bærer (1) · M4 duplisert approach-id (3) · M5 erklært
`bundle_id` driftet (2) · M6 registrert tittel driftet (1). Gaten var RØD før settene fantes. `bundle_id` driftet (2) · M6 registrert tittel driftet (1). Gaten var RØD før settene fantes.
**Ærlighets-grenser, uttalt:** de fire prosjektene er OPPDIKTET (hvert sett sier i sitt eget **Ærlighets-grenser, uttalt:** de fire prosjektene var OPPDIKTET (hvert sett sa i sitt eget
`honesty`-felt hva jeg konstruerte — navn, lengder, ÅDT og alle tolv beløp; `affected_codes` er `honesty`-felt hva som var konstruert — navn, størrelser og alle tolv beløp; `affected_codes` var
syntetiske i tre sett og EKTE `prosessnr` i `kontrakt-sorasen-2027`, som gjør de tre andre bevisst syntetiske i tre sett og EKTE `prosessnr` i prosesskatalog-settet, som gjorde de tre andre bevisst
IKKE kjørbare via `--proposals-from-mandate`); Regel U beviser at ORDET mangler, ikke at IKKE kjørbare via `--proposals-from-mandate`); Regel U beviser at ORDET mangler, ikke at
spørsmålet er ubesvarbart; og INGEN kjøring er gjort — dette er måleoppsettet, ikke målingen. spørsmålet er ubesvarbart; og INGEN kjøring er gjort — dette er måleoppsettet, ikke målingen.
**(c), CLI-flate for `run_mandate_across_bundles`, er IKKE bygget:** motoren finnes ferdig **(c), CLI-flate for `run_mandate_across_bundles`, er IKKE bygget:** motoren finnes ferdig
(`run.py:2124`), og det som mangler er ett nytt flagg (aldri et repeterbart `--bundle-dir`), en (`run.py:2124`), og det som mangler er ett nytt flagg (aldri et repeterbart `--bundle-dir`), en
`run_id`-myntingsregel operatøren må ta (utboksen er bevisst uwiret, `run.py:2178-2180`) og tre `run_id`-myntingsregel operatøren må ta (utboksen er bevisst uwiret, `run.py:2178-2180`) og tre
partisjons-rader. Form, fire sett og måling: `docs/2026-09-12-p14-kontekstsett.md`. partisjons-rader. Settene er senere erstattet av ett fiktivt eksempelsett: `serverrom-2027`,
`driftsavtale-2027` og `drift-og-avtale-2027`.
- **Dommen over en kjoring er MASKINLEST, og ordrens egen (a)-definisjon var VAKUOS (P16 DEL A, - **Dommen over en kjoring er MASKINLEST, og ordrens egen (a)-definisjon var VAKUOS (P16 DEL A,
14.09):** okt 102s kriterium ((a) bygger pa riktig fasit-konsept ELLER nekter forankret · (b') 14.09):** okt 102s kriterium ((a) bygger pa riktig fasit-konsept ELLER nekter forankret · (b')
navngir konseptet · (c) 0 hallusinasjoner) ble dømt FOR HAND, og MALT 14.09 leste ingen kode navngir konseptet · (c) 0 hallusinasjoner) ble dømt FOR HAND, og MALT 14.09 leste ingen kode
@ -2391,12 +2391,12 @@
alt finnes (`{run_id}[-{approach}]-proposal.json` · `-outcome.json` · `{run_id}-debate.json`) — alt finnes (`{run_id}[-{approach}]-proposal.json` · `-outcome.json` · `{run_id}-debate.json`) —
ingen kjoring far et nytt felt. **ORDRENS (a) VAR EN GATE SOM BARE KUNNE BLI GRONN:** pa ingen kjoring far et nytt felt. **ORDRENS (a) VAR EN GATE SOM BARE KUNNE BLI GRONN:** pa
S2c-stien stempler `run_project` `citations = bundle_citations(bundle)`, altsa EN sitering per S2c-stien stempler `run_project` `citations = bundle_citations(bundle)`, altsa EN sitering per
konseptfil — MALT pa n100-2023: 446 kontekstfiler, 446 siteringer, **6 av 6 fasit-stier «sitert» konseptfil — MALT pa en kravbase i utviklingskorpuset: 446 kontekstfiler, 446 siteringer, **6 av 6 fasit-stier «sitert»
for ett eneste modellkall**. En sitering grunner derfor KUN under en smalere liste (et erklaert for ett eneste modellkall**. En sitering grunner derfor KUN under en smalere liste (et erklaert
pre-pass-kutt); ellers ma grunningen komme av et dokument kjoringen faktisk APNET. Begge pre-pass-kutt); ellers ma grunningen komme av et dokument kjoringen faktisk APNET. Begge
halvdeler rapporteres uansett (`opened`/`cited`/`citation_scope`), sa avviket er uttalt, aldri halvdeler rapporteres uansett (`opened`/`cited`/`citation_scope`), sa avviket er uttalt, aldri
stille. **(b') ble sjekket for SAMME vakuitet og er REN:** snippetene er konsept-BODYER mens stille. **(b') ble sjekket for SAMME vakuitet og er REN:** snippetene er konsept-BODYER mens
`ref`/`title` bor i FRONTMATTER (MALT: 0 av 446 n100-bodyer inneholder `Krav 4.1.2—1`), sa `ref`/`title` bor i FRONTMATTER (MALT: 0 av 446 kravbase-bodyer inneholder `Krav 4.1.2—1`), sa
ordrens definisjon star. **NEVNER ALLTID** (ansikt 4): tool_calls, siteringer, approach-rader og ordrens definisjon star. **NEVNER ALLTID** (ansikt 4): tool_calls, siteringer, approach-rader og
konsepter i basen; en utboks uten proposal-artefakt — eller en base som skannes til null konsepter i basen; en utboks uten proposal-artefakt — eller en base som skannes til null
konsepter — REISER `EmptyMeasurement` i stedet for a rapportere «0 hallusinasjoner». konsepter — REISER `EmptyMeasurement` i stedet for a rapportere «0 hallusinasjoner».
@ -2426,7 +2426,7 @@
helbase-snippet som baerer en `ref` er svakere bevis enn modellens eget `measure` — derfor er de helbase-snippet som baerer en `ref` er svakere bevis enn modellens eget `measure` — derfor er de
to halvdelene skilt i utfallet. to halvdelene skilt i utfallet.
- **Taket pa en BETALT kjoring hadde ingen operatorflate, og tre flater beskrev en dor som ikke - **Taket pa en BETALT kjoring hadde ingen operatorflate, og tre flater beskrev en dor som ikke
fantes (P16 B2, 14.09):** `STATE.md`, `docs/2026-09-12-p14-kontekstsett.md § 4.1` og ordre fantes (P16 B2, 14.09):** `STATE.md`, P14-rapportens § 4.1 og ordre
`20260914T091846Z` publiserer alle den samme stresskommandoen, som ender `--max-rounds 8 `20260914T091846Z` publiserer alle den samme stresskommandoen, som ender `--max-rounds 8
--max-tokens 120000`. MALT: `run.py` tok ingen av dem, alle fire gratis `--live-dry-run` nektet --max-tokens 120000`. MALT: `run.py` tok ingen av dem, alle fire gratis `--live-dry-run` nektet
med `unrecognized arguments`, og `main()` sendte **aldri** `max_rounds`/`max_tokens` videre — sa med `unrecognized arguments`, og `main()` sendte **aldri** `max_rounds`/`max_tokens` videre — sa
@ -2455,8 +2455,9 @@
som annonseres. Rettet til argv; M16 (les konstantene igjen) → 1 rod, den armen ALENE. som annonseres. Rettet til argv; M16 (les konstantene igjen) → 1 rod, den armen ALENE.
- **En listing er et VINDU, og en sti kalleren fant på er en NEKT (P18/A, 14.09):** S7a-3 bandt - **En listing er et VINDU, og en sti kalleren fant på er en NEKT (P18/A, 14.09):** S7a-3 bandt
kostnaden til oppføringer på ETT nivå i stedet for dokumenter i basen, og på fixturbasene holdt kostnaden til oppføringer på ETT nivå i stedet for dokumenter i basen, og på fixturbasene holdt
det. P16 målte hva ett nivå koster på et LEVERT korpus: `krav/N200` **169 974 tegn** over 1 132 det. P16 målte hva ett nivå koster på et LEVERT korpus (et kravkorpus brukt under utviklingen): det
dokumenter, `krav/N100` 69 250 over 445, og R761s egen rot **110 912 over 2 728 UNDERKATALOGER** — største kravnivået **169 974 tegn** over 1 132 dokumenter, et annet 69 250 over 445, og
prosesskatalogens egen rot **110 912 over 2 728 UNDERKATALOGER** —
27–113× taket, ridende i hver senere prompt; 3 av 7 betalte kjøringer døde på tokentaket. 27–113× taket, ridende i hver senere prompt; 3 av 7 betalte kjøringer døde på tokentaket.
**Vinduet dekker BEGGE slag, og det er en måling, ikke symmetri:** en paginering bare over **Vinduet dekker BEGGE slag, og det er en måling, ikke symmetri:** en paginering bare over
dokumenter ville latt det STØRSTE målte nivået stå upaginert. `offset`/`limit` over kataloger dokumenter ville latt det STØRSTE målte nivået stå upaginert. `offset`/`limit` over kataloger
@ -2464,8 +2465,8 @@
med to svar. `limit` KLEMMES (`_DIRECTORY_PAGE_MAX = 50`), aldri nektes: kalleren ba om en med to svar. `limit` KLEMMES (`_DIRECTORY_PAGE_MAX = 50`), aldri nektes: kalleren ba om en
listing, og å nekte den sender en modell som ba om for mye bort med ingenting. Default 10 er valgt listing, og å nekte den sender en modell som ba om for mye bort med ingenting. Default 10 er valgt
MOT taket: én oppføring er 121–209 tegn (median 145) over de fire leverte basene, så ti av de MOT taket: én oppføring er 121–209 tegn (median 145) over de fire leverte basene, så ti av de
STØRSTE er 2 090 tegn med oppføringer — n100 lander på 1 493 (ordrens bundne krav), n500 1 453, STØRSTE er 2 090 tegn med oppføringer — kravbase A lander på 1 493 (ordrens bundne krav), kravbase C 1 453,
R761 479, og n200 på **1 537**, 2,5 % over, fordi taket er et TEGN-budsjett og vinduet er et prosesskatalogen 479, og kravbase B på **1 537**, 2,5 % over, fordi taket er et TEGN-budsjett og vinduet er et
ANTALL; uttalt, ikke justert bort. **`total` er NEVNEREN og bæres alltid** (ansikt 4: et vindu ANTALL; uttalt, ikke justert bort. **`total` er NEVNEREN og bæres alltid** (ansikt 4: et vindu
uten nevner er en måling uten nevner), mens `total_matches` er et ANNET faktum, ikke en andre kopi uten nevner er en måling uten nevner), mens `total_matches` er et ANNET faktum, ikke en andre kopi
— «hva er her» og «hvor mye av det slapp filteret gjennom» er to spørsmål, og en navigatør som — «hva er her» og «hvor mye av det slapp filteret gjennom» er to spørsmål, og en navigatør som
@ -2516,7 +2517,7 @@
ikke som én blob (P18/B1, 14.09):** P7 gjorde stadium 0b til `item.code in grounding`, ren ikke som én blob (P18/B1, 14.09):** P7 gjorde stadium 0b til `item.code in grounding`, ren
delstreng over én sammenslått streng. P16 kjørte den mot et LEVERT korpus og målte hva delstreng over én sammenslått streng. P16 kjørte den mot et LEVERT korpus og målte hva
containment ikke kan skille: falsifiseringsarmen `a4-indeksregulering` la 250 000 NOK på ÉN containment ikke kan skille: falsifiseringsarmen `a4-indeksregulering` la 250 000 NOK på ÉN
kostlinje kodet **`R761`** — basens EGET NAVN, som alle 2 756 konseptdokumenter bærer — og hele kostlinje kodet med **basens EGET NAVN** (fire tegn), som alle 2 756 konseptdokumenter bærer — og hele
gaten sa `validated` (stage 0 hoppet over, uforankret kjøring; checker `approve`). gaten sa `validated` (stage 0 hoppet over, uforankret kjøring; checker `approve`).
**`Grounding` er en STRUKTUR, ikke et andre argument ved siden av teksten:** grensene og teksten **`Grounding` er en STRUKTUR, ikke et andre argument ved siden av teksten:** grensene og teksten
er ÉTT faktum, og to bærere for ett faktum står fritt til å være uenige (kø-(p)); `.text` utledes, er ÉTT faktum, og to bærere for ett faktum står fritt til å være uenige (kø-(p)); `.text` utledes,
@ -2526,9 +2527,9 @@
under målingen og kan ikke nekte noe som er målt; og over dokumentfrekvensen til hvert under målingen og kan ikke nekte noe som er målt; og over dokumentfrekvensen til hvert
kodeformet token i hver base (`_IDENTIFIER_FORMS`) når **ingen av 1 692 distinkte** 5 % — kodeformet token i hver base (`_IDENTIFIER_FORMS`) når **ingen av 1 692 distinkte** 5 % —
høyeste noe sted er 6 av 446 (**1,35 %**), høyeste en fasit faktisk navngir 3 av 446 (**0,67 %**), høyeste noe sted er 6 av 446 (**1,35 %**), høyeste en fasit faktisk navngir 3 av 446 (**0,67 %**),
mens `R761` er **2 756 av 2 756 (100 %)**. `_GROUNDING_MAX_DOCUMENT_SHARE = 0.05` ligger altså mens basenavnet er **2 756 av 2 756 (100 %)**. `_GROUNDING_MAX_DOCUMENT_SHARE = 0.05` ligger altså
3,7× over det høyeste ekte tokenet og 20× under defekten. **Lengden er IKKE dét som gjør defekten 3,7× over det høyeste ekte tokenet og 20× under defekten. **Lengden er IKKE dét som gjør defekten
inert** (`R761` er fire tegn) — andelen er; N dekker sammentreffs-klassen målingen ikke tilfeldigvis inert** (basenavnet er fire tegn) — andelen er; N dekker sammentreffs-klassen målingen ikke tilfeldigvis
inneholdt. **Og en andel er ingen måling uten en nevner stor nok til å ta den** (ansikt 4): ett av inneholdt. **Og en andel er ingen måling uten en nevner stor nok til å ta den** (ansikt 4): ett av
tre dokumenter er 33 % og sier ingenting, så `_GROUNDING_MIN_INERT_DOCUMENTS = 10` er et ABSOLUTT tre dokumenter er 33 % og sier ingenting, så `_GROUNDING_MIN_INERT_DOCUMENTS = 10` er et ABSOLUTT
gulv — høyeste absolutte dokumenttall noen ekte identifikator når i de fire korpusene er 6, og gulv — høyeste absolutte dokumenttall noen ekte identifikator når i de fire korpusene er 6, og
@ -2538,9 +2539,10 @@
**Nevneren NAVNGIS i nekten** («appears in 2756 of the 2756 documents this run was given»), fordi **Nevneren NAVNGIS i nekten** («appears in 2756 of the 2756 documents this run was given»), fordi
Steg 5 mater den grunnen ORDRETT inn i neste forsøks prompt: en proposer som bare får «ungrounded» Steg 5 mater den grunnen ORDRETT inn i neste forsøks prompt: en proposer som bare får «ungrounded»
svarer med et nytt token av samme slag. **B2-SPIKEN FELTE ORDRENS EGEN ALTERNATIV-HYPOTESE:** (b) svarer med et nytt token av samme slag. **B2-SPIKEN FELTE ORDRENS EGEN ALTERNATIV-HYPOTESE:** (b)
«grunnlag = det kjøringen ÅPNET» ble MÅLT over P16s 16 kode-rader — `R761` står i hvert ÅPNET «grunnlag = det kjøringen ÅPNET» ble MÅLT over P16s 16 kode-rader — basenavnet står i hvert ÅPNET
dokument også, så (b) ville **ikke** fanget defekten (grunner fortsatt), mens B1 gjør den inert og dokument også, så (b) ville **ikke** fanget defekten (grunner fortsatt), mens B1 gjør den inert og
lar den ekte prosesslinja `65 ASFALTDEKKER` (29/2756 = 1,05 %) grunne. (b) er altså ikke et lar en ekte seksjonslinje (nummer + versaltittel, formen `65 LAGRINGSSYSTEMER`; 29/2756 = 1,05 %)
grunne. (b) er altså ikke et
substitutt for B1; målt, ikke bygget. Load-bearing MÅLT substitutt for B1; målt, ikke bygget. Load-bearing MÅLT
(`tests/test_inert_identifier_loadbearing.py`, **8 armer**), **sju mutasjoner mot HELE suiten** + (`tests/test_inert_identifier_loadbearing.py`, **8 armer**), **sju mutasjoner mot HELE suiten** +
grønn kontroll 1698/5: detach regelen (3 røde) · flagg alt som er til stede (**77**) · dropp grønn kontroll 1698/5: detach regelen (3 røde) · flagg alt som er til stede (**77**) · dropp
@ -2560,24 +2562,24 @@
bekrefter at den endrer utfallet levende før DEL D. bekrefter at den endrer utfallet levende før DEL D.
- **`--docs-dir` er VALGFRI når `--bundle-dir` er gitt, og det er ikke omveien (P18/C1):** på - **`--docs-dir` er VALGFRI når `--bundle-dir` er gitt, og det er ikke omveien (P18/C1):** på
bundle-stien LESES `docs_dir` aldri — retrieval, chunk-verktøyet og «no citable content»-sjekken bundle-stien LESES `docs_dir` aldri — retrieval, chunk-verktøyet og «no citable content»-sjekken
bor alle i VEG-grenen — likevel KREVDE gaten den, så den publiserte kommandoen måtte navngi samme bor alle i referanse-grenen — likevel KREVDE gaten den, så den publiserte kommandoen måtte navngi samme
katalog to ganger (P16 FUNN 2). Verdien bindes nå ÉN gang fra `--bundle-dir` når `--docs-dir` katalog to ganger (P16 FUNN 2). Verdien bindes nå ÉN gang fra `--bundle-dir` når `--docs-dir`
mangler, hvilket er byte-identisk med dét READMEen alt ber operatøren skrive for hånd; hver mangler, hvilket er byte-identisk med dét READMEen alt ber operatøren skrive for hånd; hver
eksisterende invokasjon, to-flagg-formen inkludert, er uendret. **Dette er IKKE eksisterende invokasjon, to-flagg-formen inkludert, er uendret. **Dette er IKKE
«`--docs-dir`-omveien»** (å mate prosjektdokumenter gjennom retrieval I STEDET FOR å ingeste dem «`--docs-dir`-omveien»** (å mate prosjektdokumenter gjennom retrieval I STEDET FOR å ingeste dem
inn i en kunnskapsbase): ingen slik sti åpnes, og veg-grenen nekter fortsatt uten en EKTE inn i en kunnskapsbase): ingen slik sti åpnes, og referanse-grenen nekter fortsatt uten en EKTE
`--docs-dir` (egen arm). Nekten navngir nå BEGGE dører, ikke bare den ene den pleide å navngi. `--docs-dir` (egen arm). Nekten navngir nå BEGGE dører, ikke bare den ene den pleide å navngi.
To mutasjoner, begge røde på de SAMME to armene (`tests/test_docs_dir_optional_loadbearing.py`, To mutasjoner, begge røde på de SAMME to armene (`tests/test_docs_dir_optional_loadbearing.py`,
5 armer): krev `--docs-dir` igjen (2) · la CLI-en aldri videresende basen (2). 5 armer): krev `--docs-dir` igjen (2) · la CLI-en aldri videresende basen (2).
- **Dommerens snippet-arm teller KUN under `citation_scope == "narrowed"` (P18/C2, PM-valg P16 - **Dommerens snippet-arm teller KUN under `citation_scope == "narrowed"` (P18/C2, PM-valg P16
§ 6.2):** (a) hadde alt den korreksjonen; (b′) hadde den ikke. P16s egen begrunnelse for at (b′) § 6.2):** (a) hadde alt den korreksjonen; (b′) hadde den ikke. P16s egen begrunnelse for at (b′)
var ren — «snippetene er konsept-BODYER mens `ref`/`title` bor i FRONTMATTER, målt 0 av 446 var ren — «snippetene er konsept-BODYER mens `ref`/`title` bor i FRONTMATTER, målt 0 av 446
n100-bodyer» — holder for `Krav 4.1.2—1`, men **IKKE for R761**, der et prosessnummer som `12.1` kravbase-bodyer» — holder for `Krav 4.1.2—1`, men **IKKE for prosesskatalogen**, der et prosessnummer som `12.1`
står i bodyene selv. Under en helbase-siteringsliste var det merket «sitert» før noe modellkall, står i bodyene selv. Under en helbase-siteringsliste var det merket «sitert» før noe modellkall,
så raden kom tilbake `named` for en kjøring der modellen ikke hadde sagt noe slikt. Diskriminatoren så raden kom tilbake `named` for en kjøring der modellen ikke hadde sagt noe slikt. Diskriminatoren
er PARET av to armer med SAMME snippet og SAMME merke, der eneste forskjell er scopet. Mutasjon er PARET av to armer med SAMME snippet og SAMME merke, der eneste forskjell er scopet. Mutasjon
(ignorer scopet) → 1 rød, den nye armen ALENE. **Isolert målt på ekte data:** re-dømming av runde 1 (ignorer scopet) → 1 rød, den nye armen ALENE. **Isolert målt på ekte data:** re-dømming av runde 1
med den nye dommeren tar kontrakt-sorasen fra `named=3` til `named=1`, og den ene som står igjen med den nye dommeren tar prosesskatalog-settet fra `named=3` til `named=1`, og den ene som står igjen
er `named_in_measure` — modellens egne ord. er `named_in_measure` — modellens egne ord.
- **En retning må NAVNGI kravet som binder den, og ha LEST det — og ordrens egen plassering var - **En retning må NAVNGI kravet som binder den, og ha LEST det — og ordrens egen plassering var
strukturelt inert (P19 DEL A, 15.09):** to betalte runder på rad ga **0 av 26** fasit-konsepter strukturelt inert (P19 DEL A, 15.09):** to betalte runder på rad ga **0 av 26** fasit-konsepter
@ -2624,8 +2626,8 @@
ikke `ProvenanceStamp` (stempelet beskriver gaten som dømte ÉN kandidat). ikke `ProvenanceStamp` (stempelet beskriver gaten som dømte ÉN kandidat).
- **En «kostkode» må ha en FORM når inputen tilbyr former — og formene bor ÉTT sted, i validatoren - **En «kostkode» må ha en FORM når inputen tilbyr former — og formene bor ÉTT sted, i validatoren
(P19 DEL B, 15.09):** P18s runde 2 endte med TO `validated` forslag hvis `affected_item`-koder var (P19 DEL B, 15.09):** P18s runde 2 endte med TO `validated` forslag hvis `affected_item`-koder var
vanlige ord fra en vegnormals prosa: `impulsventilator` (4 av 270 N500-dokumenter) og vanlige ord fra en kravstandards prosa (her gjengitt som `nødstrømsaggregat`, 4 av 270 dokumenter
`bituminøst bærelag` (4 av 1 133 N200). Begge er GRUNNET i P7s forstand (de står ordrett i i én kravbase, og `redundant kjøling`, 4 av 1 133 i en annen). Begge er GRUNNET i P7s forstand (de står ordrett i
inputen) og ingen av dem er INERT i P18/B1s forstand (langt under 5 %-andelen) — de er bare ikke inputen) og ingen av dem er INERT i P18/B1s forstand (langt under 5 %-andelen) — de er bare ikke
identifikatorer for en kostlinje, og gaten hadde ikke noe stadium som kunne si det. **KJENT-POSITIV identifikatorer for en kostlinje, og gaten hadde ikke noe stadium som kunne si det. **KJENT-POSITIV
MÅLT, ikke påstått:** begge spilt av offline mot basene kjøringene faktisk fikk gir `Rejection` MÅLT, ikke påstått:** begge spilt av offline mot basene kjøringene faktisk fikk gir `Rejection`
@ -2634,11 +2636,11 @@
rapport og P19/B3s gate, og to kopier av «hvordan en identifikator ser ut» ville latt rapporten og rapport og P19/B3s gate, og to kopier av «hvordan en identifikator ser ut» ville latt rapporten og
gaten være uenige om ÉN kjørings egen input (kø-(p)); importretningen tvang det uansett gaten være uenige om ÉN kjørings egen input (kø-(p)); importretningen tvang det uansett
(`generate` importerer `validator`, aldri omvendt). (`generate` importerer `validator`, aldri omvendt).
**B1 — to nye former, transkribert fra måling:** R761s kravnumre er bare punktum-tall (`12.1`, **B1 — to nye former, transkribert fra måling:** prosesskatalogens prosessnumre er bare
`52.11`), ALLE SEKS `ref`-verdier i `contexts/kontrakt-sorasen-2027/fasit.json` er av den formen, punktum-tall (`12.1`, `52.11`), ALLE SEKS `ref`-verdier i prosesskatalog-settets `fasit.json` er
og INGEN av de to pre-P19-formene matchet én av dem — r761s hele tilbud var **3 identifikatorer av den formen, og INGEN av de to pre-P19-formene matchet én av dem — prosesskatalogens hele
over 6,5 MB**. Målt etter: **2 332** (n100 435→578, n200 982→1 512, n500 272→391). Den fjerde tilbud var **3 identifikatorer over 6,5 MB**. Målt etter: **2 332** (kravbase A 435→578,
formen er `65 ASFALTDEKKER`. **Den FØRSTE formen ble UTVIDET i samme slengen, og dét er en B 982→1 512, C 272→391). Den fjerde formen er seksjonslinja (`65 LAGRINGSSYSTEMER`). **Den FØRSTE formen ble UTVIDET i samme slengen, og dét er en
måling:** B2 gjorde de samme formene til `prose`/`identifier`-avgjørelsen, og repoets EGEN måling:** B2 gjorde de samme formene til `prose`/`identifier`-avgjørelsen, og repoets EGEN
`ENERGI-TOTAL-EL` matchet ingen av dem (form 1 krevde siffer etter separatoren) — klassifisereren `ENERGI-TOTAL-EL` matchet ingen av dem (form 1 krevde siffer etter separatoren) — klassifisereren
kalte altså en ekte kostkode prosa, og den nye gaten nektet den. En gate får bare ta feil i kalte altså en ekte kostkode prosa, og den nye gaten nektet den. En gate får bare ta feil i
@ -2649,9 +2651,9 @@
i dem (M B-i → 16 røde, spredt over syv eldre testfiler); (2) **baseline-unntaket** — en kode i dem (M B-i → 16 røde, spredt over syv eldre testfiler); (2) **baseline-unntaket** — en kode
`CostBaseline` bærer nektes ALDRI her, fordi stadium 0 alt har dømt den en ekte linje av dette `CostBaseline` bærer nektes ALDRI her, fordi stadium 0 alt har dømt den en ekte linje av dette
prosjektet og det svakere stadiet skal ikke overprøve det sterkere (samme setning `_grounding_text` prosjektet og det svakere stadiet skal ikke overprøve det sterkere (samme setning `_grounding_text`
bærer om sin tredje kilde); (3) **`has_identifier_form` FULL-matcher** — `impulsventilator 12.1` bærer om sin tredje kilde); (3) **`has_identifier_form` FULL-matcher** — `nødstrømsaggregat 12.1`
gjør ikke ordet til en kostkode, og en delstrengregel ville latt enhver prosa-kode smugle med seg gjør ikke ordet til en kostkode, og en delstrengregel ville latt enhver prosa-kode smugle med seg
en. **ÆRLIGHETS-GRENSE, MÅLT OG UTTALT SOM EGEN ARM:** et desimaltall og et R761-prosessnummer er en. **ÆRLIGHETS-GRENSE, MÅLT OG UTTALT SOM EGEN ARM:** et desimaltall og et prosessnummer er
TYPOGRAFISK IDENTISKE (`42.5` vs `12.1`) og ingen regel skiller dem, så formen teller begge — TYPOGRAFISK IDENTISKE (`42.5` vs `12.1`) og ingen regel skiller dem, så formen teller begge —
P8s eksisterende «bare tall telles ikke»-arm er derfor SNEVRET til bare HELTALL (K2s målte klasse, P8s eksisterende «bare tall telles ikke»-arm er derfor SNEVRET til bare HELTALL (K2s målte klasse,
46 394 forekomster) og desimal-tvetydigheten står i en egen, navngitt arm i stedet for i en 46 394 forekomster) og desimal-tvetydigheten står i en egen, navngitt arm i stedet for i en
@ -2752,25 +2754,26 @@
sekvensiell). sekvensiell).
- **Et KRAVNUMMER er ikke en PRIS, og basens EGEN nummer-ordliste er dét som sier det — ordrens - **Et KRAVNUMMER er ikke en PRIS, og basens EGEN nummer-ordliste er dét som sier det — ordrens
regel ble FELT av målingen før noe ble bygget på den (P20 DEL B, 15.09):** tre stressrunder og regel ble FELT av målingen før noe ble bygget på den (P20 DEL B, 15.09):** tre stressrunder og
ett multi-base-pass bar `validated` forslag hvis kostkode var et kapittelnummer i en vegnormal. ett multi-base-pass bar `validated` forslag hvis kostkode var et kapittelnummer i en kravstandard.
To overlever i utboksene og er kjent-positivene: **`10.4`** (tunnel-hauglia runde 3, n500) og To overlever i utboksene og er kjent-positivene: **`10.4`** (et kravbase-sett, runde 3, kravbase C) og
**`1.10.4`** (lindaas P17b, r761). Begge er GRUNNET i P7s forstand og ingen er INERT i P18/B1s **`1.10.4`** (multi-base-passet P17b, prosesskatalogen). Begge er GRUNNET i P7s forstand og ingen er INERT i P18/B1s
(`10.4` i 12 av 274 dokumenter, `1.10.4` i **1 av 2 756**); stadium 0 kjørte aldri, fordi ingen (`10.4` i 12 av 274 dokumenter, `1.10.4` i **1 av 2 756**); stadium 0 kjørte aldri, fordi ingen
vegnormal-base bærer en kostbaseline. **Ordrens B1 sier: form 2/3 OG «står som kravbase bærer en kostbaseline. **Ordrens B1 sier: form 2/3 OG «står som
`req_number`/`prosessnr` i toppnivå-frontmatter» → nekt.** MÅLT 15.09: n500 erklærer `req_number`/`prosessnr` i toppnivå-frontmatter» → nekt.** MÅLT 15.09: kravbase C erklærer
`seksjon: 10.4.1`…`10.4.4` og `req_number: Krav 10.4.3—2`, men **aldri den bare `10.4`** — den er `seksjon: 10.4.1`…`10.4.4` og `req_number: Krav 10.4.3—2`, men **aldri den bare `10.4`** — den er
et seksjons-PREFIKS; og r761 erklærer 2 727 `prosessnr` + 2 753 `seksjon`, hvorav **ingen** er et seksjons-PREFIKS; og prosesskatalogen erklærer 2 727 `prosessnr` + 2 753 `seksjon`, hvorav **ingen**
`1.10.4`, som står ÉN gang, som prosa: «iht. vegnormal N200 Vegbygging kap. 1.10.4». **Den er `1.10.4`, som står ÉN gang, som prosa: en henvisning til kapittel 1.10.4 i en annen
kravstandard. **Den
ordrede regelen fyrer altså på INGEN av sine egne kjent-positive.** KOMPLEMENTET fyrer på BEGGE, ordrede regelen fyrer altså på INGEN av sine egne kjent-positive.** KOMPLEMENTET fyrer på BEGGE,
og lukker et hull `_ground_against_input`s egen docstring alt innrømmer skriftlig («it fails OPEN og lukker et hull `_ground_against_input`s egen docstring alt innrømmer skriftlig («it fails OPEN
… on a coincidental match»): for ÉN form — et klausulnummer — gir basen oss ordlista som skiller … on a coincidental match»): for ÉN form — et klausulnummer — gir basen oss ordlista som skiller
en ekte referanse fra et sammentreff. Regelen er derfor: **UFORANKRET + kravformet kode + basen en ekte referanse fra et sammentreff. Regelen er derfor: **UFORANKRET + kravformet kode + basen
erklærer en ordliste + koden er IKKE i den → nekt, med nevner.** **Komplementet er også dét som erklærer en ordliste + koden er IKKE i den → nekt, med nevner.** **Komplementet er også dét som
SPARER det ene kontekstsettet bygget på ekte prosesskoder:** alle fem kodene i SPARER det ene kontekstsettet bygget på ekte prosessnumre:** alle fem kodene i
`contexts/kontrakt-sorasen-2027` (`12.1`, `12.12`, `22.1`, `52.11`, `51.1`) ER erklærte prosesskatalog-settet (i dag `contexts/driftsavtale-2027`; `12.1`, `12.12`, `22.1`, `52.11`,
`prosessnr` og passerer — under den ordrede regelen ville hver av dem blitt nektet på en `51.1`) ER erklærte `prosessnr` og passerer — under den ordrede regelen ville hver av dem blitt
uforankret r761-kjøring, og settets positive armer blitt umålbare; dét er R761-risikoen ordren nektet på en uforankret prosesskatalog-kjøring, og settets positive armer blitt umålbare; dét er
selv navngir, ankommet gjennom døra den ble pekt bort fra. **MÅLT over ALLE 24 koder i runde 3 + prosesskatalog-risikoen ordren selv navngir, ankommet gjennom døra den ble pekt bort fra. **MÅLT over ALLE 24 koder i runde 3 +
P17b (10 kjøringer):** nøyaktig to er kravformede, de er de to kjent-positive, og offline replay P17b (10 kjøringer):** nøyaktig to er kravformede, de er de to kjent-positive, og offline replay
flipper nøyaktig de to (`validated → rejected`) mens 22 står uendret. **Generalitetsvernet er flipper nøyaktig de to (`validated → rejected`) mens 22 står uendret. **Generalitetsvernet er
`_form_refusal`s mønster:** en input som erklærer INGEN referansenumre kan ikke besvares i en `_form_refusal`s mønster:** en input som erklærer INGEN referansenumre kan ikke besvares i en
@ -2816,7 +2819,7 @@
mandat, så `criteria` er tom uansett og bare rendererens egen arm faller. mandat, så `criteria` er tom uansett og bare rendererens egen arm faller.
- **En parse-feil brenner ikke lenger rundeboka i stillhet, og annonseringen navngir hva den - **En parse-feil brenner ikke lenger rundeboka i stillhet, og annonseringen navngir hva den
handler om (P20 DEL C, 15.09):** `_fetch_parsed` prøvde på nytt med den BYTE-IDENTISKE prompten. handler om (P20 DEL C, 15.09):** `_fetch_parsed` prøvde på nytt med den BYTE-IDENTISKE prompten.
MÅLT (P19 F4): `kontrakt-sorasen-04` etterlot `{run_id}-parse-failures.json` med **elleve** rader, MÅLT (P19 F4): en betalt kjøring på prosesskatalog-settet etterlot `{run_id}-parse-failures.json` med **elleve** rader,
hver av dem samme feil (`claimed_saving_nok` ≤ 0) — elleve av kjøringens tolv runder, brukt på å hver av dem samme feil (`claimed_saving_nok` ≤ 0) — elleve av kjøringens tolv runder, brukt på å
spørre om igjen uten å si hva som var galt. Steg 5s `prior_rejection` bærer VALIDATOR-avvisninger, spørre om igjen uten å si hva som var galt. Steg 5s `prior_rejection` bærer VALIDATOR-avvisninger,
og et svar som aldri parset når aldri en validator, så ingen eksisterende blokk kunne bære det. og et svar som aldri parset når aldri en validator, så ingen eksisterende blokk kunne bære det.
@ -2832,11 +2835,11 @@
- **PRISEN HØRER TIL PROSJEKTET, ikke til kunnskapsbasen — og ordrens egen B2-regel ble FELT av - **PRISEN HØRER TIL PROSJEKTET, ikke til kunnskapsbasen — og ordrens egen B2-regel ble FELT av
måling (P21 DEL A+B, 15.09):** fire betalte runder (P16/P18/P19/P17b/P20) kjørte **UFORANKRET, måling (P21 DEL A+B, 15.09):** fire betalte runder (P16/P18/P19/P17b/P20) kjørte **UFORANKRET,
alle sammen**, fordi den ene fil-lasteren leser `cost-baseline.json` ut av BUNDLE-katalogen og alle sammen**, fordi den ene fil-lasteren leser `cost-baseline.json` ut av BUNDLE-katalogen og
ingen vegnormal bærer et prisskjema: N100/N200/N500/R761 er KUNNSKAP, og kunnskap bærer krav, ingen kravbase bærer et prisskjema: de fire basene er KUNNSKAP, og kunnskap bærer krav,
aldri beløp. Validatorens **stadium 0** — det ENE stadiet som skiller en oppdiktet kostlinje fra aldri beløp. Validatorens **stadium 0** — det ENE stadiet som skiller en oppdiktet kostlinje fra
en linje dette prosjektet faktisk kjøper — ble derfor hoppet over i hver eneste av dem, og en linje dette prosjektet faktisk kjøper — ble derfor hoppet over i hver eneste av dem, og
`validated` kunne ikke bety det det sier: P20 G1/G2 målte EKTE R761-prosessnumre (`12.11` ×3 på `validated` kunne ikke bety det det sier: P20 G1/G2 målte EKTE prosessnumre (`12.11` ×3 på
sorasen, `1.1.1` på lindaas) som validerte med beløp ingen hadde noe sted. prosesskatalog-settet, `1.1.1` på multi-base-settet) som validerte med beløp ingen hadde noe sted.
`--cost-baseline FILE` er PM-beslutning **(e)**, valgt over tre alternativer P20 skrev ned: `--cost-baseline FILE` er PM-beslutning **(e)**, valgt over tre alternativer P20 skrev ned:
(a) nekt enhver kravformet kode uforankret ville gjort det ENE realistiske kontekstsettet (a) nekt enhver kravformet kode uforankret ville gjort det ENE realistiske kontekstsettet
umålbart, (b) `--require-cost-baseline` som default ville etterlatt ingen stresstest, og umålbart, (b) `--require-cost-baseline` som default ville etterlatt ingen stresstest, og
@ -2872,9 +2875,9 @@
label/description, som er nøklet på samme kode (kø-(p)). **ORDRENS ARM (h) BLE FELT AV MÅLING label/description, som er nøklet på samme kode (kø-(p)). **ORDRENS ARM (h) BLE FELT AV MÅLING
FØR NOE BLE BYGGET PÅ DEN:** regelen «ingen baseline-kode er et kravnummer basen erklærer» er FØR NOE BLE BYGGET PÅ DEN:** regelen «ingen baseline-kode er et kravnummer basen erklærer» er
MÅLT mot `okf.declared_reference_numbers` over de fire monterte basene — de fire prosjektkodede MÅLT mot `okf.declared_reference_numbers` over de fire monterte basene — de fire prosjektkodede
settene bærer **0**, og `kontrakt-sorasen-2027` bærer **5 av 5** (`12.1`, `12.12`, `22.1`, settene bærer **0**, og prosesskatalog-settet bærer **5 av 5** (`12.1`, `12.12`, `22.1`,
`52.11`, `51.1` er ekte R761-`prosessnr`). Det er ikke et uhell i settet; det er hva R761 `52.11`, `51.1` er ekte `prosessnr` i katalogen). Det er ikke et uhell i settet; det er hva en
Prosesskoden ER — en norsk vegkontrakts mengdebeskrivelse prises BY prosesskode — så ordrens prosesskatalog ER — en kontrakts mengdebeskrivelse prises BY prosessnummer — så ordrens
regel ville tvunget fram en omskriving av nettopp det settet beslutning (e) ble valgt for å regel ville tvunget fram en omskriving av nettopp det settet beslutning (e) ble valgt for å
bevare. **KOMPLEMENTET beholder begge:** et prisskjema kan prise det kommisjonen NAVNGIR, og kan bevare. **KOMPLEMENTET beholder begge:** et prisskjema kan prise det kommisjonen NAVNGIR, og kan
ikke INNFØRE en korpus-identifikator som en kostlinje ingen bestilte. Ordrens egen mutasjon biter ikke INNFØRE en korpus-identifikator som en kostlinje ingen bestilte. Ordrens egen mutasjon biter
@ -2906,7 +2909,7 @@
`1,1,1,1,1,1,1,2,5,5,6,13,13`: sju erklærte basens FØRSTE krav etter å ha åpnet ETT dokument. `1,1,1,1,1,1,1,2,5,5,6,13,13`: sju erklærte basens FØRSTE krav etter å ha åpnet ETT dokument.
**C1 — ordren ba om en måling, ikke en preferanse, og målingen valgte:** alternativet («det **C1 — ordren ba om en måling, ikke en preferanse, og målingen valgte:** alternativet («det
erklærte dokumentet må ha blitt returnert av et `read_dir` filtrert på et ord fra approachens erklærte dokumentet må ha blitt returnert av et `read_dir` filtrert på et ord fra approachens
label») ble spilt av mot de EKTE listingene og nekter **13 av 13** — inkludert sorasens `12.11`, label») ble spilt av mot de EKTE listingene og nekter **13 av 13** — inkludert prosesskatalog-settets `12.11`,
som ordren navngir som det nærmeste noen kjøring kom; MÅLT kom **null** av de 13 erklæringene som ordren navngir som det nærmeste noen kjøring kom; MÅLT kom **null** av de 13 erklæringene
gjennom en filtrert listing i det hele tatt. En gate som nekter hvert målte tilfelle, riktige som gjennom en filtrert listing i det hele tatt. En gate som nekter hvert målte tilfelle, riktige som
gale, kan ikke SKILLE — det er vakuøs-gate-klassens speilbilde. Den andre regelen (**færre enn gale, kan ikke SKILLE — det er vakuøs-gate-klassens speilbilde. Den andre regelen (**færre enn
@ -2921,9 +2924,9 @@
og P19-gaten («du leste den aldri») er URØRT og sjekkes FØRST: de to er ulike fakta, og den og P19-gaten («du leste den aldri») er URØRT og sjekkes FØRST: de to er ulike fakta, og den
første kan rettes med ett kall. **C2 — nekten navngir naboene:** over de samme seks sporene navnga første kan rettes med ett kall. **C2 — nekten navngir naboene:** over de samme seks sporene navnga
**18 av 143** sti-bærende verktøykall en sti basen ikke holder, og **ELLEVE** av dem er ÉN kjøring **18 av 143** sti-bærende verktøykall en sti basen ikke holder, og **ELLEVE** av dem er ÉN kjøring
som vandrer `R761/4-3`, `4.3`, `4-2`, `4-1`, `4-0`, `4-5`, `4-6` — gjetting på en som vandrer `P900/4-3`, `4.3`, `4-2`, `4-1`, `4-0`, `4-5`, `4-6` — gjetting på en
kapittelnummer-skrivemåte korpuset ikke bruker, mens de ekte navnene er `R761/4`, `R761/41`, kapittelnummer-skrivemåte korpuset ikke bruker, mens de ekte navnene er `P900/4`, `P900/41`,
`R761/42`. Nekten navnga alt den nærmeste LISTBARE forfaren, som er riktig rung; det den ikke `P900/42`. Nekten navnga alt den nærmeste LISTBARE forfaren, som er riktig rung; det den ikke
kunne si var hvilket av den rungens navn som var ment. `okf.nearest_subdirectories` + kunne si var hvilket av den rungens navn som var ment. `okf.nearest_subdirectories` +
`nearest_listable_directory` er ÉN kopi delt av BEGGE nekt-stedene (`read_file` i `explore.py` og `nearest_listable_directory` er ÉN kopi delt av BEGGE nekt-stedene (`read_file` i `explore.py` og
`directory_listing` i `okf.py`) — ett spørsmål om én base må ikke ha to svar (kø-(p)) — og `directory_listing` i `okf.py`) — ett spørsmål om én base må ikke ha to svar (kø-(p)) — og
@ -2933,7 +2936,7 @@
bygget fra `files` kunne navngitt `type: verdict`-laget VED STI — å reklamere i en NEKT for det bygget fra `files` kunne navngitt `type: verdict`-laget VED STI — å reklamere i en NEKT for det
ene laget ingen listing nevner er den samme lekkasjen i unnskyldningens klær. **Rangert etter ene laget ingen listing nevner er den samme lekkasjen i unnskyldningens klær. **Rangert etter
lengste felles prefiks med segmentet som feilet**, så kortest, så navn — dét er hva som setter lengste felles prefiks med segmentet som feilet**, så kortest, så navn — dét er hva som setter
`R761/4` FØRST for `4-3`; uten felles prefiks i det hele tatt degraderer ordenen til «de korteste `P900/4` FØRST for `4-3`; uten felles prefiks i det hele tatt degraderer ordenen til «de korteste
navnene på dette nivået», som er et ærlig «her er hva som ER her». En rangering kan ikke nekte navnene på dette nivået», som er et ærlig «her er hva som ER her». En rangering kan ikke nekte
noe (dette er hjelpetekst på en nekt), så feilretningen er godartet. **MÅLT ETTER: 16 av 18** noe (dette er hjelpetekst på en nekt), så feilretningen er godartet. **MÅLT ETTER: 16 av 18**
nekter navngir nå minst én nabo; de to som ikke gjør det har en forfar som holder dokumenter og nekter navngir nå minst én nabo; de to som ikke gjør det har en forfar som holder dokumenter og
@ -2944,7 +2947,7 @@
detachet (2 røde) · C3(ii) nabolista tom (6) · C3(iii) bygget fra `files` (1, verdict-armen detachet (2 røde) · C3(ii) nabolista tom (6) · C3(iii) bygget fra `files` (1, verdict-armen
ALENE) · C3(iv) tell KALL i stedet for distinkte dokumenter (1, gjentakelses-armen ALENE). ALENE) · C3(iv) tell KALL i stedet for distinkte dokumenter (1, gjentakelses-armen ALENE).
**TRE EKSISTERENDE ARMER ER SKREVET OM, IKKE SVEKKET** (`test_binding_requirement`s **TRE EKSISTERENDE ARMER ER SKREVET OM, IKKE SVEKKET** (`test_binding_requirement`s
korreksjons-arm, `test_right_requirement`s tunnel-arm og `test_across_bundles_cli`s korreksjons-arm, `test_right_requirement`s kravbase-arm og `test_across_bundles_cli`s
per-base-sink-arm): alle tre leste ETT dokument og erklærte, altså nøyaktig den målte per-base-sink-arm): alle tre leste ETT dokument og erklærte, altså nøyaktig den målte
feilklassen; de leser nå tre, og armene beholder sin betydning — en erklæring kjøringens EGET feilklassen; de leser nå tre, og armene beholder sin betydning — en erklæring kjøringens EGET
spor støtter blir AKSEPTERT og REGISTRERT. **Ærlighets-grenser, uttalt:** sporet registrerer ikke spor støtter blir AKSEPTERT og REGISTRERT. **Ærlighets-grenser, uttalt:** sporet registrerer ikke
@ -2957,7 +2960,7 @@
som den kjoepte: **0 av 20** tilnaerminger validerte (runde 4: 4 av 20), og **26 av 26** som den kjoepte: **0 av 20** tilnaerminger validerte (runde 4: 4 av 20), og **26 av 26**
avvisninger — 20 approach-rader PLUSS 6 egne forslag, altsaa en STOERRE populasjon enn de 20 — avvisninger — 20 approach-rader PLUSS 6 egne forslag, altsaa en STOERRE populasjon enn de 20 —
leste `unknown cost code '<paafunn>': not in project P's cost baseline (5 known codes)`. Modellen leste `unknown cost code '<paafunn>': not in project P's cost baseline (5 known codes)`. Modellen
fant paa `signalregulering_konstruksjon`, `VENTIL_IMP`, `RIGG01`, `baerelag_asfalt` og 22 til, og fant paa `RIGG01` og 25 andre koder, og
den KUNNE ikke gjort annet: prisskjemaet naar VALIDATOREN og aldri proposeren, og nekten oppga den KUNNE ikke gjort annet: prisskjemaet naar VALIDATOREN og aldri proposeren, og nekten oppga
ANTALLET kjente koder, ikke ett eneste navn. Steg 5 mater setningen ORDRETT inn i neste forsoeks ANTALLET kjente koder, ikke ett eneste navn. Steg 5 mater setningen ORDRETT inn i neste forsoeks
prompt, saa «du gjettet feil, det finnes fem riktige» baerer ingenting aa korrigere mot. prompt, saa «du gjettet feil, det finnes fem riktige» baerer ingenting aa korrigere mot.
@ -2974,10 +2977,10 @@
M A4 → 2 roede). **Rekkefoelgen er skjemaets egen** — en sortering ville oppfunnet en rangering M A4 → 2 roede). **Rekkefoelgen er skjemaets egen** — en sortering ville oppfunnet en rangering
prosjektet aldri uttalte (M A3 → 1 roed, den armen ALENE). Vinduet er 20, valgt ved MAALING med prosjektet aldri uttalte (M A3 → 1 roed, den armen ALENE). Vinduet er 20, valgt ved MAALING med
nevner: hver kostbaseline i repoet eller dets maalte korpora er hoeyst SEKS koder (kontekstsettene nevner: hver kostbaseline i repoet eller dets maalte korpora er hoeyst SEKS koder (kontekstsettene
5/5/5/5/6, de to `shared/examples` 1 hver, MAJOR-4s avledning av det syntetiske K2-prisskjemaet 3), 5/5/5/5/6, de to leverte energi-eksemplene 1 hver, MAJOR-4s avledning av det syntetiske K2-prisskjemaet 3),
og det stoerste EKTE leverte prisskjemaet maalt er K2s `prissammenstilling-sheet-1.md` med 14 og det stoerste EKTE leverte prisskjemaet maalt er K2s `prissammenstilling-sheet-1.md` med 14
prisede rader av 118 linjer — ingenting maalt naar vinduet; det finnes for den umaalte prisede rader av 118 linjer — ingenting maalt naar vinduet; det finnes for den umaalte
R761-formede mengdebeskrivelsen, der korpuset erklaerer 2 727 `prosessnr`. prosesskatalog-formede mengdebeskrivelsen, der korpuset erklaerer 2 727 `prosessnr`.
**`rejection_stage` er koblingen P21s «a4 5/5 paa stage0-baseline» hviler paa** og noekler paa **`rejection_stage` er koblingen P21s «a4 5/5 paa stage0-baseline» hviler paa** og noekler paa
delstrengen `cost baseline (` — hadde den nye klausulen flyttet den, ville hver stadium-0-nekt delstrengen `cost baseline (` — hadde den nye klausulen flyttet den, ville hver stadium-0-nekt
blitt omdoept til `other` i stillhet (M A7 → 2 roede, hvorav ett ELDRE uavhengig vitne). blitt omdoept til `other` i stillhet (M A7 → 2 roede, hvorav ett ELDRE uavhengig vitne).
@ -3009,14 +3012,14 @@
ett trinn over. **Ordene som sammenlignes er DOKUMENTETS, aldri `ref`** — kallerens eget argument ett trinn over. **Ordene som sammenlignes er DOKUMENTETS, aldri `ref`** — kallerens eget argument
ekkoet tilbake, og en sammenligning mot kallerens input kan bare vaere enig (M B4 → 1 roed, den ekkoet tilbake, og en sammenligning mot kallerens input kan bare vaere enig (M B4 → 1 roed, den
armen ALENE; P20/A1s regel anvendt paa halvdelen P20 ikke naadde). **Sjenerøs i BEGGE retninger** armen ALENE; P20/A1s regel anvendt paa halvdelen P20 ikke naadde). **Sjenerøs i BEGGE retninger**
(delstreng hver vei, saa `rundkjoring` moeter `Rundkjoringer` og `senkekostnader` moeter `senke`), (delstreng hver vei, saa `sikkerhetskopi` moeter `Sikkerhetskopier` og `senkekostnader` moeter `senke`),
og feilretningen er VALGT: rapporten sier ett av to, og bare ett av dem kan gjoere skade — en og feilretningen er VALGT: rapporten sier ett av to, og bare ett av dem kan gjoere skade — en
falsk «ingen overlapp» skyver en modell BORT fra en erklaering som var riktig, mens en falsk falsk «ingen overlapp» skyver en modell BORT fra en erklaering som var riktig, mens en falsk
«overlapp» bare gjoer rapporten stille. Delstreng feiler mot stille (P18s `filter` valgte samme «overlapp» bare gjoer rapporten stille. Delstreng feiler mot stille (P18s `filter` valgte samme
retning av samme grunn; M B7 → 1 roed, M B2 → 5, M B3 → 2). `_LABEL_WORD_MIN = 4`, ellers deler retning av samme grunn; M B7 → 1 roed, M B2 → 5, M B3 → 2). `_LABEL_WORD_MIN = 4`, ellers deler
hver label «for»/«med»/«til» med et halvt korpus (M B8 → 3 roede). hver label «for»/«med»/«til» med et halvt korpus (M B8 → 3 roede).
**MAALT FOER DEN BLE BYGGET**, offline mot de seks sporene slik ordren krevde (ingen betalte kall **MAALT FOER DEN BLE BYGGET**, offline mot de seks sporene slik ordren krevde (ingen betalte kall
i DEL B): regelen TALER paa **10 av 12** erklaeringer og tier paa 2 (begge fv412, paa i DEL B): regelen TALER paa **10 av 12** erklaeringer og tier paa 2 (begge i ett kravbase-sett, paa
`materialer`). En regel som talte paa 12 av 12 — eller paa 0 av 12 — kunne ikke skilt de to `materialer`). En regel som talte paa 12 av 12 — eller paa 0 av 12 — kunne ikke skilt de to
klassene, samme proeve P21/C1s terskel maatte bestaa. **`labels` DEFAULTER til tom**, saa hvert klassene, samme proeve P21/C1s terskel maatte bestaa. **`labels` DEFAULTER til tom**, saa hvert
kallsted skrevet foer i dag er BYTE-IDENTISK og de tre noeklene UTELATES (fravaerende, ikke tomme: kallsted skrevet foer i dag er BYTE-IDENTISK og de tre noeklene UTELATES (fravaerende, ikke tomme:
@ -3042,8 +3045,8 @@
Grunnen er strukturell, ikke tilfeldig: den naermeste listbare forfaren til en GJETTET dokumentsti Grunnen er strukturell, ikke tilfeldig: den naermeste listbare forfaren til en GJETTET dokumentsti
holder ofte dokumenter og ingen underkataloger, og da ble naboklausulen utelatt — BEVISST, fordi en holder ofte dokumenter og ingen underkataloger, og da ble naboklausulen utelatt — BEVISST, fordi en
tom liste er en setning uten innhold. **MAALT over runde 5s seks `read_file`-bom: TRE lander paa en tom liste er en setning uten innhold. **MAALT over runde 5s seks `read_file`-bom: TRE lander paa en
slik forfar** (`krav/N100` med 445 dokumenter; `R761/1` med NOEYAKTIG ETT — som to separate slik forfar** (`krav/D100` med 445 dokumenter; `P900/1` med NOEYAKTIG ETT — som to separate
gjetninger i samme kjoering, `R761/1/1-1.md` og `R761/1/R761-1-1_id-...md`, begge strakte seg gjetninger i samme kjoering, `P900/1/1-1.md` og `P900/1/P900-1-1_id-...md`, begge strakte seg
etter), og tre har underkataloger og var alt besvart. `okf.nearest_documents` er soesknet til etter), og tre har underkataloger og var alt besvart. `okf.nearest_documents` er soesknet til
`nearest_subdirectories`, ALDRI en utvidelse av den: **aldri begge klausuler** (forfaren er ETT `nearest_subdirectories`, ALDRI en utvidelse av den: **aldri begge klausuler** (forfaren er ETT
nivaa, og aa navngi dens dokumenter naar den ogsaa har underkataloger besvarer et annet spoersmaal nivaa, og aa navngi dens dokumenter naar den ogsaa har underkataloger besvarer et annet spoersmaal
@ -3058,7 +3061,7 @@
**`_shared_prefix` er den ene rangeringsregelen, delt av begge soesken**, og det staar her fordi en **`_shared_prefix` er den ene rangeringsregelen, delt av begge soesken**, og det staar her fordi en
MUTASJON FANT DEN UVITNET: aa bytte den mot en ren revers-sortering lot HELE suiten staa groenn MUTASJON FANT DEN UVITNET: aa bytte den mot en ren revers-sortering lot HELE suiten staa groenn
(1890/5) — bindingen, kilden og resolver-egenskapen var alle gatet, og REKKEFOELGEN var det ikke. (1890/5) — bindingen, kilden og resolver-egenskapen var alle gatet, og REKKEFOELGEN var det ikke.
For `R761/1` koster det ingenting (ett dokument, ett svar), men et nivaa i et levert korpus kan For `P900/1` koster det ingenting (ett dokument, ett svar), men et nivaa i et levert korpus kan
holde 445, og da ER hvilke fem den navngir hele verdien av klausulen. Den nye armen bygger et nivaa holde 445, og da ER hvilke fem den navngir hele verdien av klausulen. Den nye armen bygger et nivaa
der det naermeste navnet ogsaa er det LENGSTE, saa en lengde-regel legger det sist og en der det naermeste navnet ogsaa er det LENGSTE, saa en lengde-regel legger det sist og en
alfabetisk legger et annet foerst — bare prefiks-regelen legger det foerst (M C7 → 1 roed etter alfabetisk legger et annet foerst — bare prefiks-regelen legger det foerst (M C7 → 1 roed etter
@ -3090,7 +3093,7 @@
sporingskravet borte (2) · AI-vakten borte (1) · `>` i stedet for `≥` 80 % (1) · godkjennings- sporingskravet borte (2) · AI-vakten borte (1) · `>` i stedet for `≥` 80 % (1) · godkjennings-
vakten borte (1) · rad 6 ignorerer k (1) · rad 7 blir fellende (2). **Ærlighets-grenser, uttalt:** vakten borte (1) · rad 6 ignorerer k (1) · rad 7 blir fellende (2). **Ærlighets-grenser, uttalt:**
basene rad 6–7 dømmer mot er et annet repos montering og kan være under ombygging (målt 17.09: basene rad 6–7 dømmer mot er et annet repos montering og kan være under ombygging (målt 17.09:
`r761-2025` uten `index.md`) — `--bundle-root` peker da på en utpakket kopi; bevisene for type 1, 3 prosesskatalogen uten `index.md`) — `--bundle-root` peker da på en utpakket kopi; bevisene for type 1, 3
og 7 er EKSISTERENDE tester registrert ved node-id, så en omdøping gjør typen rød til registeret og 7 er EKSISTERENDE tester registrert ved node-id, så en omdøping gjør typen rød til registeret
rettes (gatet av en egen arm). rettes (gatet av en egen arm).
- **v1-gaten er HERDET mot forfalskning — og sier det den ikke kan bevise (uavhengig review 17.09):** - **v1-gaten er HERDET mot forfalskning — og sier det den ikke kan bevise (uavhengig review 17.09):**
@ -3129,7 +3132,7 @@
stedet for å skjule at modellstøy og et besvart innspill ser like ut. Rapportens stadie-navn er stedet for å skjule at modellstøy og et besvart innspill ser like ut. Rapportens stadie-navn er
prosa, aldri `stage4-p90`, og et sitat bærer ANTALLET siterte steder. Load-bearing MÅLT prosa, aldri `stage4-p90`, og et sitat bærer ANTALLET siterte steder. Load-bearing MÅLT
(`tests/test_round_builder_loadbearing.py`, 40 armer, hvert tall talt en gang til fra (`tests/test_round_builder_loadbearing.py`, 40 armer, hvert tall talt en gang til fra
fixturens egen tabell — men «MÅLT» om ANTALLET holder først fra 19.09, se raden under); MÅLT på fire EKTE arkiverte utbokser (`tunnel-hauglia-2027` -04/-06/-07 fixturens egen tabell — men «MÅLT» om ANTALLET holder først fra 19.09, se raden under); MÅLT på fire EKTE arkiverte utbokser (et kravbase-sett, kjøring -04/-06/-07
/-08): gaten leser rundene, rad 1 = **FORM OK, IKKE BEVIST**. **Ærlighets-grense, uttalt:** rad /-08): gaten leser rundene, rad 1 = **FORM OK, IKKE BEVIST**. **Ærlighets-grense, uttalt:** rad
2 blir RØD og ikke FORM OK på de samme rundene — radene endret seg (2, 5, 5), men ingen endring 2 blir RØD og ikke FORM OK på de samme rundene — radene endret seg (2, 5, 5), men ingen endring
er sporet til en feedback-id, fordi sporingen ikke finnes ennå. Den hører i oversettelsen er sporet til en feedback-id, fordi sporingen ikke finnes ennå. Den hører i oversettelsen
@ -3149,14 +3152,14 @@
gitt. FIRE av de fem ligger ikke i en utboks som også har coverage — og for `plan-review` sier gitt. FIRE av de fem ligger ikke i en utboks som også har coverage — og for `plan-review` sier
det ingenting, for den typen finnes ikke som artefakt i repoet i det hele tatt (0 filer). det ingenting, for den typen finnes ikke som artefakt i repoet i det hele tatt (0 filer).
`multibase` GJØR det: fire utbokser (`p17b-multibase/`, `p20-stress/`, `p21-stress/`, `multibase` GJØR det: fire utbokser (`p17b-multibase/`, `p20-stress/`, `p21-stress/`,
`p22-stress/`, alle `lindaas`) har både multibase og coverage, og hver av dem har to `p22-stress/`, alle for multi-base-settet) har både multibase og coverage, og hver av dem har to
coverage-filer. Det er derfor de faller utenfor «nøyaktig én coverage»-regelen unionen telles coverage-filer. Det er derfor de faller utenfor «nøyaktig én coverage»-regelen unionen telles
over — og samtidig beviset på at sju er GULVET målingen gir, ikke et tak. Binderen kopierer over — og samtidig beviset på at sju er GULVET målingen gir, ikke et tak. Binderen kopierer
dessuten på glob, ikke på denne lista, så en type utenfor den bæres uansett). **Setningen er dessuten på glob, ikke på denne lista, så en type utenfor den bæres uansett). **Setningen er
skrevet om TRE ganger og var usann hver gang; den er nå pinnet** av en arm som teller utboksene skrevet om TRE ganger og var usann hver gang; den er nå pinnet** av en arm som teller utboksene
og krever at raden sier det tellingen sier og krever at raden sier det tellingen sier
(`test_the_ledgers_claim_about_the_five_other_types_is_what_the_repo_measures`). Talt over de (`test_the_ledgers_claim_about_the_five_other_types_is_what_the_repo_measures`). Talt over de
fire arkiverte kjøringene sjekkpunktet leste (`tunnel-hauglia-2027` -04/-06/-07/-08; hver fire arkiverte kjøringene sjekkpunktet leste (kravbase-settets -04/-06/-07/-08; hver
`<run_id>-<rest>.json` typet som `<run_id>-<rest>.json` typet som
`proposal`/`outcome` når `<rest>` ender der, ellers `<rest>` selv): `-06`, `-07` og `-08` har `proposal`/`outcome` når `<rest>` ender der, ellers `<rest>` selv): `-06`, `-07` og `-08` har
alle sju (15 filer hver), `-04` har seks (12 filer, ingen `parse-failures` — den skrives bare når alle sju (15 filer hver), `-04` har seks (12 filer, ingen `parse-failures` — den skrives bare når
@ -3174,7 +3177,7 @@
kjøringens hele hentede kontekst stemplet én gang per forslag. Rapporten kan ikke gjøre det kjøringens hele hentede kontekst stemplet én gang per forslag. Rapporten kan ikke gjøre det
sitatet informativt; den kan slutte å gjenta det, og si hva lista faktisk er · **samme sitatet informativt; den kan slutte å gjenta det, og si hva lista faktisk er · **samme
kostnadslinje på begge sider av dommen navngis der det skjer** (den ekte rapporten avviste kostnadslinje på begge sider av dommen navngis der det skjer** (den ekte rapporten avviste
`TUN-LYS-01` under én etikett og validerte den under en annen uten å si det — `TUN-VENT-01` én kostnadslinje under én etikett og validerte den under en annen uten å si det — en andre linje
gjorde det samme) · en **fjernet tilnærming** skrives med etiketten fagpersonen så, med id-en i gjorde det samme) · en **fjernet tilnærming** skrives med etiketten fagpersonen så, med id-en i
parentes, lest fra coverage i FORRIGE rundes egen utboks; `outcome.json` beholder sine fire parentes, lest fra coverage i FORRIGE rundes egen utboks; `outcome.json` beholder sine fire
kolonner. **12 av 12 mutanter felt** i scratch-klone, kontroll 40 av 40. Den tolvte var først kolonner. **12 av 12 mutanter felt** i scratch-klone, kontroll 40 av 40. Den tolvte var først
@ -3219,7 +3222,7 @@
`rejection_stage` `unsupported`, dommeren) sjekker klassen FØRST. `validator_decision` forblir `rejection_stage` `unsupported`, dommeren) sjekker klassen FØRST. `validator_decision` forblir
`validated` (den speiler kun validatoren). **Regelen er aktiv nøyaktig når debatten hadde `validated` (den speiler kun validatoren). **Regelen er aktiv nøyaktig når debatten hadde
erklæringsverktøyet** — også på mikro-basen, som har 0 kravnumre (PM-rettelse: ingen erklæringsverktøyet** — også på mikro-basen, som har 0 kravnumre (PM-rettelse: ingen
spesialbehandling); veg-stien og pre-pass-stien har ingen trapp og er urørt. Erklæringens spesialbehandling); referanse-stien og pre-pass-stien har ingen trapp og er urørt. Erklæringens
KVALITET dømmes ikke (P22 § 4), så regelen kan spilles ved å erklære et hvilket som helst lest KVALITET dømmes ikke (P22 § 4), så regelen kan spilles ved å erklære et hvilket som helst lest
dokument — uttalt svakhet. Dommeren leser en erklæring under tilnærmingens id som `approach`, en dokument — uttalt svakhet. Dommeren leser en erklæring under tilnærmingens id som `approach`, en
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
@ -3227,13 +3230,16 @@
(`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 - **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 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 korpus-tester leste et annet repos levende `build/ferdig`-mappe DIREKTE. **MÅLT 17.09 17:43:**
bygget om `r761-2025`, rad 6–7 sa «IKKE MÅLT» og fem tester falt, for en endring ingen her gjorde. korpusbygget bygget om en av kildebasene, 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 **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 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å 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, 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). mens BUNDLENE ALDRI committes her (et eksternt korpus skal ikke til en offentlig flate). (Siden
23.09 er standardlageret de to syntetiske eksempelbasene som følger med pakken,
`src/portfolio_optimiser/data/kunnskapsbaser/`; et eget korpus pekes ut med
`PORTFOLIO_FROZEN_BUNDLES`.)
**Tre tilstander, skilt VED KONSTRUKSJON, og den tredje er hele poenget:** kopien MATCHER → den **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 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 eksisterende `except OSError` gir IKKE MÅLT + exit 1 uendret og korpus-testene SKIPPER (MAJOR-3s
@ -3245,19 +3251,19 @@
bytene** — uten det ville et korpus stokket om under samme bytes pinnet rent — og katalognavnet 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 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 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 (README § Frozen knowledge bases). `--bundle-root` / `PORTFOLIO_BUNDLE_ROOT` består som
operatørens EKSPLISITTE, UPINNEDE levende montering — måten å se på et ferskt korpus før man 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. 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` **Grep-gaten dekker BEGGE stavemåtene av det andre repoets byggesti, hver med sin egen
(3 treff før) og det siterte sti-segmentet `"vegnormal-okf"` (4 treff før) — en fil-bred gate med kjent-positiv:** stien til byggemappa (3 treff før) og det siterte sti-segmentet (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 É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` (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. MÅLT folder to tilstøtende literaler 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 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, 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) · **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 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) · skipper på avvik også (4 — én per fil) · M6a byggestien tilbake i `src` (1) ·
M6b det siterte sti-segmentet tilbake i en test (1) · M7 katalognavnet dropper den korte digesten 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 (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 overstyringen ignoreres (3, hvorav TO i `test_stress_judge_loadbearing.py`, uavhengige vitner
@ -3269,7 +3275,7 @@
kjørt med `--bundle-root` måler ikke det pinnede korpuset og sier det ikke — pinnen er default, 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 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 (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. tatt 18.09 fra korpusbyggets mappe KUN ved lesing — kildens mtimer er uendret.
- **B-gaten måler po som VERKTØYKASSE Claude Code driver — og po får aldri en vei tilbake til - **B-gaten måler po som VERKTØYKASSE Claude Code driver — og po får aldri en vei tilbake til
Claude (19.09, ordre `20260919T040628Z-4756715055`, REPARERT samme dag etter PM-sjekkpunktet, Claude (19.09, ordre `20260919T040628Z-4756715055`, REPARERT samme dag etter PM-sjekkpunktet,

View file

@ -50,7 +50,7 @@ The verdict is always the human's; the machine only ever translates and structur
This recipe describes the *process*. It does not say which categories of knowledge a given run This recipe describes the *process*. It does not say which categories of knowledge a given run
needs, what each content type is for, or what happens when one is missing. That is covered, in needs, what each content type is for, or what happens when one is missing. That is covered, in
Norwegian for the domain expert and the technical person together, in Norwegian for the domain expert and the technical person together, in
[`kunnskapsbase-for-en-kjoring.md`](kunnskapsbase-for-en-kjoring.md) — including a worked road [`kunnskapsbase-for-en-kjoring.md`](kunnskapsbase-for-en-kjoring.md) — including a worked example
project from the commission to a base that passes the dry-run check. The two documents are project from the commission to a base that passes the dry-run check. The two documents are
deliberately disjoint: phases and roles live here, composition lives there. deliberately disjoint: phases and roles live here, composition lives there.

View file

@ -32,7 +32,7 @@ Tre dokumenter står rundt dette:
| Kategori | Følger | Hvem eier den | | Kategori | Følger | Hvem eier den |
|---|---|---| |---|---|---|
| **Prosjektlaget** | prosjektet / anlegget | prosjekteier og driftsorganisasjon | | **Prosjektlaget** | prosjektet / anlegget | prosjekteier og driftsorganisasjon |
| **Faglaget** | fagområdet (veglys, tunnel, bygg …) | fagmiljøet | | **Faglaget** | fagområdet (klientpark, kjøling, bygg …) | fagmiljøet |
| **Erfaringslaget** | organisasjonen, over tid | fagekspertene som avgir dommer | | **Erfaringslaget** | organisasjonen, over tid | fagekspertene som avgir dommer |
| **Kjøringslaget** | denne ene bestillingen | bestilleren | | **Kjøringslaget** | denne ene bestillingen | bestilleren |
@ -47,10 +47,10 @@ den i, hva loopen bruker den til, og hva som skjer hvis den mangler. Den viktigs
**`cost-baseline.json`**: mangler den, starter kjøringen uten et ord — og validatoren dømmer da bare **`cost-baseline.json`**: mangler den, starter kjøringen uten et ord — og validatoren dømmer da bare
mot tall forslaget selv oppga (VERIFISERT ved kjøring, [§4.1](#41-den-skarpeste-mangelen-cost-baselinejson)). mot tall forslaget selv oppga (VERIFISERT ved kjøring, [§4.1](#41-den-skarpeste-mangelen-cost-baselinejson)).
**3. Hvordan ser det ut for et veiprosjekt?** [§5](#5-veiprosjektet-fylkesveg-sør-fra-bestilling-til-kjøreklar-base) **3. Hvordan ser det ut for et konkret prosjekt?** [§5](#5-klientparken-hos-eksempelvirksomheten-fra-bestilling-til-kjøreklar-base)
går gjennom en veglysportefølje langs fylkesveg, fra oppdragsfila til en base som består går gjennom klientparken til den oppdiktede Eksempelvirksomheten, fra oppdragsfila til en
kjøreklar-sjekken. Basen det ender i finnes og kjører (VERIFISERT: `shared/examples/veglys-fv-soer/` kjøreklar base. Basen det ender i er sjekket inn (VERIFISERT:
er den demoen bruker). `src/portfolio_optimiser/data/bundles/klientpark-energi/`, den leverte basen demoen leser).
## 1. Hva kjøringen leser, og hvorfor det avgjør hva basen må inneholde ## 1. Hva kjøringen leser, og hvorfor det avgjør hva basen må inneholde
@ -86,7 +86,7 @@ nekter kjøringen å starte (VERIFISERT ved kjøring, [§4.1](#41-den-skarpeste-
`cost-baseline.json` er valgfri, og det er nettopp problemet: uten den starter kjøringen som om alt `cost-baseline.json` er valgfri, og det er nettopp problemet: uten den starter kjøringen som om alt
var i orden. var i orden.
**Målt på veglys-basen:** mappa har 9 filer. Én er `index.md`, én er dommen, to er tallfiler — **Målt på klientpark-basen:** mappa har 9 filer. Én er `index.md`, én er dommen, to er tallfiler —
og **5** er det kjøringen faktisk navigerer inn som kontekst (VERIFISERT: og **5** er det kjøringen faktisk navigerer inn som kontekst (VERIFISERT:
`tests/golden/demo-transcript.stdout` linje 13, «navigerte konseptfiler (5)»). `tests/golden/demo-transcript.stdout` linje 13, «navigerte konseptfiler (5)»).
@ -112,28 +112,30 @@ og **5** er det kjøringen faktisk navigerer inn som kontekst (VERIFISERT:
**Deles metode- og litteraturlaget på tvers?** Svaret er todelt, og begge halvdeler er målt. **Deles metode- og litteraturlaget på tvers?** Svaret er todelt, og begge halvdeler er målt.
*Logisk* er det samme fagstoff: alle tre eksempelbasene (kontorbygg, veglys, tunnel) bærer en *Logisk* er det samme fagstoff: alle tre eksempelbasene (kontorbygg, klientpark, kjøling i
`metode-ipmvp-a.md` og en `kilder-*.md`, og alle tre bygger på samme M&V-rammeverk (IPMVP Option driftssenteret) bærer en `metode-ipmvp-a.md`, og alle tre bygger på samme M&V-rammeverk (IPMVP
A). *Fysisk* er det tre ulike filer: 40, 81 og 98 linjer, med hver sin tittel — «for veglys — og Option A). *Fysisk* er det tre ulike filer — målt 2026-08-21 til 40, 81 og 98 linjer — med hver
hvorfor de andre opsjonene er stengt», «for tunnelstyring — anlegget måler inngangssignalet, ikke sin tittel: «for klientparken — og hvorfor de andre opsjonene er stengt», «for kjølestyring —
energien» (VERIFISERT: `wc -l` + `diff` over de tre). Begge veiprosjekt-basene sier det selv i anlegget måler inngangssignalet, ikke energien» (VERIFISERT: `wc -l` + `diff` over de tre). Begge
`index.md`: «metode- og kildelaget er **materialisert inn her**, ikke lenket på tvers av bundler». de leverte eksempelbasene sier det selv i `index.md`: «metode- og kildelaget er **materialisert
inn her**, ikke lenket på tvers av bundler».
Grunnen er teknisk og ufravikelig: navigasjonen følger aldri en lenke ut av basen Grunnen er teknisk og ufravikelig: navigasjonen følger aldri en lenke ut av basen
([§1](#1-hva-kjøringen-leser-og-hvorfor-det-avgjør-hva-basen-må-inneholde)). Men det er også ([§1](#1-hva-kjøringen-leser-og-hvorfor-det-avgjør-hva-basen-må-inneholde)). Men det er også
faglig riktig: metoden *for veglys* er ikke metoden *for tunnel*. I veglys er ex-post stengt fordi faglig riktig: metoden *for klientparken* er ikke metoden *for kjøleanlegget*. I klientparken er
anlegget mangler måler; i tunnel er ex-ante stengt fordi anlegget ble bygget før noen målte ex-post stengt fordi anlegget mangler måler; i kjøleanlegget er ex-ante stengt fordi anlegget ble
(VERIFISERT: de to `index.md`-filene). En delt fil ville måttet si begge deler og dermed ingen av bygget før noen målte (VERIFISERT: de to `index.md`-filene). En delt fil ville måttet si begge deler og dermed ingen av
dem. dem.
Praktisk betyr det (ANTATT, anbefaling): fagmiljøet eier en **mal** per fagområde; hver base får Praktisk betyr det (ANTATT, anbefaling): fagmiljøet eier en **mal** per fagområde; hver base får
en **tilpasset kopi**; når malen endres, er det en kjent jobb å gå gjennom kopiene. To kopier en **tilpasset kopi**; når malen endres, er det en kjent jobb å gå gjennom kopiene. To kopier
drifter — det er prisen, og den skal være uttalt, ikke skjult. drifter — det er prisen, og den skal være uttalt, ikke skjult.
**Prosjektlaget er det som byttes ut.** Begge veiprosjekt-basene er bygget med fiktivt prosjektlag **Prosjektlaget er det som byttes ut per prosjekt.** Begge de leverte eksempelbasene er fiktive i
og ekte litteraturlag, og sier selv hvordan de er ment brukt: «en produksjons-deployer erstatter begge lag — prosjektet og kildene tilhører den oppdiktede Eksempelvirksomheten — og sier selv
prosjektlaget med en ekte kunnskapsbase og beholder litteraturlaget» (VERIFISERT: begge hvordan de er ment brukt: «En produksjons-deployer erstatter begge lagene med en ekte
`index.md`). Det er nøyaktig kategoriskillet over, skrevet fra eksemplenes side. kunnskapsbase og ekte kilder» (VERIFISERT: begge `index.md`). Kategoriskillet over står likevel:
prosjektlaget skrives fra det ene prosjektets tall, litteraturlaget fra fagområdets kilder.
**Erfaringslaget er reservert.** Ingen automatisk kilde kan skrive en `type: verdict`-fil inn i **Erfaringslaget er reservert.** Ingen automatisk kilde kan skrive en `type: verdict`-fil inn i
basen; den eneste veien dit er en promotering av en dom et menneske har godkjent, eller en basen; den eneste veien dit er en promotering av en dom et menneske har godkjent, eller en
@ -166,7 +168,7 @@ første avgjør om kjøringen i det hele tatt kan starte.
Svaret blir prosjekt-ID-en. Den må være identisk tre steder — kommandolinjen, Svaret blir prosjekt-ID-en. Den må være identisk tre steder — kommandolinjen,
`validator-input.json` og `cost-baseline.json` — ellers nektes kjøringen (VERIFISERT: `validator-input.json` og `cost-baseline.json` — ellers nektes kjøringen (VERIFISERT:
`run.py` `_project_from_bundle`, «bundle project_id … != requested»). Velg en ID uten mellomrom og `run.py` `_project_from_bundle`, «bundle project_id … != requested»). Velg en ID uten mellomrom og
æøå; eksemplene bruker formen `VEGLYS-FV-SOER` (KONVENSJON). æøå; eksemplene bruker formen `KLIENTPARK-ENERGI` (KONVENSJON).
*Hvis svaret er «flere anlegg»:* én base per prosjekt. En portefølje er flere baser, kjørt i *Hvis svaret er «flere anlegg»:* én base per prosjekt. En portefølje er flere baser, kjørt i
porteføljemodus. porteføljemodus.
@ -196,33 +198,33 @@ fortsatt den ene projiserte kandidaten — se
**4. Hvilke harde rammer gjelder?** **4. Hvilke harde rammer gjelder?**
Minstekrav som setter et gulv ingen besparelse kan gå under, ting som ikke kan endres, budsjett- Minstekrav som setter et gulv ingen besparelse kan gå under, ting som ikke kan endres, budsjett-
og anskaffelsesrammer. Svaret skrives inn i `type: project`-fila. Veglys-eksempelet har fire: og anskaffelsesrammer. Svaret skrives inn i `type: project`-fila. Klientpark-eksempelet har fire
lystekniske minstekrav, vedlikeholdsfaktoren, at nattslukking ikke kan antas, og at tiltak som begrenser tiltakene: ytelseskravene, ytelsesreserven, at nattlig avstenging ikke kan antas,
vurderes inne i porteføljen (VERIFISERT: `shared/examples/veglys-fv-soer/veglys-fv-soer.md`, og at tiltak vurderes inne i porteføljen (VERIFISERT:
«Rammer»). Agentene leser dem som tekst; koden håndhever dem ikke (ANTATT: det følger av at `src/portfolio_optimiser/data/bundles/klientpark-energi/klientpark-energi.md`, «Rammer»). Agentene leser dem som tekst; koden håndhever dem ikke (ANTATT: det følger av at
koden bare leser `title` fra fila, men er ikke målt mot en levende modell). koden bare leser `title` fra fila, men er ikke målt mot en levende modell).
**5. Hvilke tilnærminger vil du ha vurdert, og hvorfor?** **5. Hvilke tilnærminger vil du ha vurdert, og hvorfor?**
Svaret blir oppdragsfila — se [bestille-en-kjoring.md](bestille-en-kjoring.md). `description`-feltet Svaret blir oppdragsfila — se [bestille-en-kjoring.md](bestille-en-kjoring.md). `description`-feltet
mates ordrett inn til modellen; det er der fagkunnskapen om *hvorfor* tiltaket er verdt å prøve mates ordrett inn til modellen; det er der fagkunnskapen om *hvorfor* tiltaket er verdt å prøve
gjør en forskjell. Hver tilnærming bør ha et motstykke i basen: en `type: hypothesis`-fil med gjør en forskjell. Hver tilnærming bør ha et motstykke i basen: en `type: hypothesis`-fil med
parametere, modellert besparelse og kjent usikkerhet (KONVENSJON: begge veiprosjekt-basene har parametere, modellert besparelse og kjent usikkerhet (KONVENSJON: begge de leverte eksempelbasene
én hypothesis-fil per kandidat-tiltak; koden krever det ikke). har én hypothesis-fil per kandidat-tiltak; koden krever det ikke).
**6. Hvordan måles og verifiseres en besparelse i dette faget?** **6. Hvordan måles og verifiseres en besparelse i dette faget?**
Svaret blir `type: methodology`-fila. Den forteller agentene *hvorfor* modellert og faktisk Svaret blir `type: methodology`-fila. Den forteller agentene *hvorfor* modellert og faktisk
besparelse kan sprike, og hvilken måleopsjon som er åpen for dette anlegget. For veglys er svaret besparelse kan sprike, og hvilken måleopsjon som er åpen for dette anlegget. For klientparken er
«IPMVP Option A, ved eliminasjon» fordi anlegget mangler måler (VERIFISERT: veglys svaret «IPMVP Option A, ved eliminasjon» fordi anlegget mangler måler (VERIFISERT: klientparkens
`metode-ipmvp-a.md`). `metode-ipmvp-a.md`).
*Hvis fagmiljøet har en mal:* kopier og tilpass. Tilpasningen er ikke pynt — den delen som *Hvis fagmiljøet har en mal:* kopier og tilpass. Tilpasningen er ikke pynt — den delen som
forklarer hvilke opsjoner som er *stengt for dette anlegget* er prosjektspesifikk. forklarer hvilke opsjoner som er *stengt for dette anlegget* er prosjektspesifikk.
**7. Hva vet litteraturen om gapet mellom modellert og faktisk besparelse her?** **7. Hva vet litteraturen om gapet mellom modellert og faktisk besparelse her?**
Svaret blir `type: reference`-fila. Skill skarpt mellom det som er målt i vårt eget land/regime Svaret blir `type: reference`-fila. Skill skarpt mellom det som er målt på eget anlegg og det
og det som er lånt fra andre program — veglys-eksempelet deler fila i «Del A — norsk materiale» som er lånt fra andre program — klientpark-eksempelet deler fila i «Del A — materiale om
og «Del B — lånt materiale», og sier hvorfor: «Å blande de to ville gjort et lånt tall til en klientparken» og «Del B — lånt materiale», og sier hvorfor: «Å blande de to ville gjort et lånt
norsk måling» (VERIFISERT: `kilder-veglys-realisering.md`). tall til en måling av klientparken» (VERIFISERT: `kilder-klientpark-realisering.md`).
*Hvis svaret er «det finnes ingen norsk måling»:* skriv det. Et navngitt evidenshull er innhold; *Hvis svaret er «det finnes ingen måling på eget anlegg»:* skriv det. Et navngitt evidenshull er innhold;
et oppdiktet tall er forurensning. et oppdiktet tall er forurensning.
**8. Finnes det tidligere erfaring med lignende tiltak — en dom noen faktisk har avgitt?** **8. Finnes det tidligere erfaring med lignende tiltak — en dom noen faktisk har avgitt?**
@ -291,7 +293,8 @@ Fila forankrer gaten i prosjektets ekte kostlinjer: hvert forslag avstemmes mot
eller enhetspris utenfor 5 % av baselinens verdi (VERIFISERT: `validator.py:154-190`, `:210-214`). eller enhetspris utenfor 5 % av baselinens verdi (VERIFISERT: `validator.py:154-190`, `:210-214`).
Men fila er **valgfri** på bundle-stien, og fraværet er stille. Målt 2026-08-21 med Men fila er **valgfri** på bundle-stien, og fraværet er stille. Målt 2026-08-21 med
`--live-dry-run` på fire kopier av veglys-basen, samme kommando, samme oppdragsfil: `--live-dry-run` på fire kopier av den forrige eksempelbasen (samme filsett som klientpark-basen
i §5), samme kommando, samme oppdragsfil:
| Variant | Utfall | rc | Melding | | Variant | Utfall | rc | Melding |
|---|---|---|---| |---|---|---|---|
@ -315,7 +318,7 @@ stammer fra den *samme* oppslagsverdien inne i kjøringen — ikke fra en ny les
skrevet av tørrkjøringen, av den fulle enkeltkjøringen og per prosjekt i porteføljemodus. Er skrevet av tørrkjøringen, av den fulle enkeltkjøringen og per prosjekt i porteføljemodus. Er
basen forankret, skrives **ingen linje i det hele tatt** — en linje for noe kjøringen ikke har basen forankret, skrives **ingen linje i det hele tatt** — en linje for noe kjøringen ikke har
utelates, samme regel som resten av kunngjøringen følger (VERIFISERT: kjørt 2026-08-21 mot to utelates, samme regel som resten av kunngjøringen følger (VERIFISERT: kjørt 2026-08-21 mot to
kopier av veglys-basen; intakt kopi er byte-uendret, kopi uten fila bærer linja). kopier av den forrige eksempelbasen; intakt kopi er byte-uendret, kopi uten fila bærer linja).
Ankeringen er fortsatt **valgfri** — en base skrevet før fila fantes kjører uendret. Dette er Ankeringen er fortsatt **valgfri** — en base skrevet før fila fantes kjører uendret. Dette er
synlighet, ikke en ny nekt. Demoen har sin egen, norske formulering synlighet, ikke en ny nekt. Demoen har sin egen, norske formulering
@ -328,74 +331,80 @@ er det eneste spørsmålet der et «vet ikke» ikke stopper noe — og derfor de
dokumenteres utenfor systemet. En base uten `cost-baseline.json` bør ikke kalles kjøreklar av dokumenteres utenfor systemet. En base uten `cost-baseline.json` bør ikke kalles kjøreklar av
noen som vet hva fila gjør (ANTATT: en arbeidsregel; koden lar deg kjøre). noen som vet hva fila gjør (ANTATT: en arbeidsregel; koden lar deg kjøre).
## 5. Veiprosjektet Fylkesveg Sør: fra bestilling til kjøreklar base ## 5. Klientparken hos Eksempelvirksomheten: fra bestilling til kjøreklar base
Eksempelet følger en veglysportefølje langs fylkesveg. Basen det ender i er Eksempelet følger klientparken — 9 500 stasjonære arbeidsstasjoner — i den oppdiktede
`shared/examples/veglys-fv-soer/`, som er sjekket inn, kjører i demoen og er målt med Eksempelvirksomheten. Basen det ender i er `src/portfolio_optimiser/data/bundles/klientpark-energi/`, som er
kjøreklar-sjekken under. Prosjektlaget i den basen er **fiktivt** — porteføljen finnes ikke — sjekket inn og er den leverte basen demoen leser. Både prosjektlaget og litteraturlaget er
mens litteraturlaget er ekte og kildebelagt (VERIFISERT: basens `index.md`). Det gjør den til et **fiktive** — porteføljen og kildene finnes ikke (VERIFISERT: basens `index.md`). Skillet
godt eksempel på nøyaktig det skillet [§2](#2-kategoriene-hva-følger-hva) handler om: et ekte [§2](#2-kategoriene-hva-følger-hva) handler om er likevel synlig i den: prosjektlaget er skrevet
prosjekt bytter ut prosjektlaget og beholder resten. fra porteføljens egne tall, litteraturlaget fra fagområdets kilder, og et ekte prosjekt bytter ut
prosjektlaget.
> **Om målingene i dette kapitlet.** Kjøreklar-sjekken og tørrkjøringene i §4.1 ble målt
> 2026-08-21 på den forrige eksempelbasen, som hadde samme filsett, samme kostlinje og samme tall.
> Basen er siden skrevet om til klientpark-eksempelet; navnene i utskriftene under er byttet til
> eksempelets, og målingene er **ikke** gjentatt på den omskrevne basen.
### 5.1 Bestillingen ### 5.1 Bestillingen
En driftsleder i fylkeskommunen vil vite hva LED-utskifting gir på de eldste strekningene, og om En driftsleder i IT-driftsavdelingen vil vite hva utskifting gir på de eldste maskinene, og om
styring oppå det er verdt noe. Oppdragsfila (VERIFISERT: akseptert av kjøringen, kunngjøringen strømstyring oppå det er verdt noe. Oppdragsfila (VERIFISERT 2026-08-21: en oppdragsfil med samme
under er dens faktiske utskrift): form ble akseptert av kjøringen, og kunngjøringen under er dens utskrift, med eksempelets navn):
```json ```json
{ {
"objective": "Redusere energikostnaden i veglysporteføljen Fylkesveg Sør uten å gå under lystekniske minstekrav, med tiltak som kan bestilles i 2027.", "objective": "Redusere energikostnaden i klientparken til Eksempelvirksomheten uten å gå under ytelseskravene, med tiltak som kan bestilles i 2027.",
"approaches": [ "approaches": [
{ {
"id": "led-trinn-1", "id": "pc-trinn-1",
"label": "LED-utskifting av de 2 500 eldste HPS-punktene", "label": "Utskifting av de 2 500 eldste stasjonære PC-ene",
"description": "Drift melder at armaturene på de eldste strekningene er fra før 2005 og byttes hyppig; vi vil vite hva ren armaturutskifting gir før styring vurderes." "description": "Drift melder at maskinene på de eldste kontorene er fra før 2015 og byttes hyppig; vi vil vite hva ren maskinutskifting gir før strømstyring vurderes."
}, },
{ {
"id": "adaptiv-styring", "id": "adaptiv-stromstyring",
"label": "Adaptiv styring på de LED-utskiftede punktene", "label": "Adaptiv strømstyring på de utskiftede maskinene",
"description": "Håndbok V124 tillater MF 0,85; vi tror nye anlegg overdimensjoneres og at marginen kan hentes ut med dimming, men har ingen måling." "description": "IT-driftshåndboken tillater RF 0,85; vi tror nye maskiner overdimensjoneres og at marginen kan hentes ut med strømsparing, men har ingen måling."
} }
], ],
"allow_own_proposals": true, "allow_own_proposals": true,
"success_criteria": "Minst ett tiltak som passerer validatoren og som driftsavdelingen kan stå inne for." "success_criteria": "Minst ett tiltak som passerer validatoren og som IT-driftsavdelingen kan stå inne for."
} }
``` ```
Bestillingen er den første målingen av basen: hver tilnærming nevner ting basen må kunne svare Bestillingen er den første målingen av basen: hver tilnærming nevner ting basen må kunne svare
på — armaturalder, vedlikeholdsfaktor, lystekniske minstekrav, fravær av måling. på — maskinalder, ytelsesreserve, ytelseskrav, fravær av måling.
### 5.2 Spørsmålene, besvart for dette prosjektet ### 5.2 Spørsmålene, besvart for dette prosjektet
| # | Spørsmål | Svar for Fylkesveg Sør | Lander i | | # | Spørsmål | Svar for klientparken | Lander i |
|---|---|---|---| |---|---|---|---|
| 1 | Prosjekt-ID | `VEGLYS-FV-SOER` — samme streng på kommandolinjen og i begge tallfiler | alle tre | | 1 | Prosjekt-ID | `KLIENTPARK-ENERGI` — samme streng på kommandolinjen og i begge tallfiler | alle tre |
| 2 | Kostlinjer med ekte tall | én linje: porteføljens årlige energikostnad, `ENERGI-VEGLYS-EL`, 4 386 150 kWh à 1,00 NOK. Investeringskostnad **bevisst utelatt** — ingen kilde gir NOK per lyspunkt | `cost-baseline.json` | | 2 | Kostlinjer med ekte tall | én linje: porteføljens årlige energikostnad, `ENERGI-KLIENTPARK-EL`, 4 386 150 kWh à 1,00 NOK. Investeringskostnad **bevisst utelatt** — ingen kilde gir NOK per arbeidsstasjon | `cost-baseline.json` |
| 3 | Den ene projiserte kandidaten | LED-utskifting trinn 1 (2 500 punkter, 114 → 70 W), modellert 445 500 NOK/år | `validator-input.json` | | 3 | Den ene projiserte kandidaten | PC-utskifting trinn 1 (2 500 maskiner, 114 → 70 W), modellert 445 500 NOK/år | `validator-input.json` |
| 4 | Harde rammer | lystekniske minstekrav (1,0 cd/m², 5 lx), MF ≤ 0,85, nattslukking kan ikke antas, tiltak vurderes inne i porteføljen | `veglys-fv-soer.md` | | 4 | Harde rammer | ytelseskrav (1,0 s responstid, 5 samtidige applikasjoner), RF ≤ 0,85, nattlig avstenging kan ikke antas, tiltak vurderes inne i porteføljen | `klientpark-energi.md` |
| 5 | Tilnærminger | de to i oppdragsfila, pluss systemets egne | oppdragsfila + to `hypothesis`-filer | | 5 | Tilnærminger | de to i oppdragsfila, pluss systemets egne | oppdragsfila + to `hypothesis`-filer |
| 6 | Målemetode | IPMVP Option A, ved eliminasjon: umålt anlegg stenger B, C og D | `metode-ipmvp-a.md` | | 6 | Målemetode | IPMVP Option A, ved eliminasjon: umålt anlegg stenger B, C og D | `metode-ipmvp-a.md` |
| 7 | Litteratur om gapet | norsk: baseline, regelverk og *årsaken* til at gapet ikke kan ses (mangler måler). Lånt: selve realiseringsgraden (amerikansk programlitteratur, 0,81) | `kilder-veglys-realisering.md` | | 7 | Litteratur om gapet | om klientparken: baseline, normankere og *årsaken* til at gapet ikke kan ses (mangler måler). Lånt: selve realiseringsgraden (virksomhetens etterevalueringer av et annet program, 0,81) | `kilder-klientpark-realisering.md` |
| 8 | Tidligere erfaring | én frø-dom: godkjent med realiseringskorreksjon, rate 0,81, **merket som lån** | `verdict-veglys-fro.md` | | 8 | Tidligere erfaring | én frø-dom: godkjent med realiseringskorreksjon, rate 0,81, **merket som lån** | `verdict-klientpark-fro.md` |
| 9 | Avgrensning til kostakse | nei — porteføljen har én kostlinje | — | | 9 | Avgrensning til kostakse | nei — porteføljen har én kostlinje | — |
| 10 | Kilder som data | nei — anleggsregisteret er levert som tall i et notat; alt er håndkuratert | — | | 10 | Kilder som data | nei — utstyrsregisteret er levert som tall i et notat; alt er håndkuratert | — |
| 11 | Hvem dømmer | fylkets egen energirådgiver, etter kjøringen, via innboksen | `--verdict-dir` | | 11 | Hvem dømmer | virksomhetens egen energirådgiver, etter kjøringen, via innboksen | `--verdict-dir` |
(Alle svar i kolonnen «Svar» er VERIFISERT mot filene i `shared/examples/veglys-fv-soer/`; kolonnen (Alle svar i kolonnen «Svar» er VERIFISERT mot filene i `src/portfolio_optimiser/data/bundles/klientpark-energi/`; kolonnen
«Lander i» er VERIFISERT mot filnavnene der.) «Lander i» er VERIFISERT mot filnavnene der.)
### 5.3 Hva fagpersonene leverer ### 5.3 Hva fagpersonene leverer
| Leveranse | Fra | Form de leverer i | Blir til | | Leveranse | Fra | Form de leverer i | Blir til |
|---|---|---|---| |---|---|---|---|
| Anleggsregister: antall lyspunkter, armaturtype, installert effekt | drift | uttrekk fra anleggsdatabasen, regneark | `type: project` (energibaseline) + raden i `cost-baseline.json` | | Utstyrsregister: antall arbeidsstasjoner, modell, installert effekt | IT-drift | uttrekk fra utstyrsdatabasen, regneark | `type: project` (energibaseline) + raden i `cost-baseline.json` |
| Brenntimer og energipris | drift / økonomi | tabellverdi (Håndbok V124) + fakturagrunnlag | samme; prisbåndet i `validator-input.json` | | Driftstimer og energipris | IT-drift / økonomi | tabellverdi (IT-driftshåndboken) + fakturagrunnlag | samme; prisbåndet i `validator-input.json` |
| Kravgrunnlag: lystekniske minstekrav, vedlikeholdsfaktor | fagmiljø vegbelysning | henvisning til NMFV og Håndbok V124 | «Rammer» i `type: project` | | Kravgrunnlag: ytelseskrav, ytelsesreserve | fagmiljø arbeidsflate | henvisning til innkjøpsspesifikasjonen og IT-driftshåndboken | «Rammer» i `type: project` |
| Kandidat-tiltak med parametere | drift + fagmiljø | notat: før/etter-effekt, antall, hva som er utledet | to `type: hypothesis`-filer | | Kandidat-tiltak med parametere | IT-drift + fagmiljø | notat: før/etter-effekt, antall, hva som er utledet | to `type: hypothesis`-filer |
| M&V-praksis for umålte anlegg | fagmiljø | mal for IPMVP, tilpasset | `type: methodology` | | M&V-praksis for umålte anlegg | fagmiljø | mal for IPMVP, tilpasset | `type: methodology` |
| Litteratur om realiseringsgap, med kilde | fagmiljø | kildeliste med URL og årstall, merket norsk/lånt | `type: reference` | | Litteratur om realiseringsgap, med kilde | fagmiljø | kildeliste med URL og årstall, merket eget anlegg/lånt | `type: reference` |
| Tidligere vurdering av LED på småveg | energirådgiver | kort notat: «forvent ~80 % av modellert, fordi …» | `type: verdict` (frø) | | Tidligere vurdering av utskifting i en mindre klientpark | energirådgiver | kort notat: «forvent ~80 % av modellert, fordi …» | `type: verdict` (frø) |
Leveranseformene er ANTATT — de er det en slik leveranse rimelig ser ut som, ikke noe Leveranseformene er ANTATT — de er det en slik leveranse rimelig ser ut som, ikke noe
eksempelbasen dokumenterer. Det som er VERIFISERT er hva hver leveranse *blir til*. eksempelbasen dokumenterer. Det som er VERIFISERT er hva hver leveranse *blir til*.
@ -403,32 +412,32 @@ eksempelbasen dokumenterer. Det som er VERIFISERT er hva hver leveranse *blir ti
### 5.4 Basen som bygges ### 5.4 Basen som bygges
``` ```
veglys-fv-soer/ klientpark-energi/
├── index.md type: index inngangen; lenker til alt under ├── index.md type: index inngangen; lenker til alt under
├── veglys-fv-soer.md type: project porteføljen, energibaselinen, rammene ├── klientpark-energi.md type: project porteføljen, energibaselinen, rammene
├── tiltak-led-utskifting.md type: hypothesis kandidat 1 — den som er projisert ├── tiltak-pc-utskifting.md type: hypothesis kandidat 1 — den som er projisert
├── tiltak-adaptiv-styring.md type: hypothesis kandidat 2 — svakere kildebelagt, og merket slik ├── tiltak-adaptiv-stromstyring.md type: hypothesis kandidat 2 — svakere kildebelagt, og merket slik
├── metode-ipmvp-a.md type: methodology Option A, og hvorfor de andre er stengt ├── metode-ipmvp-a.md type: methodology Option A, og hvorfor de andre er stengt
├── kilder-veglys-realisering.md type: reference Del A norsk / Del B lånt ├── kilder-klientpark-realisering.md type: reference Del A eget anlegg / Del B lånt
├── verdict-veglys-fro.md type: verdict frø-dommen — holdes ute av lesekonteksten ├── verdict-klientpark-fro.md type: verdict frø-dommen — holdes ute av lesekonteksten
├── validator-input.json den projiserte kandidaten ├── validator-input.json den projiserte kandidaten
└── cost-baseline.json prosjektets ene kostlinje └── cost-baseline.json prosjektets ene kostlinje
``` ```
(VERIFISERT: `ls shared/examples/veglys-fv-soer/` og `grep '^type:'` over filene.) (VERIFISERT: `ls src/portfolio_optimiser/data/bundles/klientpark-energi/` og `grep '^type:'` over filene.)
`index.md` gjør to jobber i denne basen. Den første er navigasjon: seks lenker, én per fil, med `index.md` gjør to jobber i denne basen. Den første er navigasjon: seks lenker, én per fil, med
type og én setning hver. Den andre er å si høyt hva som er fiktivt og hva som er ekte, og type og én setning hver. Den andre er å si høyt hva som er fiktivt og hva som er ekte, og
*hvorfor* domenet er valgt — at realiseringsgraden i norsk veglys er «strukturelt usynlig» fordi *hvorfor* domenet er valgt — at realiseringsgraden i klientparken er «strukturelt usynlig» fordi
anlegget mangler måler. Begge deler går ordrett inn som det første agentene leser. anlegget mangler måler. Begge deler går ordrett inn som det første agentene leser.
### 5.5 De to tallfilene — skrevet fra samme linje ### 5.5 De to tallfilene — skrevet fra samme linje
Hele basens tallgrunnlag er én linje aritmetikk: Hele basens tallgrunnlag er én linje aritmetikk:
> 9 500 lyspunkter × 114 W × 4 050 t/år ÷ 1 000 = **4 386 150 kWh/år** à 1,00 NOK = 4 386 150 NOK/år > 9 500 arbeidsstasjoner × 114 W × 4 050 t/år ÷ 1 000 = **4 386 150 kWh/år** à 1,00 NOK = 4 386 150 NOK/år
`cost-baseline.json` bærer den som `ENERGI-VEGLYS-EL: {quantity: 4386150, unit_cost: 1.0}`. `cost-baseline.json` bærer den som `ENERGI-KLIENTPARK-EL: {quantity: 4386150, unit_cost: 1.0}`.
`validator-input.json` bærer **nøyaktig samme** kode, mengde og pris i `affected_items`, pluss den `validator-input.json` bærer **nøyaktig samme** kode, mengde og pris i `affected_items`, pluss den
modellerte besparelsen for trinn 1 (2 500 × 44 W × 4 050 t ÷ 1 000 = 445 500 kWh ≈ 445 500 NOK) modellerte besparelsen for trinn 1 (2 500 × 44 W × 4 050 t ÷ 1 000 = 445 500 kWh ≈ 445 500 NOK)
og prisbåndet 0,70–1,40 NOK/kWh til risikosimuleringen (VERIFISERT: begge filene). De to er ikke og prisbåndet 0,70–1,40 NOK/kWh til risikosimuleringen (VERIFISERT: begge filene). De to er ikke
@ -436,9 +445,9 @@ og prisbåndet 0,70–1,40 NOK/kWh til risikosimuleringen (VERIFISERT: begge fil
toleransen på 5 % skal lukkes: ved konstruksjon, ikke ved avstemming etterpå. toleransen på 5 % skal lukkes: ved konstruksjon, ikke ved avstemming etterpå.
**Én beslutning i mappingen er verdt å lære av:** `affected_items` er *hele porteføljens* **Én beslutning i mappingen er verdt å lære av:** `affected_items` er *hele porteføljens*
energikostnad, ikke de 2 500 berørte punktenes eget forbruk. Hadde det vært det siste, ville energikostnad, ikke de 2 500 berørte maskinenes eget forbruk. Hadde det vært det siste, ville
besparelsen vært 38,6 % av linjen — over validatorens 30 %-tak — og det riktige forslaget blitt besparelsen vært 38,6 % av linjen — over validatorens 30 %-tak — og det riktige forslaget blitt
avvist av en gate som målte feil størrelse (VERIFISERT: `tiltak-led-utskifting.md`, «Mapping til avvist av en gate som målte feil størrelse (VERIFISERT: `tiltak-pc-utskifting.md`, «Mapping til
validatoren»). Kostlinjen skal være den linjen tiltaket *virker på* i regnskapet. validatoren»). Kostlinjen skal være den linjen tiltaket *virker på* i regnskapet.
### 5.6 Frø-dommen ### 5.6 Frø-dommen
@ -449,14 +458,15 @@ decision: approved_with_adjustment
realization_rate: 0.81 realization_rate: 0.81
modelled_saving_nok: 445500 modelled_saving_nok: 445500
expected_actual_saving_nok: 360855 expected_actual_saving_nok: 360855
description: "… brenntimene er et nasjonalt tabellanslag, ikke en målt kurve, og anlegget description: "… driftstimene er et internt tabellanslag, ikke en målt kurve, og klientparken
mangler måler — så avviket kan ikke oppdages i drift. Forventet faktisk besparelse settes til mangler måler — så avviket kan ikke oppdages i drift. Forventet faktisk besparelse settes til
81 % av modellert, lånt fra belysnings-programlitteratur og merket som lån. …" 81 % av modellert, lånt fra virksomhetens etterevalueringer av belysningsprogrammer og merket
provenance: "frø — AI-forfattet. Realiseringsgraden er LÅNT … Det finnes INGEN norsk ex-post-måling som lån. …"
for veglys. Erstattes av ekte HITL i produksjon." provenance: "frø — AI-forfattet, fiktiv virksomhet. Realiseringsgraden er LÅNT … Det finnes INGEN
ex-post-måling for klientparken. Erstattes av ekte HITL i produksjon."
``` ```
(Utdrag; VERIFISERT: `verdict-veglys-fro.md`.) Det som når neste hypotese er `description` pluss (Utdrag; VERIFISERT: `verdict-klientpark-fro.md`.) Det som når neste hypotese er `description` pluss
`[realiseringsgrad=0.81; forventet_faktisk_NOK=360855]` (VERIFISERT: `verdicts.py` `[realiseringsgrad=0.81; forventet_faktisk_NOK=360855]` (VERIFISERT: `verdicts.py`
`_verdict_rationale`). `provenance`-feltet leses ikke av koden — men det er det som gjør at en `_verdict_rationale`). `provenance`-feltet leses ikke av koden — men det er det som gjør at en
fagperson som åpner basen ser at dommen er et frø og raten et lån. I et ekte prosjekt erstattes fagperson som åpner basen ser at dommen er et frø og raten et lån. I et ekte prosjekt erstattes
@ -469,25 +479,26 @@ Det finnes ingen egen «valider basen»-kommando
bestillingen på plass: bestillingen på plass:
```bash ```bash
uv run python -m portfolio_optimiser.run VEGLYS-FV-SOER \ uv run python -m portfolio_optimiser.run KLIENTPARK-ENERGI \
--docs-dir shared/examples/veglys-fv-soer \ --docs-dir src/portfolio_optimiser/data/bundles/klientpark-energi \
--bundle-dir shared/examples/veglys-fv-soer \ --bundle-dir src/portfolio_optimiser/data/bundles/klientpark-energi \
--mandate oppdrag.json \ --mandate oppdrag.json \
--live-dry-run --live-dry-run
``` ```
Målt 2026-08-21 (VERIFISERT, rc 0): Målt 2026-08-21 på den forrige eksempelbasen (VERIFISERT, rc 0; navnene byttet til eksempelets,
se notisen øverst i §5):
``` ```
Run mandate for VEGLYS-FV-SOER Run mandate for KLIENTPARK-ENERGI
Objective: Redusere energikostnaden i veglysporteføljen Fylkesveg Sør uten å gå under lystekniske minstekrav, med tiltak som kan bestilles i 2027. Objective: Redusere energikostnaden i klientparken til Eksempelvirksomheten uten å gå under ytelseskravene, med tiltak som kan bestilles i 2027.
Evaluates: 2 expert-proposed approach(es) + the system's own proposals Evaluates: 2 expert-proposed approach(es) + the system's own proposals
1. led-trinn-1 — LED-utskifting av de 2 500 eldste HPS-punktene 1. pc-trinn-1 — Utskifting av de 2 500 eldste stasjonære PC-ene
2. adaptiv-styring — Adaptiv styring på de LED-utskiftede punktene 2. adaptiv-stromstyring — Adaptiv strømstyring på de utskiftede maskinene
Stops at: 3 rounds / 100000 tokens Stops at: 3 rounds / 100000 tokens
Contacts: no external services Contacts: no external services
Success: Minst ett tiltak som passerer validatoren og som driftsavdelingen kan stå inne for. Success: Minst ett tiltak som passerer validatoren og som IT-driftsavdelingen kan stå inne for.
VEGLYS-FV-SOER: LIVE-DRY-RUN OK (profile=local, models={'proposer': 'qwen3:4b', 'checker': 'qwen3:4b'}, max_rounds=3, max_tokens=100000, top_k=3) — ingen modellkall gjort (stoppet før første debate.run) KLIENTPARK-ENERGI: LIVE-DRY-RUN OK (profile=local, models={'proposer': 'qwen3:4b', 'checker': 'qwen3:4b'}, max_rounds=3, max_tokens=100000, top_k=3) — ingen modellkall gjort (stoppet før første debate.run)
``` ```
**Hva `OK` beviser:** basen åpner — `index.md` finnes, `validator-input.json` finnes og bærer **Hva `OK` beviser:** basen åpner — `index.md` finnes, `validator-input.json` finnes og bærer
@ -519,8 +530,8 @@ unåbar, og da finnes det ingen lenke å rapportere), eller at innholdet er godt
mappe. mappe.
**Det offline ende-til-ende-beviset på denne basen** er demoen: **Det offline ende-til-ende-beviset på denne basen** er demoen:
`uv run python -m portfolio_optimiser.simulation`. Den kjører nøyaktig `veglys-fv-soer`, skriver `uv run python -m portfolio_optimiser.simulation`. Den kjører nøyaktig `klientpark-energi`, skriver
«KUNNSKAPSBASE: veglys-fv-soer — kostbaseline erklært (ENERGI-VEGLYS-EL 4386150 x 1)», navigerer «KUNNSKAPSBASE: klientpark-energi — kostbaseline erklært (ENERGI-KLIENTPARK-EL 4386150 x 1)», navigerer
de fem konseptfilene, og viser at en dom avgitt etter kjøring A når kjøring B (VERIFISERT: de fem konseptfilene, og viser at en dom avgitt etter kjøring A når kjøring B (VERIFISERT:
`tests/golden/demo-transcript.stdout`, linjene 7, 13 og 50). Agent-svarene i demoen er `tests/golden/demo-transcript.stdout`, linjene 7, 13 og 50). Agent-svarene i demoen er
skriptede; den beviser dataflyten, ikke modellens dømmekraft. skriptede; den beviser dataflyten, ikke modellens dømmekraft.
@ -562,14 +573,14 @@ så dokumentet ikke lover mer enn det som kan leveres.
- **Uforankret kjøring er synlig, men ikke summert.** Feltet og linja finnes per kjøring - **Uforankret kjøring er synlig, men ikke summert.** Feltet og linja finnes per kjøring
([§4.1](#41-den-skarpeste-mangelen-cost-baselinejson)); det finnes ingen rapport som teller opp ([§4.1](#41-den-skarpeste-mangelen-cost-baselinejson)); det finnes ingen rapport som teller opp
hvor mange kjøringer i et porteføljepass som gikk uforankret. hvor mange kjøringer i et porteføljepass som gikk uforankret.
- **Eksempelbasene er ikke ekte prosjekter.** Prosjektlaget er fiktivt; realiseringsgraden i alle - **Eksempelbasene er ikke ekte prosjekter.** Prosjektlaget er fiktivt; realiseringsgraden i
tre frø-dommene er lånt fra utenlandsk programlitteratur fordi ingen norsk ex-post-måling frø-dommene er lånt fra andre program fordi ingen ex-post-måling av anlegget selv finnes
finnes (VERIFISERT: `provenance`-feltet i de tre dom-filene). (VERIFISERT: `provenance`-feltet i dom-filene).
- **De fire innholdstypene er konvensjon**, ikke spesifikasjon - **De fire innholdstypene er konvensjon**, ikke spesifikasjon
([§4](#4-innholdstypene)). ([§4](#4-innholdstypene)).
- **`docs/extending.md` er utdatert på ett punkt:** den sier at ingen eksempelbase shipper - **`docs/extending.md` er utdatert på ett punkt:** den sier at ingen eksempelbase shipper
`cost-baseline.json`. Det var sant da den ble skrevet (2026-08-05); begge veiprosjekt-basene har `cost-baseline.json`. Det var sant da den ble skrevet (2026-08-05); begge de leverte
fått fila siden (VERIFISERT: `ls`, begge datert 2026-08-09). Rettelsen er ikke gjort her, for eksempelbasene har fått fila siden (VERIFISERT: `ls`, begge datert 2026-08-09). Rettelsen er ikke gjort her, for
den hører hjemme i det dokumentet. den hører hjemme i det dokumentet.
- **1–2 uker.** Oppskriften sier det, og ingenting i dette dokumentet korter det ned. Det som - **1–2 uker.** Oppskriften sier det, og ingenting i dette dokumentet korter det ned. Det som
står her er hva ukene skal brukes til. står her er hva ukene skal brukes til.
@ -606,18 +617,18 @@ ikke fyrer der.
| Prosjektnavn leses fra `type: project`-filas `title`, ellers ID | samme | VERIFISERT | | Prosjektnavn leses fra `type: project`-filas `title`, ellers ID | samme | VERIFISERT |
| Steg 0 avviser ukjent kode og avvik > 5 % | `validator.py:154-190`; `BASELINE_TOLERANCE_DEFAULT = 0.05` | VERIFISERT | | Steg 0 avviser ukjent kode og avvik > 5 % | `validator.py:154-190`; `BASELINE_TOLERANCE_DEFAULT = 0.05` | VERIFISERT |
| Manglende `cost-baseline.json` → `None` → steg 0 hoppes over | `okf.py:323-335`; `run.py:516`; `validator.py:213` | VERIFISERT | | Manglende `cost-baseline.json` → `None` → steg 0 hoppes over | `okf.py:323-335`; `run.py:516`; `validator.py:213` | VERIFISERT |
| Fire tørrkjøringer: intakt rc 0 · uten IR rc 1 · uten baseline rc 0 uten melding · korrupt baseline rc 1 | kjørt 2026-08-21 på kopier i scratchpad, kommandoen i §5.7 | VERIFISERT | | Fire tørrkjøringer: intakt rc 0 · uten IR rc 1 · uten baseline rc 0 uten melding · korrupt baseline rc 1 | kjørt 2026-08-21 på kopier av den forrige eksempelbasen i scratchpad, kommandoen i §5.7 | VERIFISERT |
| Ingen artefakt bærer forankret/uforankret | `grep baseline src/portfolio_optimiser/provenance.py src/portfolio_optimiser/outbox.py` — null treff | VERIFISERT | | Ingen artefakt bærer forankret/uforankret | `grep baseline src/portfolio_optimiser/provenance.py src/portfolio_optimiser/outbox.py` — null treff | VERIFISERT |
| Demoen printer forankringsstatus | `simulation.py:791-802`; golden linje 7 | VERIFISERT | | Demoen printer forankringsstatus | `simulation.py:791-802`; golden linje 7 | VERIFISERT |
| `--live-dry-run` navigerer basen og laster begge tallfiler før kuttet | `run.py:513-516` vs `:565-588` | VERIFISERT | | `--live-dry-run` navigerer basen og laster begge tallfiler før kuttet | `run.py:513-516` vs `:565-588` | VERIFISERT |
| `--docs-dir` påkrevd men ulest på bundle-stien | `run.py:513-530`, `:1504` | VERIFISERT | | `--docs-dir` påkrevd men ulest på bundle-stien | `run.py:513-530`, `:1504` | VERIFISERT |
| Demoen kjører `veglys-fv-soer`, navigerer 5 konseptfiler, henter 0 så 3 dommer | `tests/golden/demo-transcript.stdout` linjene 7, 13, 14, 50 | VERIFISERT | | Demoen kjører `klientpark-energi`, navigerer 5 konseptfiler, henter 0 så 3 dommer | `tests/golden/demo-transcript.stdout` linjene 7, 13, 14, 50 | VERIFISERT |
| Frø-rationale = `description` + `[realiseringsgrad=…; forventet_faktisk_NOK=…]` | `verdicts.py` `_verdict_rationale` | VERIFISERT | | Frø-rationale = `description` + `[realiseringsgrad=…; forventet_faktisk_NOK=…]` | `verdicts.py` `_verdict_rationale` | VERIFISERT |
| Dom-nøkkel-trioen: alle tre felt eller ingen | `verdicts.py` `_features_from_verdict_frontmatter`; `README.md` | VERIFISERT | | Dom-nøkkel-trioen: alle tre felt eller ingen | `verdicts.py` `_features_from_verdict_frontmatter`; `README.md` | VERIFISERT |
| Veglys-tallene: 9 500 × 114 W × 4 050 t = 4 386 150 kWh; 445 500 NOK modellert; 10,2 % av total; 38,6 % av berørte punkter | `veglys-fv-soer.md`, `tiltak-led-utskifting.md`, begge JSON-filer | VERIFISERT | | Klientpark-tallene: 9 500 × 114 W × 4 050 t = 4 386 150 kWh; 445 500 NOK modellert; 10,2 % av total; 38,6 % av berørte maskiner | `klientpark-energi.md`, `tiltak-pc-utskifting.md`, begge JSON-filer | VERIFISERT |
| Frø-dommen: rate 0,81, forventet 360 855, lånt | `verdict-veglys-fro.md` frontmatter | VERIFISERT | | Frø-dommen: rate 0,81, forventet 360 855, lånt | `verdict-klientpark-fro.md` frontmatter | VERIFISERT |
| Fabrikk og fri-format-oversettelse ikke bygget | `docs/knowledge-base-recipe.md`; `docs/plan/2026-07-14-revisjonspakke-DF-DI.md` §3 | VERIFISERT | | Fabrikk og fri-format-oversettelse ikke bygget | `docs/knowledge-base-recipe.md`; `docs/plan/2026-07-14-revisjonspakke-DF-DI.md` §3 | VERIFISERT |
| Ingest har ingen CLI; ingen base materialisert fra levende kilde | `grep __main__ src/portfolio_optimiser/ingest.py` — null treff; `README.md` «How it is set up» | VERIFISERT | | Ingest har ingen CLI; ingen base materialisert fra levende kilde | `grep __main__ src/portfolio_optimiser/ingest.py` — null treff; `README.md` «How it is set up» | VERIFISERT |
| Ingest skriver ikke `validator-input.json` / `cost-baseline.json` | `docs/extending.md`, «Legg til en ingest-kilde» | VERIFISERT | | Ingest skriver ikke `validator-input.json` / `cost-baseline.json` | `docs/extending.md`, «Legg til en ingest-kilde» | VERIFISERT |
| `docs/extending.md` sier ingen eksempelbase shipper `cost-baseline.json` | samme dokument; motbevist av `ls shared/examples/{veglys-fv-soer,tunnel-hauglia}/` | VERIFISERT (utdatert) | | `docs/extending.md` sier ingen eksempelbase shipper `cost-baseline.json` | samme dokument; motbevist av `ls src/portfolio_optimiser/data/bundles/{klientpark-energi,driftssenter-kjoling}/` | VERIFISERT (utdatert) |
| Leveranseformene i §5.3; anbefalingene merket ANTATT | — | ANTATT | | Leveranseformene i §5.3; anbefalingene merket ANTATT | — | ANTATT |

View file

@ -8,7 +8,7 @@
| D1 | **Python-først** | Stabil `agent-framework` 1.8.0; Functional API, `McpSkillsSource`, Neo4j-memory tilgjengelig. C# utsettes. | | D1 | **Python-først** | Stabil `agent-framework` 1.8.0; Functional API, `McpSkillsSource`, Neo4j-memory tilgjengelig. C# utsettes. |
| D2 | **To profiler: Azure/Foundry (full) + lokal (fallback)** | Full: Cosmos, AI Search agentic retrieval, Durable Task, Foundry-modeller. Lokal: fil-checkpointing, SQLite/Chroma, lokale embeddings. Samme kjerne-API, pluggbar backend. | | D2 | **To profiler: Azure/Foundry (full) + lokal (fallback)** | Full: Cosmos, AI Search agentic retrieval, Durable Task, Foundry-modeller. Lokal: fil-checkpointing, SQLite/Chroma, lokale embeddings. Samme kjerne-API, pluggbar backend. |
| D3 | **Rent teknisk rammeverk** | Deployer eier behandlingsformål/DPIA/ROS. Vi bygger IKKE compliance-funksjoner (anti-scope A-ny). Vi leverer tekniske forutsetninger (lokal-only, provenance, ingen stille egress) + tydelig disclaimer. | | D3 | **Rent teknisk rammeverk** | Deployer eier behandlingsformål/DPIA/ROS. Vi bygger IKKE compliance-funksjoner (anti-scope A-ny). Vi leverer tekniske forutsetninger (lokal-only, provenance, ingen stille egress) + tydelig disclaimer. |
| D4 | **Generisk kjerne + 1 syntetisk referanse-domene** | Referanse-domenet er nøytralt/syntetisk (ikke SVV-data) — beviser kjernen uten datasensitivitet, i tråd med D3. | | D4 | **Generisk kjerne + 1 syntetisk referanse-domene** | Referanse-domenet er nøytralt/syntetisk (ingen virkelige virksomhetsdata) — beviser kjernen uten datasensitivitet, i tråd med D3. |
| D5 | **90%-prinsipp** | Sluttproduktet funker aldri 100 % for noen. Bygg ~90 % generisk kjerne + tydelige extension points; jakt IKKE de siste 10 %. Kompetente folk konfigurerer siste mil per kunde. | | D5 | **90%-prinsipp** | Sluttproduktet funker aldri 100 % for noen. Bygg ~90 % generisk kjerne + tydelige extension points; jakt IKKE de siste 10 %. Kompetente folk konfigurerer siste mil per kunde. |
| D6 | **Kostnadsdisiplin** | Privat MS-tenant tilgjengelig (gjør Azure/Foundry-profilen testbar), MEN kostnadstak: utvikle primært på **lokal profil** (gratis); Foundry/Azure kun til målrettet, minimal verifisering; spikes på billigste modeller + bittesmå syntetiske data + harde token-/runde-tak. Ingen tunge test-kjøringer. | | D6 | **Kostnadsdisiplin** | Privat MS-tenant tilgjengelig (gjør Azure/Foundry-profilen testbar), MEN kostnadstak: utvikle primært på **lokal profil** (gratis); Foundry/Azure kun til målrettet, minimal verifisering; spikes på billigste modeller + bittesmå syntetiske data + harde token-/runde-tak. Ingen tunge test-kjøringer. |

View file

@ -406,7 +406,7 @@ hva den blokkerer/åpner (ikke hele fasiten).
- **Mål:** `affected_items` avstemmes fail-closed mot prosjektets faktiske kostbaseline — den - **Mål:** `affected_items` avstemmes fail-closed mot prosjektets faktiske kostbaseline — den
deterministiske gaten kan ikke lenger mates med hallusinerte kostlinjer. deterministiske gaten kan ikke lenger mates med hallusinerte kostlinjer.
- **Scope:** baseline-projeksjon i bundle (`cost-baseline.json`: code→{quantity, unit_cost} — - **Scope:** baseline-projeksjon i bundle (`cost-baseline.json`: code→{quantity, unit_cost} —
commons-amendment) + fra `reference_domain.cost_items` på road-stien; ny avstemmings-stage i commons-amendment) + fra `reference_domain.cost_items` på referanse-stien; ny avstemmings-stage i
`validate_proposal` (kode finnes ikke i baseline → Rejection; quantity/unit_cost utenfor `validate_proposal` (kode finnes ikke i baseline → Rejection; quantity/unit_cost utenfor
toleranse → Rejection; toleranse konfig); metode-registry: metode-caps keyes via toleranse → Rejection; toleranse konfig); metode-registry: metode-caps keyes via
dimensjon/konfig, ikke strengen `energy_efficiency` (F8). Baseline-argument er VALGFRITT i dimensjon/konfig, ikke strengen `energy_efficiency` (F8). Baseline-argument er VALGFRITT i

View file

@ -123,7 +123,7 @@ schema-validated fail-fast at startup").
### 13.3 The dimension catalogue (startup contract) ### 13.3 The dimension catalogue (startup contract)
A **dimension** is one cost axis a project is reduced along (energy, paving, ...). A conforming A **dimension** is one cost axis a project is reduced along (energy, licences, ...). A conforming
implementation MUST define a **dimension catalogue**: configuration, schema-validated fail-fast at implementation MUST define a **dimension catalogue**: configuration, schema-validated fail-fast at
startup (§10), listing the dimensions the run recognises. Each entry: startup (§10), listing the dimensions the run recognises. Each entry:

View file

@ -193,8 +193,8 @@ pre-pull-manifest → 15/15 OK. Full suite etter pull: 785 passed / 4 skipped
Ingen reset, ingen NO-GO. Pullen var ren tilføyelse pluss ÉN endret fil (`ingest-spec.md`, se Ingen reset, ingen NO-GO. Pullen var ren tilføyelse pluss ÉN endret fil (`ingest-spec.md`, se
åpent punkt under). åpent punkt under).
**(b) Retningen er snudd.** `_CANDIDATES` har nå en `VEGLYS-FV-SOER`-oppføring hvis kostlinjer er **(b) Retningen er snudd.** `_CANDIDATES` har nå en `KLIENTPARK-ENERGI`-oppføring hvis kostlinjer er
skrevet FRA `shared/examples/veglys-fv-soer/cost-baseline.json`; `baseline_from_scripted_candidate` skrevet FRA `src/portfolio_optimiser/data/bundles/klientpark-energi/cost-baseline.json`; `baseline_from_scripted_candidate`
brukes IKKE på denne stien (`main()` leser levert fil via `okf.load_optional_cost_baseline`). Det brukes IKKE på denne stien (`main()` leser levert fil via `okf.load_optional_cost_baseline`). Det
korrigerte svaret ER den leverte IR-projeksjonen ordrett — inkludert `assumptions`-bandet, så korrigerte svaret ER den leverte IR-projeksjonen ordrett — inkludert `assumptions`-bandet, så
Monte Carlo-en er ekte og ikke degenerert. Monte Carlo-en er ekte og ikke degenerert.
@ -300,13 +300,13 @@ maskeringen er selv under test: `test_normalisation_does_not_mask_a_new_warning`
EKSTRA linje gjennom samme normaliserer og krever at den slutter å matche. EKSTRA linje gjennom samme normaliserer og krever at den slutter å matche.
*Punkt 4 — den forhåndsskrevne frø-setningen var FEIL, og målingen fanget det.* Planen sa «én av de *Punkt 4 — den forhåndsskrevne frø-setningen var FEIL, og målingen fanget det.* Planen sa «én av de
to tidligere dommene i Kjøring B fulgte med eksempelet». Målt mot levert VEGLYS-bundle henter to tidligere dommene i Kjøring B fulgte med eksempelet». Målt mot levert energi-bundle henter
Kjøring B **tre**: den frøsatte (`verdict-veglys-fro.md`), Steg-8-promoteringen og Steg-7-innboksen — Kjøring B **tre**: den frøsatte (`verdict-klientpark-fro.md`), Steg-8-promoteringen og Steg-7-innboksen —
altså **én fulgte med, to er demoens egne, én per tidsskala**. Setningen ville vært en falsk påstand altså **én fulgte med, to er demoens egne, én per tidsskala**. Setningen ville vært en falsk påstand
sagt på scenen om et tall som står printet linja over. Splitten er derfor **avledet** sagt på scenen om et tall som står printet linja over. Splitten er derfor **avledet**
(`_verdict_origin_line`), ikke skrevet ned: en håndskrevet «én av tre» er den andre kopien som (`_verdict_origin_line`), ikke skrevet ned: en håndskrevet «én av tre» er den andre kopien som
drifter (samme regel som punkt 0), og ville blitt sagt uendret etter at en framtidig bundle shipper drifter (samme regel som punkt 0), og ville blitt sagt uendret etter at en framtidig bundle shipper
en andre frøsatt dom. GO-varianten av provenans-setningen sto allerede live som `_VEGLYS_PROVENANCE` en andre frøsatt dom. GO-varianten av provenans-setningen sto allerede live som `_KLIENTPARK_PROVENANCE`
siden P3; den var IKKE positivt asserted noe sted (anker-testen utelukker bare reservens setning), så siden P3; den var IKKE positivt asserted noe sted (anker-testen utelukker bare reservens setning), så
den er nå pinnet mot fasiten. den er nå pinnet mot fasiten.
@ -564,8 +564,8 @@ og risikoen ligger tidlig med slakk bak seg.
|---|---| |---|---|
| fre 7. | **Planrevisjonen (I1–I6)** ✔ + **P1/S1.a**: Steg 7-innboksen inn i demoløpet (økt 1) | | fre 7. | **Planrevisjonen (I1–I6)** ✔ + **P1/S1.a**: Steg 7-innboksen inn i demoløpet (økt 1) |
| lør 8.–søn 9. | **P2/S1.b**: innholdsgaten (økt 2, evt. 3) + **P4-forskuddet UTVIDET** (0,75–1,25 økt), i denne rekkefølgen: forankret prøvekjøring + 10 %-prøven (pkt. 0, I1) **✔ 08-09** → `[project.scripts]` (pkt. 5, I3) → stderr-demping FØR pinning (pkt. 2, I2) → fresh-clone (pkt. 1) → golden-transkript-mekanikk (pkt. 3) → de to ærlighets-setningene (pkt. 4) — alt bygges og måles mot den FORANKREDE mikro-reserven NÅ | | lør 8.–søn 9. | **P2/S1.b**: innholdsgaten (økt 2, evt. 3) + **P4-forskuddet UTVIDET** (0,75–1,25 økt), i denne rekkefølgen: forankret prøvekjøring + 10 %-prøven (pkt. 0, I1) **✔ 08-09** → `[project.scripts]` (pkt. 5, I3) → stderr-demping FØR pinning (pkt. 2, I2) → fresh-clone (pkt. 1) → golden-transkript-mekanikk (pkt. 3) → de to ærlighets-setningene (pkt. 4) — alt bygges og måles mot den FORANKREDE mikro-reserven NÅ |
| man 10. | **SYV ØKTER, alle ✔.** (1) **Generalprøve nr. 0** — kjørt mot **LEVERT VEGLYS-FV-SOER, ikke reserven**: P3 falt to døgn før fristen, så prøven målte demoinnholdet selv. Se UTFØRT-blokka under tabellen. (2) **S1.c synk + CHANGELOG** (`a41272d`) — forskuttert fra ons kveld; TAGGEN gjenstår, frys-gatet. (3) **`open/`-beslutningen TATT** — taggen til `origin` ALENE; speilet til P5-vinduet (se S1.c-raden i §0). (4) **Planen gjort sann** (`c9787cf`) — to `open/`-instrukser felt, P2 lukket, kalenderen rettet. (5) **Frysedagens to udefinerte steg lukket** — **P4 ✔** (fersk klon re-målt på `c9787cf`, elleve commits etter forrige måling) og **FRYS gjort kjørbar** (frys-blokka under). (6) **Persona-pullen landet** (`71b7b66`+`d0e8bb0`) — commons svarte to døgn før fristen, så tirsdagens eneste punkt ble tatt mandag; fasiten re-målt mot en prediksjon skrevet FØR pullen. **Ingenting kodemessig gjenstår før onsdag, og tirsdagen er tom.** (7) **P4.5-runbooken SKREVET** (`c7a57d8`) — beslutnings-innholdet forskuttert fra onsdag (gaten dekker målingene, ikke forfatterskapet), inkludert **mandat-setningen som aldri fantes som tekst**; onsdag fyller ~~tre~~ **to** målte felt (tag-feltet felt tir 11. økt 12 — det var sirkulært). Vedlegget felte tre «scene-kosmetiske» STATE-premisser: ingen av dem er synlige på skjermen (målt). **Frysen ble VURDERT flyttet fram og bevisst IKKE flyttet** — onsdag er en dato-beslutning på en enveis-handling, og risikoen den ville hedget er retirert av økt 6 (samme kjøresti målt grønn; `d0e8bb0..HEAD` er dokumenter alene). | | man 10. | **SYV ØKTER, alle ✔.** (1) **Generalprøve nr. 0** — kjørt mot **LEVERT energi-bundle, ikke reserven**: P3 falt to døgn før fristen, så prøven målte demoinnholdet selv. Se UTFØRT-blokka under tabellen. (2) **S1.c synk + CHANGELOG** (`a41272d`) — forskuttert fra ons kveld; TAGGEN gjenstår, frys-gatet. (3) **`open/`-beslutningen TATT** — taggen til `origin` ALENE; speilet til P5-vinduet (se S1.c-raden i §0). (4) **Planen gjort sann** (`c9787cf`) — to `open/`-instrukser felt, P2 lukket, kalenderen rettet. (5) **Frysedagens to udefinerte steg lukket** — **P4 ✔** (fersk klon re-målt på `c9787cf`, elleve commits etter forrige måling) og **FRYS gjort kjørbar** (frys-blokka under). (6) **Persona-pullen landet** (`71b7b66`+`d0e8bb0`) — commons svarte to døgn før fristen, så tirsdagens eneste punkt ble tatt mandag; fasiten re-målt mot en prediksjon skrevet FØR pullen. **Ingenting kodemessig gjenstår før onsdag, og tirsdagen er tom.** (7) **P4.5-runbooken SKREVET** (`c7a57d8`) — beslutnings-innholdet forskuttert fra onsdag (gaten dekker målingene, ikke forfatterskapet), inkludert **mandat-setningen som aldri fantes som tekst**; onsdag fyller ~~tre~~ **to** målte felt (tag-feltet felt tir 11. økt 12 — det var sirkulært). Vedlegget felte tre «scene-kosmetiske» STATE-premisser: ingen av dem er synlige på skjermen (målt). **Frysen ble VURDERT flyttet fram og bevisst IKKE flyttet** — onsdag er en dato-beslutning på en enveis-handling, og risikoen den ville hedget er retirert av økt 6 (samme kjøresti målt grønn; `d0e8bb0..HEAD` er dokumenter alene). |
| tir 11. | **IKKE TOM — SEKS ØKTER (8, 9, 10, 11, 12, 13), alle måling/dokument, null kodeendring.** Frysen ble VURDERT flyttet fram og bevisst IKKE flyttet: **et frysevindu er en forpliktelse, ikke slakk** — å tagge tirsdag ville forbudt kjørestien et døgn lenger og gjort ethvert onsdagsfunn til en `v1.0.1`-beslutning på demo-aften. Tirsdagen er verdt mer som **lovlig-fiks-dag**. (8) generalprøve som PRØVE, alt grønt på `818b55a`; X bevisst IKKE notert; pre-flight mot stale tag ren. (9) runbookens §2 målt mot fasiten — 67 påstander + hver kommando kjørt, null feil; §1s stderr felte én defekt (`/tmp/po-sim-…` kan aldri vises, `TMPDIR` = `/var/folders/…/T/`). (10) **runbookens §6 — torsdagens pre-flight — tilføyd** (se P4.5). (11) **to en-linjes herdinger av onsdagen/torsdagen:** §5 manglet en **arbeidstre-sjekk ved X** (frys-gaten er commit-til-commit, prøven leser treet — en ucommittet `src/`-endring ville gjort X til en beskrivelse av noe som aldri ble prøvd, usynlig for BEGGE gate-kjøringene), og **§3s abortsti brukte relativ sti** til fasit-fila, altså feilet av nøyaktig den «feil katalog»-årsaken prosaen selv navngir (målt). Frysen er nå **tre** kommandoer. (12) **§0s tag-felt var SIRKULÆRT og §5s haker var tvillingen** — feltet kunne først fylles etter §5s siste punkt, mens punkt 6 krever at gaten er tom; hakene settes i fila, og punkt 7–10 skjer etter runbook-commiten `Y`. Begge utveier gjorde en av **torsdagens** to gater rød (ucommittet → §6 steg 1; commit etter taggen → §6 steg 2s identitet). Tag-raden fjernet, ellevte §5-punkt bekrefter taggen der den settes, hakene forlot fila. Se P4.5-amendementet. (13) **§5s punkt 5 var en gate som ikke kunne feile** — `<X>` **er** HEAD der, så `<X>..HEAD` er tom per konstruksjon (målt med en endret `src/`-fil i treet: fortsatt tom). Den målte altså ingen tilstand, mens økt 7s begrunnelse sa «en tilstand som ikke lenger finnes». Punktet beholdt (fjerning ville renummerert elleve punkter og brutt fem kryssreferanser på frys-eve), men gitt en **andre arm** mot fast hash `c255662` → ikke tomt (6 filer), som beviser at kommandoen kan diskriminere før punkt 9 hviler på at den er tom — uten den ville en ødelagt pathspec gitt grønt på feil grunnlag. Punkt 9s tilbakereferanse og §5s ingress rettet i samme pass. Se frys-blokkas økt-13-amendement. *(Raden sa «TOM — GÅ RETT PÅ ONSDAG»; det var sant da den ble skrevet man 10. og sluttet å være det samme uke. Samme drift-klasse som radene økt 4, 5 og 6 hver for seg fant.)* *(Amendert man 10. økt 6: tirsdagens ENESTE åpne punkt var commons-svaret på persona-formuleringen — «i kontorbygg» på et veglys-prosjekt, printet ordrett i Steg 7. Svaret kom **to døgn før fristen** og ble tatt samme dag: subtree pull av commons `73136eb` → «i tilsvarende anlegg», `marker` byte-uendret (`71b7b66`), fasiten re-målt (`d0e8bb0`). Abortstien I4 ble aldri utløst — men den ble verifisert KJØRBAR først: `git reset --hard` blokkeres av hooken, **`--keep` slipper**. Rød-settet etter pullen var NØYAKTIG én test, det pinnede transkriptet; goldenene (kriterium 8) uendret; `pyproject.toml`/`uv.lock` urørt. Denne raden instruerte om arbeid som var utført — samme drift-klasse som økt 4 felte.)* | | tir 11. | **IKKE TOM — SEKS ØKTER (8, 9, 10, 11, 12, 13), alle måling/dokument, null kodeendring.** Frysen ble VURDERT flyttet fram og bevisst IKKE flyttet: **et frysevindu er en forpliktelse, ikke slakk** — å tagge tirsdag ville forbudt kjørestien et døgn lenger og gjort ethvert onsdagsfunn til en `v1.0.1`-beslutning på demo-aften. Tirsdagen er verdt mer som **lovlig-fiks-dag**. (8) generalprøve som PRØVE, alt grønt på `818b55a`; X bevisst IKKE notert; pre-flight mot stale tag ren. (9) runbookens §2 målt mot fasiten — 67 påstander + hver kommando kjørt, null feil; §1s stderr felte én defekt (`/tmp/po-sim-…` kan aldri vises, `TMPDIR` = `/var/folders/…/T/`). (10) **runbookens §6 — torsdagens pre-flight — tilføyd** (se P4.5). (11) **to en-linjes herdinger av onsdagen/torsdagen:** §5 manglet en **arbeidstre-sjekk ved X** (frys-gaten er commit-til-commit, prøven leser treet — en ucommittet `src/`-endring ville gjort X til en beskrivelse av noe som aldri ble prøvd, usynlig for BEGGE gate-kjøringene), og **§3s abortsti brukte relativ sti** til fasit-fila, altså feilet av nøyaktig den «feil katalog»-årsaken prosaen selv navngir (målt). Frysen er nå **tre** kommandoer. (12) **§0s tag-felt var SIRKULÆRT og §5s haker var tvillingen** — feltet kunne først fylles etter §5s siste punkt, mens punkt 6 krever at gaten er tom; hakene settes i fila, og punkt 7–10 skjer etter runbook-commiten `Y`. Begge utveier gjorde en av **torsdagens** to gater rød (ucommittet → §6 steg 1; commit etter taggen → §6 steg 2s identitet). Tag-raden fjernet, ellevte §5-punkt bekrefter taggen der den settes, hakene forlot fila. Se P4.5-amendementet. (13) **§5s punkt 5 var en gate som ikke kunne feile** — `<X>` **er** HEAD der, så `<X>..HEAD` er tom per konstruksjon (målt med en endret `src/`-fil i treet: fortsatt tom). Den målte altså ingen tilstand, mens økt 7s begrunnelse sa «en tilstand som ikke lenger finnes». Punktet beholdt (fjerning ville renummerert elleve punkter og brutt fem kryssreferanser på frys-eve), men gitt en **andre arm** mot fast hash `c255662` → ikke tomt (6 filer), som beviser at kommandoen kan diskriminere før punkt 9 hviler på at den er tom — uten den ville en ødelagt pathspec gitt grønt på feil grunnlag. Punkt 9s tilbakereferanse og §5s ingress rettet i samme pass. Se frys-blokkas økt-13-amendement. *(Raden sa «TOM — GÅ RETT PÅ ONSDAG»; det var sant da den ble skrevet man 10. og sluttet å være det samme uke. Samme drift-klasse som radene økt 4, 5 og 6 hver for seg fant.)* *(Amendert man 10. økt 6: tirsdagens ENESTE åpne punkt var commons-svaret på persona-formuleringen — «i kontorbygg» på et prosjekt som ikke var et kontorbygg, printet ordrett i Steg 7. Svaret kom **to døgn før fristen** og ble tatt samme dag: subtree pull av commons `73136eb` → «i tilsvarende anlegg», `marker` byte-uendret (`71b7b66`), fasiten re-målt (`d0e8bb0`). Abortstien I4 ble aldri utløst — men den ble verifisert KJØRBAR først: `git reset --hard` blokkeres av hooken, **`--keep` slipper**. Rød-settet etter pullen var NØYAKTIG én test, det pinnede transkriptet; goldenene (kriterium 8) uendret; `pyproject.toml`/`uv.lock` urørt. Denne raden instruerte om arbeid som var utført — samme drift-klasse som økt 4 felte.)* |
| ons 12. | **FRYSESEKVENSEN, i denne rekkefølgen. P4 ER LUKKET (man 10., økt 5 — fersk-klon-målingen var det eneste som gjensto, og den er kjørt på `c9787cf`), så sekvensen starter på prøven, ikke på en udefinert re-måling:** **generalprøve ×2** (bruk **distinkt**-tellingen `grep -oE "^ *Steg [1-8]" \| tr -d ' ' \| sort -u \| wc -l` → **8**; linje-tellingen gir 9, se avviket under tabellen) → **FRYS** → **P4.5: FYLL UT demo-runbooken** (`docs/plan/2026-08-12-demo-runbook.md` — den er SKREVET man 10. økt 7, `c7a57d8`; onsdag setter inn **to** målte felt (X + prøve-tidspunkt) og følger §5s **elleve** punkter i terminalen — **hakene settes ALDRI i fila**, og taggen bekreftes i punkt 11, ikke som et felt i §0; begge deler ville krevd en skriving etter taggen og gjort torsdagens identitets-anker rødt (tir 11. økt 12). Utfyllings-gaten: `grep -n '<<[A-ZÆØÅ-]*>>' docs/plan/2026-08-12-demo-runbook.md` → TOMT) → **S1.c-TAGGEN sist** (CHANGELOG-stempel + `git tag -a v1.0.0 -m "<ordrett fra runbookens §5 punkt 10>"` — **ANNOTERT**, som `v0.1.0`; `-m` er PÅKREVD (uten den: exit 128, ingen tag), og meldingsteksten står literalt i §5 punkt 10 og skal ikke skrives på nytt her — + push til **`origin` ALENE** — synk + CHANGELOG-innhold er alt gjort man 10.; `[project.scripts]` lå i helgen, I3). **Punkt 11s bekreftelse tåler nå en rate-limitet `origin`** (tir 11. økt 14, målt): tom utskrift er tvetydig, og exit-koden er det som skiller «taggen mangler» (0) fra «kom ikke fram» (128). **Alle beslutninger er tatt på forhånd — onsdag skal MÅLE og UTFØRE, ikke avgjøre.** **FRYSEN ER TRE PUNKTER, ikke en holdning — se frys-blokka under tabellen** (rent tre → noter X → gaten; arbeidstre-sjekken tilføyd tir 11. økt 11, fordi gaten er commit-til-commit og prøven leser treet). Punkt 3 er **to armer** siden økt 13 — kjøringen mot `<X>` er tom per konstruksjon og beviser ingenting alene; diskriminerings-armen mot `c255662` er den som gjør den siste gate-kjøringen meningsfull. | | ons 12. | **FRYSESEKVENSEN, i denne rekkefølgen. P4 ER LUKKET (man 10., økt 5 — fersk-klon-målingen var det eneste som gjensto, og den er kjørt på `c9787cf`), så sekvensen starter på prøven, ikke på en udefinert re-måling:** **generalprøve ×2** (bruk **distinkt**-tellingen `grep -oE "^ *Steg [1-8]" \| tr -d ' ' \| sort -u \| wc -l` → **8**; linje-tellingen gir 9, se avviket under tabellen) → **FRYS** → **P4.5: FYLL UT demo-runbooken** (`docs/plan/2026-08-12-demo-runbook.md` — den er SKREVET man 10. økt 7, `c7a57d8`; onsdag setter inn **to** målte felt (X + prøve-tidspunkt) og følger §5s **elleve** punkter i terminalen — **hakene settes ALDRI i fila**, og taggen bekreftes i punkt 11, ikke som et felt i §0; begge deler ville krevd en skriving etter taggen og gjort torsdagens identitets-anker rødt (tir 11. økt 12). Utfyllings-gaten: `grep -n '<<[A-ZÆØÅ-]*>>' docs/plan/2026-08-12-demo-runbook.md` → TOMT) → **S1.c-TAGGEN sist** (CHANGELOG-stempel + `git tag -a v1.0.0 -m "<ordrett fra runbookens §5 punkt 10>"` — **ANNOTERT**, som `v0.1.0`; `-m` er PÅKREVD (uten den: exit 128, ingen tag), og meldingsteksten står literalt i §5 punkt 10 og skal ikke skrives på nytt her — + push til **`origin` ALENE** — synk + CHANGELOG-innhold er alt gjort man 10.; `[project.scripts]` lå i helgen, I3). **Punkt 11s bekreftelse tåler nå en rate-limitet `origin`** (tir 11. økt 14, målt): tom utskrift er tvetydig, og exit-koden er det som skiller «taggen mangler» (0) fra «kom ikke fram» (128). **Alle beslutninger er tatt på forhånd — onsdag skal MÅLE og UTFØRE, ikke avgjøre.** **FRYSEN ER TRE PUNKTER, ikke en holdning — se frys-blokka under tabellen** (rent tre → noter X → gaten; arbeidstre-sjekken tilføyd tir 11. økt 11, fordi gaten er commit-til-commit og prøven leser treet). Punkt 3 er **to armer** siden økt 13 — kjøringen mot `<X>` er tom per konstruksjon og beviser ingenting alene; diskriminerings-armen mot `c255662` er den som gjør den siste gate-kjøringen meningsfull. |
| tor 13. | **DEMO = v1 vises** (runbooken i hånda). **FØRST runbookens §6 — pre-flighten, før noen er i rommet:** rent tre (`git status --short --untracked-files=no` → TOMT) · tag-ankeret `git describe --tags --exact-match HEAD` → **`v1.0.0`** (IDENTITET, ikke frys-gatens diff-med-unntak — de unntakene er onsdagens, og arvet hit ville de vært fail-open mot den parallelle `docs/`-sesjonen) · golden-diffen TOM. Feiler ankeret: les `git diff --stat v1.0.0..HEAD` UTEN unntak — kjøresti-filer = §3, kun `docs/` = upåvirket men vitende. Golden-diff ikke tom = §3, vis fasit-fila; aldri debugging på scenen. *(Tilføyd tir 11. — goldenen ble bygget for å fange regresjon onsdag→torsdag, men kommandoen sto som et show-element UNDER demoen; kjørt der oppdager den regresjonen samtidig med publikum.)* | | tor 13. | **DEMO = v1 vises** (runbooken i hånda). **FØRST runbookens §6 — pre-flighten, før noen er i rommet:** rent tre (`git status --short --untracked-files=no` → TOMT) · tag-ankeret `git describe --tags --exact-match HEAD` → **`v1.0.0`** (IDENTITET, ikke frys-gatens diff-med-unntak — de unntakene er onsdagens, og arvet hit ville de vært fail-open mot den parallelle `docs/`-sesjonen) · golden-diffen TOM. Feiler ankeret: les `git diff --stat v1.0.0..HEAD` UTEN unntak — kjøresti-filer = §3, kun `docs/` = upåvirket men vitende. Golden-diff ikke tom = §3, vis fasit-fila; aldri debugging på scenen. *(Tilføyd tir 11. — goldenen ble bygget for å fange regresjon onsdag→torsdag, men kommandoen sto som et show-element UNDER demoen; kjørt der oppdager den regresjonen samtidig med publikum.)* |
| fre 14.–lør 15. | **P5**: README (O4) | | fre 14.–lør 15. | **P5**: README (O4) |
@ -653,7 +653,7 @@ BEGGE former, med samme svar; det er kun README/CLAUDE-vinduet over som skiller
**UTFØRT — GENERALPRØVE NR. 0 (2026-08-10). BESTÅTT, ingen kodeendring.** Ingen fil i repoet ble **UTFØRT — GENERALPRØVE NR. 0 (2026-08-10). BESTÅTT, ingen kodeendring.** Ingen fil i repoet ble
rørt av prøven; den er ren måling. Planen forutsatte at prøven kjørte mot den forankrede reserven — rørt av prøven; den er ren måling. Planen forutsatte at prøven kjørte mot den forankrede reserven —
den kjørte mot **levert VEGLYS-FV-SOER**, fordi P3 falt 08-09. Det er en STRENGERE prøve enn den kjørte mot **levert energi-bundle**, fordi P3 falt 08-09. Det er en STRENGERE prøve enn
planlagt (reserven validerer mekanikk, aldri presentasjon — P3s lærdom 2), og reserve-stien er planlagt (reserven validerer mekanikk, aldri presentasjon — P3s lærdom 2), og reserve-stien er
fortsatt målt: den lever i suiten som `test_anchored_reserve_loadbearing.py`, ikke som demoens fortsatt målt: den lever i suiten som `test_anchored_reserve_loadbearing.py`, ikke som demoens
call-site-valg. call-site-valg.

View file

@ -113,7 +113,7 @@ CHANGELOG-ens Security-oppføring. Si det hvis det spørres; ikke som en fotnote
### Kunnskapsbasen (skjermlinje 7–9) ### Kunnskapsbasen (skjermlinje 7–9)
Linje 9 **er** ærlighets-setningen om provenans — den står allerede på skjermen og skal ikke sies Linje 9 **er** ærlighets-setningen om provenans — den står allerede på skjermen og skal ikke sies
på nytt: *«tallene er levert i kunnskapsbasen — utledet av fagkilder (Håndbok V124, NMFV), ikke av på nytt: *«tallene er levert i kunnskapsbasen — utledet av dens egne (fiktive) kilder, ikke av
demo-manuset»*. Pek på den. Poenget er at kostbaselinen er **erklært**, og at validatorens stage 0 demo-manuset»*. Pek på den. Poenget er at kostbaselinen er **erklært**, og at validatorens stage 0
avstemmer forslagets kostlinjer mot den **før** løseren. avstemmer forslagets kostlinjer mot den **før** løseren.
@ -391,7 +391,7 @@ STATE bar tre «scene-kosmetiske» punkter. Alle tre er målt mot det pinnede tr
Målt: `grep -c "23700" tests/golden/demo-transcript.stdout` → **0**. STATEs formulering Målt: `grep -c "23700" tests/golden/demo-transcript.stdout` → **0**. STATEs formulering
(«printes fortsatt ordrett») var et premiss, ikke en måling. Det trengs altså **ingen** setning («printes fortsatt ordrett») var et premiss, ikke en måling. Det trengs altså **ingen** setning
på scenen — kun hvis noen åpner selve persona-fila. på scenen — kun hvis noen åpner selve persona-fila.
2. **`0.82` vs `0.79`.** `0.82` hører til bygg-eksempelets golden, ikke veglys-kjøringen. 2. **`0.82` vs `0.79`.** `0.82` hører til bygg-eksempelets golden, ikke energi-kjøringen.
Målt: `grep -c "0\.82" tests/golden/demo-transcript.stdout` → **0**. Målt: `grep -c "0\.82" tests/golden/demo-transcript.stdout` → **0**.
3. **`docs/ekspert-svar.md`** er en operatør-guide og leses ikke av demoen. 3. **`docs/ekspert-svar.md`** er en operatør-guide og leses ikke av demoen.

View file

@ -53,14 +53,15 @@
<!-- 1 --> <!-- 1 -->
<section class="slide on"> <section class="slide on">
<p class="kicker">Kostnadskutt i veg- og tunnelprosjekter</p> <p class="kicker">Kostnadskutt i IT-driftsprosjekter</p>
<h1>Hva løsningen trenger fra deg</h1> <h1>Hva løsningen trenger fra deg</h1>
<p class="lead">Du er fagpersonen. Løsningen finner ingen besparelser uten det du vet — <p class="lead">Du er fagpersonen. Løsningen finner ingen besparelser uten det du vet —
og den kan ikke gjette seg til det.</p> og den kan ikke gjette seg til det.</p>
<p>Denne presentasjonen er de <b>sju spørsmålene</b> du blir stilt før en kjøring bestilles, <p>Denne presentasjonen er de <b>sju spørsmålene</b> du blir stilt før en kjøring bestilles,
hva du skal svare, og hva du må skaffe på forhånd. Eksemplene er hentet fra hva du skal svare, og hva du må skaffe på forhånd. Eksemplene er hentet fra
<b>utbedringsprosjekter på veg</b> og <b>tunnelprosjekter</b>.</p> <b>kontor-IT-prosjekter</b> og <b>driftssenterprosjekter</b> i en oppdiktet virksomhet,
Eksempelvirksomheten.</p>
<table> <table>
<tr><th>Spørsmål</th><th>Du leverer</th><th>Tid</th></tr> <tr><th>Spørsmål</th><th>Du leverer</th><th>Tid</th></tr>
@ -163,18 +164,18 @@
<p><b>Fire tiltakstyper dekker det meste.</b> Bruk dem som huskeliste, ikke som fasit:</p> <p><b>Fire tiltakstyper dekker det meste.</b> Bruk dem som huskeliste, ikke som fasit:</p>
<table> <table>
<tr><th>Tiltakstype</th><th>Utbedring på veg</th><th>Tunnel</th></tr> <tr><th>Tiltakstype</th><th>Kontor-IT</th><th>Driftssenter</th></tr>
<tr><td><b>Ny teknologi erstatter gammel</b></td> <tr><td><b>Ny teknologi erstatter gammel</b></td>
<td>LED i veglys · nye rekkverkstyper med lengre levetid</td> <td>energigjerrige PC-er erstatter eldre modeller · skjermer med lengre levetid</td>
<td>LED-armaturer · frekvensstyrte vifter</td></tr> <td>effektive kjøleaggregater · frekvensstyrte vifter</td></tr>
<tr><td><b>Behovsstyring framfor fast drift</b></td> <tr><td><b>Behovsstyring framfor fast drift</b></td>
<td>vinterdrift utløst av målestasjon/prognose framfor fast rode-utkalling</td> <td>brukerstøtte utløst av overvåkingsvarsel framfor fast besøksrunde</td>
<td>ventilasjon styrt på målt CO/NO₂ framfor fast drift · finere dimmetrinn på dagsonen</td></tr> <td>kjøling styrt på målt temperatur framfor fast drift · finere styringstrinn på hovedkjølingen</td></tr>
<tr><td><b>Tilstandsbasert framfor intervallbasert</b></td> <tr><td><b>Tilstandsbasert framfor intervallbasert</b></td>
<td>dekkefornyelse etter målt spor og jevnhet framfor fast syklus · grøfterens etter tilstand</td> <td>utskifting av PC-er etter målt feilrate framfor fast syklus · lisensopprydding etter faktisk bruk</td>
<td>vask og renhold etter målt tilsmussing framfor fast frekvens</td></tr> <td>filterbytte og rengjøring etter målt trykkfall framfor fast frekvens</td></tr>
<tr><td><b>Levetidsforlengelse framfor utskifting</b></td> <tr><td><b>Levetidsforlengelse framfor utskifting</b></td>
<td>forsegling eller tynndekke framfor full reasfaltering · reparasjon framfor bytte av rekkverk</td> <td>minneoppgradering framfor ny maskin · reparasjon framfor bytte av dokkingstasjoner</td>
<td>rehabilitering av eksisterende installasjon framfor full utskifting</td></tr> <td>rehabilitering av eksisterende installasjon framfor full utskifting</td></tr>
</table> </table>
@ -197,11 +198,11 @@
<table> <table>
<tr><th></th><th>Hypotese</th><th>Blir til</th></tr> <tr><th></th><th>Hypotese</th><th>Blir til</th></tr>
<tr><td>✔</td><td>Færre vinterutkallinger med prognosestyring</td><td>antall utkallinger × kr per utkalling</td></tr> <tr><td>✔</td><td>Færre utrykninger med overvåkingsstyring</td><td>antall utrykninger × kr per utrykning</td></tr>
<tr><td>✔</td><td>Lengre intervall mellom tunnelvask</td><td>antall vask per år × kr per vask</td></tr> <tr><td>✔</td><td>Lengre intervall mellom filterbytte i kjøleanlegget</td><td>antall bytter per år × kr per bytte</td></tr>
<tr><td>✔</td><td>Finere dimming av tunnelbelysningen</td><td>kWh per år × kr per kWh</td></tr> <tr><td>✔</td><td>Finere trinnstyring av kjøleanlegget</td><td>kWh per år × kr per kWh</td></tr>
<tr><td>✔</td><td>Tynndekke framfor full reasfaltering</td><td>m² × kr per m²</td></tr> <tr><td>✔</td><td>Minneoppgradering framfor ny PC</td><td>antall maskiner × kr per maskin</td></tr>
<tr><td>✘</td><td>«Bedre samhandling med entreprenøren»</td><td>ingen mengde, ingen enhetspris</td></tr> <tr><td>✘</td><td>«Bedre samhandling med leverandøren»</td><td>ingen mengde, ingen enhetspris</td></tr>
<tr><td>✘</td><td>«Tidligere involvering av fagressurser»</td><td>ingen mengde, ingen enhetspris</td></tr> <tr><td>✘</td><td>«Tidligere involvering av fagressurser»</td><td>ingen mengde, ingen enhetspris</td></tr>
</table> </table>
@ -226,15 +227,15 @@
<table> <table>
<tr><th>Prosjekttype</th><th>Typiske linjer</th><th>Formen</th></tr> <tr><th>Prosjekttype</th><th>Typiske linjer</th><th>Formen</th></tr>
<tr><td rowspan="4"><b>Utbedring veg</b></td> <tr><td rowspan="4"><b>Kontor-IT</b></td>
<td>dekkefornyelse</td><td>m² × kr/m²</td></tr> <td>utskifting av PC-er</td><td>antall × kr/stk</td></tr>
<tr><td>vinterdrift</td><td>utkallinger/år × kr per utkalling, eller km × kr/km</td></tr> <tr><td>brukerstøtte</td><td>utrykninger/år × kr per utrykning, eller brukere × kr/bruker</td></tr>
<tr><td>veglys, energi</td><td>kWh/år × kr/kWh</td></tr> <tr><td>klientpark, energi</td><td>kWh/år × kr/kWh</td></tr>
<tr><td>grøfterens, kantklipp, rekkverk</td><td>løpemeter × kr/lm</td></tr> <tr><td>kabling, nettverksuttak</td><td>løpemeter × kr/lm</td></tr>
<tr><td rowspan="4"><b>Tunnel</b></td> <tr><td rowspan="4"><b>Driftssenter</b></td>
<td>belysning, energi</td><td>kWh/år × kr/kWh</td></tr> <td>kjøling, energi</td><td>kWh/år × kr/kWh</td></tr>
<tr><td>ventilasjon, energi</td><td>kWh/år × kr/kWh</td></tr> <tr><td>øvrig teknisk anlegg, energi</td><td>kWh/år × kr/kWh</td></tr>
<tr><td>vask og renhold</td><td>vask/år × kr per vask</td></tr> <tr><td>filterbytte og rengjøring</td><td>bytter/år × kr per bytte</td></tr>
<tr><td>utskifting av komponenter</td><td>antall × kr/stk</td></tr> <tr><td>utskifting av komponenter</td><td>antall × kr/stk</td></tr>
</table> </table>
@ -260,25 +261,25 @@
<p>Ikke spør om integrasjoner. Spør per tall. Fem kategorier dekker det meste:</p> <p>Ikke spør om integrasjoner. Spør per tall. Fem kategorier dekker det meste:</p>
<table> <table>
<tr><th>Kategori</th><th>Utbedring veg</th><th>Tunnel</th><th>Uten den</th></tr> <tr><th>Kategori</th><th>Kontor-IT</th><th>Driftssenter</th><th>Uten den</th></tr>
<tr><td><b>A. Kostnad og regnskap</b><br><span class="dimt">faktura, kalkyle, kontraktspriser</span></td> <tr><td><b>A. Kostnad og regnskap</b><br><span class="dimt">faktura, kalkyle, kontraktspriser</span></td>
<td>enhetspriser fra driftskontrakt, sluttkostnad fra tilsvarende prosjekt</td> <td>enhetspriser fra rammeavtale, sluttkostnad fra tilsvarende prosjekt</td>
<td>energifaktura, priser fra siste elektroanbud</td> <td>energifaktura, priser fra siste kjøleanbud</td>
<td><b>kontrollen er uforankret</b></td></tr> <td><b>kontrollen er uforankret</b></td></tr>
<tr><td><b>B. Objekt og mengde</b><br><span class="dimt">hva anlegget består av</span></td> <tr><td><b>B. Objekt og mengde</b><br><span class="dimt">hva anlegget består av</span></td>
<td>km veg, m² dekke, antall stikkrenner, meter rekkverk, alder og tilstand</td> <td>antall PC-er og skjermer, antall lisenser, meter kabling, alder og tilstand</td>
<td>antall armaturer og effekt, antall vifter og pumper, lengde, antall løp</td> <td>antall kjøleaggregater og effekt, antall vifter og pumper, rackplasser, antall rom</td>
<td>ingen mengde å gange med</td></tr> <td>ingen mengde å gange med</td></tr>
<tr><td><b>C. Bruk og driftsprofil</b><br><span class="dimt">hvor mye, hvor ofte, hvor lenge</span></td> <tr><td><b>C. Bruk og driftsprofil</b><br><span class="dimt">hvor mye, hvor ofte, hvor lenge</span></td>
<td>ÅDT, antall vinterutkallinger, saltmengde, klippefrekvens</td> <td>antall brukere, antall utrykninger, driftstimer per maskin, lisensbruk</td>
<td>brenntimer, driftstimer vifter, vaskefrekvens, trafikkfordeling</td> <td>driftstimer kjøling, driftstimer vifter, filterbyttefrekvens, lastfordeling</td>
<td>årsforbruket kan ikke regnes</td></tr> <td>årsforbruket kan ikke regnes</td></tr>
<tr><td><b>D. Historikk</b><br><span class="dimt">hva som er gjort</span></td> <tr><td><b>D. Historikk</b><br><span class="dimt">hva som er gjort</span></td>
<td>utførte dekkefornyelser med årstall og strekning</td> <td>utførte utskiftinger av PC-er med årstall og avdeling</td>
<td>utskiftinger, oppgraderinger, rehabiliteringer med årstall</td> <td>utskiftinger, oppgraderinger, rehabiliteringer med årstall</td>
<td>tiltak foreslås på nytt, gevinst dobbelttelles</td></tr> <td>tiltak foreslås på nytt, gevinst dobbelttelles</td></tr>
<tr><td><b>E. Kontrakt og avtale</b><br><span class="dimt">hva som er bundet</span></td> <tr><td><b>E. Kontrakt og avtale</b><br><span class="dimt">hva som er bundet</span></td>
<td>driftskontraktens omfang og løpetid, opsjoner</td> <td>rammeavtalens omfang og løpetid, opsjoner</td>
<td>serviceavtaler, garantiperioder</td> <td>serviceavtaler, garantiperioder</td>
<td>tiltak foreslås som ikke kan bestilles</td></tr> <td>tiltak foreslås som ikke kan bestilles</td></tr>
</table> </table>
@ -304,8 +305,8 @@
<table> <table>
<tr><th>Kategori</th><th>Hva det er</th><th>Hva det gjør i kjøringen</th></tr> <tr><th>Kategori</th><th>Hva det er</th><th>Hva det gjør i kjøringen</th></tr>
<tr><td><b>F. Normer og krav</b></td> <tr><td><b>F. Normer og krav</b></td>
<td>håndbøker og vegnormaler som gjelder tiltaket — for tunnelbelysning <td>standarder og interne driftskrav som gjelder tiltaket — for kjøling i et
f.eks. Håndbok V124 og N500, med paragraf</td> serverrom f.eks. virksomhetens egne temperaturkrav, med paragraf</td>
<td>setter gulvet ingen besparelse kan gå under; hindrer forslag som bryter krav</td></tr> <td>setter gulvet ingen besparelse kan gå under; hindrer forslag som bryter krav</td></tr>
<tr><td><b>G. Priser og indekser</b></td> <tr><td><b>G. Priser og indekser</b></td>
<td>kraftpris og nettleie, prisindekser, markedspriser fra sammenlignbare anbud</td> <td>kraftpris og nettleie, prisindekser, markedspriser fra sammenlignbare anbud</td>
@ -390,18 +391,18 @@
som bryter krav ingen har fortalt den om, og du bruker tid på å avvise det samme igjen og igjen.</p> som bryter krav ingen har fortalt den om, og du bruker tid på å avvise det samme igjen og igjen.</p>
<table> <table>
<tr><th>Type ramme</th><th>Utbedring veg</th><th>Tunnel</th></tr> <tr><th>Type ramme</th><th>Kontor-IT</th><th>Driftssenter</th></tr>
<tr><td><b>Fagkrav med minstenivå</b></td> <tr><td><b>Fagkrav med minstenivå</b></td>
<td>krav til friksjon, jevnhet, sporddybde, siktforhold</td> <td>krav til ytelse, skjermstørrelse, oppetid, universell utforming</td>
<td>lystekniske minstekrav i sonene, luftkvalitetskrav, hysteresetid ved nivåendring</td></tr> <td>temperaturkrav i rommet, fuktighetskrav, hysteresetid ved nivåendring</td></tr>
<tr><td><b>Sikkerhetskrav</b></td> <tr><td><b>Sikkerhetskrav</b></td>
<td>rekkverksklasser, arbeidsvarsling</td> <td>krav til kryptering, tilgangsstyring</td>
<td>krav til nødbelysning, ventilasjon ved brann, redundans</td></tr> <td>krav til nødstrøm, brannslukking, redundant kjøling</td></tr>
<tr><td><b>Antakelser som ikke holder</b></td> <tr><td><b>Antakelser som ikke holder</b></td>
<td>«vi kan ikke forutsette at strekningen kan stenges»</td> <td>«vi kan ikke forutsette at brukerne kan være uten maskin en hel dag»</td>
<td>«vi kan ikke forutsette nattstenging for arbeid»</td></tr> <td>«vi kan ikke forutsette planlagt nedetid for arbeid»</td></tr>
<tr><td><b>Kontraktsbundet</b></td> <tr><td><b>Kontraktsbundet</b></td>
<td>driftskontraktens omfang ut avtaleperioden</td> <td>rammeavtalens omfang ut avtaleperioden</td>
<td>serviceavtaler, garantibetingelser</td></tr> <td>serviceavtaler, garantibetingelser</td></tr>
<tr><td><b>Budsjett og anskaffelse</b></td> <tr><td><b>Budsjett og anskaffelse</b></td>
<td colspan="2">hva som kan bestilles i hvilket år, terskelverdier</td></tr> <td colspan="2">hva som kan bestilles i hvilket år, terskelverdier</td></tr>
@ -452,7 +453,7 @@
leverer det. Der stopper regnestykket og din erfaring begynner — og det er den <b>eneste</b> leverer det. Der stopper regnestykket og din erfaring begynner — og det er den <b>eneste</b>
kunnskapen i hele prosessen som ikke kan hentes fra et system.</p> kunnskapen i hele prosessen som ikke kan hentes fra et system.</p>
<p><b>Et godt svar navngir mekanismen, ikke bare tallet.</b> Eksempel fra tunnelbelysning, der <p><b>Et godt svar navngir mekanismen, ikke bare tallet.</b> Eksempel fra trinnstyrt kjøling, der
tre kjente mekanismer trekker gevinsten ned:</p> tre kjente mekanismer trekker gevinsten ned:</p>
<table> <table>
@ -462,12 +463,12 @@
<tr><td>Den delen av tiltaket som ikke blir implementert</td> <tr><td>Den delen av tiltaket som ikke blir implementert</td>
<td>halve gevinsten kan ligge i en del som rutinemessig faller ut av leveransen</td></tr> <td>halve gevinsten kan ligge i en del som rutinemessig faller ut av leveransen</td></tr>
<tr><td>Kalibrering med sikkerhetsmargin</td> <tr><td>Kalibrering med sikkerhetsmargin</td>
<td>systematisk og ensrettet: ingen driftsorganisasjon justerer seg til for lite lys</td></tr> <td>systematisk og ensrettet: ingen driftsorganisasjon justerer seg til for lite kjøling</td></tr>
</table> </table>
<p>De samme spørsmålene på vegsiden: <i>Ble den nye driftsrutinen faktisk fulgt hele vinteren? <p>De samme spørsmålene på kontorsiden: <i>Ble den nye strømstyringen faktisk fulgt hele året?
Ble tilstandsmålingene brukt til å styre, eller bare rapportert? Hvor mye av tynndekket måtte Ble feilratene brukt til å styre utskiftingen, eller bare rapportert? Hvor mange av de oppgraderte
gjøres om igjen innen tre år?</i></p> maskinene måtte likevel byttes innen tre år?</i></p>
<div class="done"><b>Ferdig når</b> <div class="done"><b>Ferdig når</b>
Du har sagt, med egne ord: «forvent rundt <b>X</b> prosent av det som er beregnet, fordi <b>Y</b>.» Du har sagt, med egne ord: «forvent rundt <b>X</b> prosent av det som er beregnet, fordi <b>Y</b>.»
@ -563,7 +564,7 @@
<h3>Gjør svaret vesentlig bedre</h3> <h3>Gjør svaret vesentlig bedre</h3>
<ul> <ul>
<li>Objekt- og mengdedata: antall, effekt, alder, tilstand</li> <li>Objekt- og mengdedata: antall, effekt, alder, tilstand</li>
<li>Driftsprofil: timer, frekvenser, ÅDT, utkallinger</li> <li>Driftsprofil: timer, frekvenser, antall brukere, utrykninger</li>
<li>Erfaringstall for realiseringsgrad — eget eller lånt, merket hvilket</li> <li>Erfaringstall for realiseringsgrad — eget eller lånt, merket hvilket</li>
<li><b>Navngitte åpne datakilder</b>: hva hver av dem svarer på, og hvem som eier tilgangen</li> <li><b>Navngitte åpne datakilder</b>: hva hver av dem svarer på, og hvem som eier tilgangen</li>
<li>Produkt- og leverandørdata for de aktuelle tiltakene</li> <li>Produkt- og leverandørdata for de aktuelle tiltakene</li>

View file

@ -253,16 +253,16 @@
<rect class="box dashed" x="40" y="40" width="430" height="360"/> <rect class="box dashed" x="40" y="40" width="430" height="360"/>
<rect class="box" x="70" y="64" width="370" height="58"/> <rect class="box" x="70" y="64" width="370" height="58"/>
<text class="lbl" x="88" y="90">FV42-GSV-E1</text> <text class="lbl" x="88" y="90">KONTOR-IT-E1</text>
<text class="sub" x="88" y="110">Fv. 42 gang- og sykkelveg, etappe 1</text> <text class="sub" x="88" y="110">Kontor-IT, etappe 1</text>
<rect class="box soft" x="70" y="150" width="370" height="58"/> <rect class="box soft" x="70" y="150" width="370" height="58"/>
<text class="lbl" x="88" y="176">RV13-RAS-TP</text> <text class="lbl" x="88" y="176">NETT-SIKR-TP</text>
<text class="sub" x="88" y="196">Rv. 13 rassikring tunnelportal</text> <text class="sub" x="88" y="196">Nettverkssikring, tilkoblingspunkter</text>
<rect class="box soft" x="70" y="236" width="370" height="58"/> <rect class="box soft" x="70" y="236" width="370" height="58"/>
<text class="lbl" x="88" y="262">BRU-LAKS-REHAB</text> <text class="lbl" x="88" y="262">ARKIV-LAGR-MIGR</text>
<text class="sub" x="88" y="282">Bru over Lakselva, rehabilitering</text> <text class="sub" x="88" y="282">Arkivlagring — migrering</text>
<rect class="box soft" x="70" y="322" width="370" height="58"/> <rect class="box soft" x="70" y="322" width="370" height="58"/>
<text class="lbl" x="88" y="348">SKOLE-VVS-OPPGR</text> <text class="lbl" x="88" y="348">SKOLE-VVS-OPPGR</text>
@ -275,7 +275,7 @@
<path class="edge" d="M472 93 H556"/> <path class="edge" d="M472 93 H556"/>
<rect class="box" x="560" y="40" width="600" height="360"/> <rect class="box" x="560" y="40" width="600" height="360"/>
<text class="lbl" x="580" y="72">FV42-GSV-E1 — kostnadslinjene INNI prosjektet</text> <text class="lbl" x="580" y="72">KONTOR-IT-E1 — kostnadslinjene INNI prosjektet</text>
<path class="hair" d="M580 86 H1140"/> <path class="hair" d="M580 86 H1140"/>
<text class="tiny" x="580" y="106">kode</text> <text class="tiny" x="580" y="106">kode</text>
<text class="tiny" x="650" y="106">beskrivelse</text> <text class="tiny" x="650" y="106">beskrivelse</text>
@ -283,32 +283,32 @@
<text class="tiny" x="1140" y="106" text-anchor="end">enhetspris</text> <text class="tiny" x="1140" y="106" text-anchor="end">enhetspris</text>
<text class="mono" x="580" y="134">01.1</text> <text class="mono" x="580" y="134">01.1</text>
<text class="mono" x="650" y="134">Rigg og drift</text> <text class="mono" x="650" y="134">Prosjektledelse og drift</text>
<text class="mono" x="940" y="134">1 rs</text> <text class="mono" x="940" y="134">1 rs</text>
<text class="mono" x="1140" y="134" text-anchor="end">850 000</text> <text class="mono" x="1140" y="134" text-anchor="end">850 000</text>
<text class="mono" x="580" y="164">02.3</text> <text class="mono" x="580" y="164">02.3</text>
<text class="mono" x="650" y="164">Masseutskifting bløt grunn</text> <text class="mono" x="650" y="164">Migrering av brukerdata</text>
<text class="mono" x="940" y="164">4 200 m3</text> <text class="mono" x="940" y="164">4 200 GB</text>
<text class="mono" x="1140" y="164" text-anchor="end">420</text> <text class="mono" x="1140" y="164" text-anchor="end">420</text>
<text class="mono" x="580" y="194">03.1</text> <text class="mono" x="580" y="194">03.1</text>
<text class="mono" x="650" y="194">Forsterkningslag knust grus</text> <text class="mono" x="650" y="194">Nettverksutstyr kontor</text>
<text class="mono" x="940" y="194">1 800 m3</text> <text class="mono" x="940" y="194">1 800 port</text>
<text class="mono" x="1140" y="194" text-anchor="end">310</text> <text class="mono" x="1140" y="194" text-anchor="end">310</text>
<text class="mono" x="580" y="224">05.2</text> <text class="mono" x="580" y="224">05.2</text>
<text class="mono" x="650" y="224">Asfalt Ab11</text> <text class="mono" x="650" y="224">Lisens kontorpakke</text>
<text class="mono" x="940" y="224">4 300 m2</text> <text class="mono" x="940" y="224">4 300 lisens</text>
<text class="mono" x="1140" y="224" text-anchor="end">215</text> <text class="mono" x="1140" y="224" text-anchor="end">215</text>
<text class="mono" x="580" y="254">07.4</text> <text class="mono" x="580" y="254">07.4</text>
<text class="mono" x="650" y="254">Kantstein granitt</text> <text class="mono" x="650" y="254">Skjerm 27 tommer</text>
<text class="mono" x="940" y="254">2 400 m</text> <text class="mono" x="940" y="254">2 400 stk</text>
<text class="mono" x="1140" y="254" text-anchor="end">690</text> <text class="mono" x="1140" y="254" text-anchor="end">690</text>
<text class="mono" x="580" y="284">09.1</text> <text class="mono" x="580" y="284">09.1</text>
<text class="mono" x="650" y="284">Veglysmaster LED</text> <text class="mono" x="650" y="284">Møteromsskjerm med kamera</text>
<text class="mono" x="940" y="284">34 stk</text> <text class="mono" x="940" y="284">34 stk</text>
<text class="mono" x="1140" y="284" text-anchor="end">28 500</text> <text class="mono" x="1140" y="284" text-anchor="end">28 500</text>
@ -1243,9 +1243,9 @@ Produce a REVISED SavingsProposal that resolves this.</code></pre>
<p class="lede">Stage 0 avstemmer hver <code>affected_item</code> mot prosjektets egen <code>CostBaseline</code>: koden må finnes, og avviket i mengde og enhetspris må ligge innenfor 5 % av baselinen.</p> <p class="lede">Stage 0 avstemmer hver <code>affected_item</code> mot prosjektets egen <code>CostBaseline</code>: koden må finnes, og avviket i mengde og enhetspris må ligge innenfor 5 % av baselinen.</p>
<table class="compact"> <table class="compact">
<tr><th>Prosjekt</th><th>01.1 enhetspris</th><th>Σ prosjekt</th><th>Utfall</th></tr> <tr><th>Prosjekt</th><th>01.1 enhetspris</th><th>Σ prosjekt</th><th>Utfall</th></tr>
<tr><td>FV42-GSV-E1</td><td>850 000</td><td>6 721 500</td><td class="ok">validated</td></tr> <tr><td>KONTOR-IT-E1</td><td>850 000</td><td>6 721 500</td><td class="ok">validated</td></tr>
<tr><td>RV13-RAS-TP</td><td>1 450 000</td><td>7 529 700</td><td class="bad">rejected</td></tr> <tr><td>NETT-SIKR-TP</td><td>1 450 000</td><td>7 529 700</td><td class="bad">rejected</td></tr>
<tr><td>BRU-LAKS-REHAB</td><td>620 000</td><td>3 099 700</td><td class="bad">rejected</td></tr> <tr><td>ARKIV-LAGR-MIGR</td><td>620 000</td><td>3 099 700</td><td class="bad">rejected</td></tr>
<tr><td>SKOLE-VVS-OPPGR</td><td>480 000</td><td>4 640 000</td><td class="bad">rejected</td></tr> <tr><td>SKOLE-VVS-OPPGR</td><td>480 000</td><td>4 640 000</td><td class="bad">rejected</td></tr>
</table> </table>
<p class="small">READMEs portefølje-demo sender det SAMME forslaget mot alle fire. Alle fire bærer kostnadskoden 01.1, men til fire ulike beløp. Ett validert forslag, tre avvisninger. Ingenting ved forslaget endret seg.</p> <p class="small">READMEs portefølje-demo sender det SAMME forslaget mot alle fire. Alle fire bærer kostnadskoden 01.1, men til fire ulike beløp. Ett validert forslag, tre avvisninger. Ingenting ved forslaget endret seg.</p>
@ -1262,15 +1262,15 @@ Produce a REVISED SavingsProposal that resolves this.</code></pre>
<path class="edge" d="M520 96 C600 150 340 370 240 438"/> <path class="edge" d="M520 96 C600 150 340 370 240 438"/>
<rect class="box ok" x="240" y="140" width="640" height="64"/> <rect class="box ok" x="240" y="140" width="640" height="64"/>
<text class="lbl" x="262" y="166">FV42-GSV-E1</text> <text class="lbl" x="262" y="166">KONTOR-IT-E1</text>
<text class="sub" x="262" y="190">01.1 = 850 000 · innenfor toleransen · videre til solveren</text> <text class="sub" x="262" y="190">01.1 = 850 000 · innenfor toleransen · videre til solveren</text>
<rect class="box warn" x="240" y="230" width="640" height="64"/> <rect class="box warn" x="240" y="230" width="640" height="64"/>
<text class="lbl" x="262" y="256">RV13-RAS-TP</text> <text class="lbl" x="262" y="256">NETT-SIKR-TP</text>
<text class="sub" x="262" y="280">01.1 = 1 450 000 · avviket er langt over 5 % · nektet</text> <text class="sub" x="262" y="280">01.1 = 1 450 000 · avviket er langt over 5 % · nektet</text>
<rect class="box warn" x="240" y="320" width="640" height="64"/> <rect class="box warn" x="240" y="320" width="640" height="64"/>
<text class="lbl" x="262" y="346">BRU-LAKS-REHAB</text> <text class="lbl" x="262" y="346">ARKIV-LAGR-MIGR</text>
<text class="sub" x="262" y="370">01.1 = 620 000 · avviket er langt over 5 % · nektet</text> <text class="sub" x="262" y="370">01.1 = 620 000 · avviket er langt over 5 % · nektet</text>
<rect class="box warn" x="240" y="410" width="640" height="64"/> <rect class="box warn" x="240" y="410" width="640" height="64"/>
@ -1337,7 +1337,7 @@ Produce a REVISED SavingsProposal that resolves this.</code></pre>
<text class="lbl" x="452" y="424">classify_codes: identifier | prose | requirement</text> <text class="lbl" x="452" y="424">classify_codes: identifier | prose | requirement</text>
<text class="tiny" x="452" y="448">En RAPPORT om hva kjøringen gjorde av koden, ikke porten. Havner i provenance-stemplet som code_forms.</text> <text class="tiny" x="452" y="448">En RAPPORT om hva kjøringen gjorde av koden, ikke porten. Havner i provenance-stemplet som code_forms.</text>
</svg> </svg>
<figcaption>Fire underkontroller per berørt post, første treff vinner. Målt i P18 runde 2: to alminnelige ord fra en standards prosa (4 av 270 N500-dokumenter og 4 av 1 133 N200-dokumenter) passerte hele porten som kostnadskoder før kodeform-regelen fantes.</figcaption> <figcaption>Fire underkontroller per berørt post, første treff vinner. Målt i P18 runde 2: to alminnelige ord fra en standards prosa (4 av 270 dokumenter i én kravbase og 4 av 1 133 i en annen, i et kravkorpus brukt under utviklingen) passerte hele porten som kostnadskoder før kodeform-regelen fantes.</figcaption>
</figure> </figure>
<p class="src">Kilde: <code>src/portfolio_optimiser/validator.py:471-491</code> · <code>validator.py:494-571</code> · <code>validator.py:574-645</code> · <code>validator.py:388-408</code></p> <p class="src">Kilde: <code>src/portfolio_optimiser/validator.py:471-491</code> · <code>validator.py:494-571</code> · <code>validator.py:574-645</code> · <code>validator.py:388-408</code></p>
</div> </div>
@ -1409,7 +1409,7 @@ Produce a REVISED SavingsProposal that resolves this.</code></pre>
</table> </table>
<figure class="fig"> <figure class="fig">
<svg viewBox="0 0 1200 130" role="img" aria-label="Elleve av tolv runder brukt paa svar som feilet identisk"> <svg viewBox="0 0 1200 130" role="img" aria-label="Elleve av tolv runder brukt paa svar som feilet identisk">
<text class="sub" x="40" y="26">kontrakt-sorasen-04, runde-3-kjøring: 11 av 12 runder brukt på svar som feilet på samme måte</text> <text class="sub" x="40" y="26">Runde-3-kjøring på et kontraktssett under utviklingen: 11 av 12 runder brukt på svar som feilet på samme måte</text>
<rect class="box warn" x="40" y="42" width="82" height="46"/> <rect class="box warn" x="40" y="42" width="82" height="46"/>
<rect class="box warn" x="128" y="42" width="82" height="46"/> <rect class="box warn" x="128" y="42" width="82" height="46"/>
<rect class="box warn" x="216" y="42" width="82" height="46"/> <rect class="box warn" x="216" y="42" width="82" height="46"/>
@ -2043,7 +2043,7 @@ Produce a REVISED SavingsProposal that resolves this.</code></pre>
</svg> </svg>
<figcaption>Alle tre lukkes uten at noe forlater prosessen. Avvisningene samles opp i <code>RunResult.refinements</code>, så en leser kan se hva kjøringen faktisk ble fortalt.</figcaption> <figcaption>Alle tre lukkes uten at noe forlater prosessen. Avvisningene samles opp i <code>RunResult.refinements</code>, så en leser kan se hva kjøringen faktisk ble fortalt.</figcaption>
</figure> </figure>
<p class="callout warn"><b>Målt, ikke antatt.</b> Før parse-grunnen ble båret videre, kalte <code>_fetch_parsed</code> modellen med byte-identisk prompt. Én betalt kjøring (<code>kontrakt-sorasen-04</code>) brukte elleve av sine tolv runder på svar som feilet på samme måte, fordi ingenting noensinne fortalte modellen hva som var galt.</p> <p class="callout warn"><b>Målt, ikke antatt.</b> Før parse-grunnen ble båret videre, kalte <code>_fetch_parsed</code> modellen med byte-identisk prompt. Én betalt kjøring på et kontraktssett under utviklingen brukte elleve av sine tolv runder på svar som feilet på samme måte, fordi ingenting noensinne fortalte modellen hva som var galt.</p>
<p class="src">Kilde: <code>src/portfolio_optimiser/generate.py:282-327</code>, <code>:642-673</code> · <code>workflow.py:100-116</code> · <code>run.py:664-687</code>, <code>:204-214</code> · <code>budget.py:44</code></p> <p class="src">Kilde: <code>src/portfolio_optimiser/generate.py:282-327</code>, <code>:642-673</code> · <code>workflow.py:100-116</code> · <code>run.py:664-687</code>, <code>:204-214</code> · <code>budget.py:44</code></p>
</div> </div>
</section> </section>
@ -2945,7 +2945,7 @@ Produce a REVISED SavingsProposal that resolves this.</code></pre>
<tr><td>Penger kvantiseres i én rekkefølge, fra én kilde: per beløp, så summeres heltallene.</td><td>Tre linjer à 60000,005 NOK er 18 000 003 øre kvantisert først, 18 000 001 summert først. En fiks på bare ett av de to kallstedene overlevde hele suiten</td><td><code>test_money_quantization_loadbearing.py</code>, 5 mutasjoner røde</td></tr> <tr><td>Penger kvantiseres i én rekkefølge, fra én kilde: per beløp, så summeres heltallene.</td><td>Tre linjer à 60000,005 NOK er 18 000 003 øre kvantisert først, 18 000 001 summert først. En fiks på bare ett av de to kallstedene overlevde hele suiten</td><td><code>test_money_quantization_loadbearing.py</code>, 5 mutasjoner røde</td></tr>
<tr><td>Sporing er opt-in, og «av» betyr at MAF aldri kalles.</td><td>Et kall med tom exporter-liste ville installert providers og lest hver <code>OTEL_EXPORTER_OTLP_*</code> i omgivelsene. «Av» må være fravær av kallet</td><td><code>test_tracing_loadbearing.py</code>, 9 mutasjoner røde</td></tr> <tr><td>Sporing er opt-in, og «av» betyr at MAF aldri kalles.</td><td>Et kall med tom exporter-liste ville installert providers og lest hver <code>OTEL_EXPORTER_OTLP_*</code> i omgivelsene. «Av» må være fravær av kallet</td><td><code>test_tracing_loadbearing.py</code>, 9 mutasjoner røde</td></tr>
<tr><td>En nekt modellen skal kunne rette seg etter er en returverdi, aldri en raise.</td><td>Funn 99, 08.09: MAF gjør en raise om til <code>Error: Function failed.</code>, undertrykker detaljen og teller den mot grensen på 3 påfølgende verktøyfeil. Modellen gjettet på JSON-formatet, som var riktig hele veien</td><td><code>test_run_cli_chatclient_refusal.py</code>, 8 armer, 7 mutasjoner røde</td></tr> <tr><td>En nekt modellen skal kunne rette seg etter er en returverdi, aldri en raise.</td><td>Funn 99, 08.09: MAF gjør en raise om til <code>Error: Function failed.</code>, undertrykker detaljen og teller den mot grensen på 3 påfølgende verktøyfeil. Modellen gjettet på JSON-formatet, som var riktig hele veien</td><td><code>test_run_cli_chatclient_refusal.py</code>, 8 armer, 7 mutasjoner røde</td></tr>
<tr><td>Målingene leser en FROSSET kopi av korpuset, pinnet med sha256 — aldri et annet repos build-mappe.</td><td>17.09 kl. 17:43: nabo-repoet bygde om <code>r761-2025</code>, gatens rad 6 og 7 ble IKKE MÅLT og fem tester falt, for en endring ingen her hadde gjort</td><td><code>test_frozen_bundles_loadbearing.py</code>, 17 armer, 8 mutasjoner røde</td></tr> <tr><td>Målingene leser en FROSSET kopi av korpuset, pinnet med sha256 — aldri et annet repos build-mappe.</td><td>17.09 kl. 17:43: nabo-repoet bygde om en av kildebasene, gatens rad 6 og 7 ble IKKE MÅLT og fem tester falt, for en endring ingen her hadde gjort</td><td><code>test_frozen_bundles_loadbearing.py</code>, 17 armer, 8 mutasjoner røde</td></tr>
</tbody> </tbody>
</table> </table>
<p class="callout"><b>Hovedboken ble flyttet 18.09.2026.</b> <code>CLAUDE.md</code> hadde vokst til 310 919 byte mot Claude Codes kutt på 150 000 tegn, så omtrent halvparten av reglene nådde ingen økt. Etter flyttingen er fila 6 033 byte, og 93 rader står ordrett i hovedboken.</p> <p class="callout"><b>Hovedboken ble flyttet 18.09.2026.</b> <code>CLAUDE.md</code> hadde vokst til 310 919 byte mot Claude Codes kutt på 150 000 tegn, så omtrent halvparten av reglene nådde ingen økt. Etter flyttingen er fila 6 033 byte, og 93 rader står ordrett i hovedboken.</p>
@ -2961,7 +2961,7 @@ Produce a REVISED SavingsProposal that resolves this.</code></pre>
<figure class="fig"> <figure class="fig">
<svg viewBox="0 0 1200 500" role="img" aria-label="Fire målte kontekstkostnader, hver med søyle for før og etter"> <svg viewBox="0 0 1200 500" role="img" aria-label="Fire målte kontekstkostnader, hver med søyle for før og etter">
<text class="lbl" x="10" y="40">list_bundles — katalogen</text> <text class="lbl" x="10" y="40">list_bundles — katalogen</text>
<text class="sub" x="10" y="60">3 flate Vegnormal-baser, o200k_base</text> <text class="sub" x="10" y="60">3 flate kravbaser (utviklingskorpus), o200k_base</text>
<text class="tiny" x="10" y="78">docs/2026-08-26-katalogkostnaden.md</text> <text class="tiny" x="10" y="78">docs/2026-08-26-katalogkostnaden.md</text>
<text class="tiny" x="420" y="42" text-anchor="end">før</text> <text class="tiny" x="420" y="42" text-anchor="end">før</text>
<rect class="box soft" x="430" y="24" width="560" height="24"/> <rect class="box soft" x="430" y="24" width="560" height="24"/>
@ -2972,7 +2972,7 @@ Produce a REVISED SavingsProposal that resolves this.</code></pre>
<line class="hair" x1="10" y1="110" x2="1190" y2="110"/> <line class="hair" x1="10" y1="110" x2="1190" y2="110"/>
<text class="lbl" x="10" y="158">read_bundle — payloaden</text> <text class="lbl" x="10" y="158">read_bundle — payloaden</text>
<text class="sub" x="10" y="178">tunnel-basen, 5 kopier per kjøring</text> <text class="sub" x="10" y="178">én eksempelbase, 5 kopier per kjøring</text>
<text class="tiny" x="10" y="196">docs/2026-09-02-read-bundle-kontekstkostnad.md</text> <text class="tiny" x="10" y="196">docs/2026-09-02-read-bundle-kontekstkostnad.md</text>
<text class="tiny" x="420" y="160" text-anchor="end">før</text> <text class="tiny" x="420" y="160" text-anchor="end">før</text>
<rect class="box soft" x="430" y="142" width="560" height="24"/> <rect class="box soft" x="430" y="142" width="560" height="24"/>
@ -3050,7 +3050,7 @@ Produce a REVISED SavingsProposal that resolves this.</code></pre>
<figcaption>Testtallet er målt i denne økten med <code>uv run pytest -q --co</code> (kun innsamling; suiten ble ikke kjørt). STATE fører hovedtreet som 1 984 passed / 5 skipped / 5 xfailed.</figcaption> <figcaption>Testtallet er målt i denne økten med <code>uv run pytest -q --co</code> (kun innsamling; suiten ble ikke kjørt). STATE fører hovedtreet som 1 984 passed / 5 skipped / 5 xfailed.</figcaption>
</figure> </figure>
<p class="callout"><b>To forsøk samme dag, begge dokumentert.</b> Måleprotokollen fra 14.08 fører første forsøk som RC=1 med <code>BudgetExceeded</code> på rundetaket (rounds 12/13), som en uhåndtert traceback; det ble senere den hostede 429-armen. Gjenkjøringen samme dag (protokollens § 6) konkluderte i <code>rejected</code>: validatoren tok en kostlinje modellen hadde funnet på, på toleranseporten fordi bundlen ikke bar noen baseline. README beskriver gjenkjøringen. Kilde: <code>docs/2026-08-14-fase1b-forste-levende-kjoring.md:35-60</code> og <code>:189-224</code>.</p> <p class="callout"><b>To forsøk samme dag, begge dokumentert.</b> Måleprotokollen fra 14.08 fører første forsøk som RC=1 med <code>BudgetExceeded</code> på rundetaket (rounds 12/13), som en uhåndtert traceback; det ble senere den hostede 429-armen. Gjenkjøringen samme dag (protokollens § 6) konkluderte i <code>rejected</code>: validatoren tok en kostlinje modellen hadde funnet på, på toleranseporten fordi bundlen ikke bar noen baseline. README beskriver gjenkjøringen. Kilde: <code>docs/2026-08-14-fase1b-forste-levende-kjoring.md:35-60</code> og <code>:189-224</code>.</p>
<p class="src">Kilde: <code>docs/2026-08-14-fase1b-forste-levende-kjoring.md:36</code>, <code>:55</code> · <code>README.md:281</code>, <code>:284</code> · <code>docs/2026-08-25-fable-misjonsreview.md:21</code>, <code>:22</code> · <code>docs/2026-09-14-p16-stressrunde-1.md:178</code></p> <p class="src">Kilde: <code>docs/2026-08-14-fase1b-forste-levende-kjoring.md:36</code>, <code>:55</code> · <code>README.md:281</code>, <code>:284</code> · <code>docs/2026-08-25-fable-misjonsreview.md:21</code>, <code>:22</code> · <code>docs/invarianter.md</code></p>
</div> </div>
</section> </section>
@ -3137,8 +3137,8 @@ Produce a REVISED SavingsProposal that resolves this.</code></pre>
</svg> </svg>
<figcaption>Runde 1–4 teller fasit-KONSEPTER åpnet (dokumenter i <code>fasit.must_cite</code>). Runde 5–6 rapporterer i stedet <code>grounded</code> per TILNÆRMING, 20 approach-rader. De to nevnerne er ulike og tallene er ikke direkte sammenlignbare; de står som publisert. Runde 3 er publisert både som 1 av 32 og som 1 av 26 pluss P17bs 0 av 6. <b>Og en sammendragslinje i STATE blander de to målene:</b> «0/26, 0/26, 1/32, 2/20, 1/20, 1/20» har skiftende nevner, og den siste verdien tilhører <code>named</code>, ikke <code>grounded</code> — runde 6 leser <code>grounded</code> 3 av 20. Konklusjonen overlever korreksjonen; aritmetikken i linja gjør det ikke.</figcaption> <figcaption>Runde 1–4 teller fasit-KONSEPTER åpnet (dokumenter i <code>fasit.must_cite</code>). Runde 5–6 rapporterer i stedet <code>grounded</code> per TILNÆRMING, 20 approach-rader. De to nevnerne er ulike og tallene er ikke direkte sammenlignbare; de står som publisert. Runde 3 er publisert både som 1 av 32 og som 1 av 26 pluss P17bs 0 av 6. <b>Og en sammendragslinje i STATE blander de to målene:</b> «0/26, 0/26, 1/32, 2/20, 1/20, 1/20» har skiftende nevner, og den siste verdien tilhører <code>named</code>, ikke <code>grounded</code> — runde 6 leser <code>grounded</code> 3 av 20. Konklusjonen overlever korreksjonen; aritmetikken i linja gjør det ikke.</figcaption>
</figure> </figure>
<p class="callout warn"><b>«N100 = FERDIG» fra økt 102 er motsagt av P16 til P22.</b> Mekanikken virker og navigerer ekte korpus; de faglige treffene kommer ikke. Ingen faktura er lest i noen runde, så hvert NOK-tall er et listepris-anslag. Og som P22 selv skriver: at <code>must_refuse</code> falt fra 5 av 5 til 2 av 5 er ikke bevis for at systemet er blitt verre, det er bevis for at den forrige målingen ikke kunne skille.</p> <p class="callout warn"><b>«Første kravbase = FERDIG» fra økt 102 er motsagt av P16 til P22.</b> Mekanikken virker og navigerer ekte korpus; de faglige treffene kommer ikke. Ingen faktura er lest i noen runde, så hvert NOK-tall er et listepris-anslag. Og som P22 selv skriver: at <code>must_refuse</code> falt fra 5 av 5 til 2 av 5 er ikke bevis for at systemet er blitt verre, det er bevis for at den forrige målingen ikke kunne skille.</p>
<p class="src">Kilde: <code>docs/2026-09-14-p16-stressrunde-1.md</code> · <code>-p18-</code> · <code>-p19-</code> · <code>-p20-</code> · <code>-p21-</code> · <code>docs/2026-09-16-p22-stressrunde-6.md</code> · <code>STATE.md</code></p> <p class="src">Kilde: <code>docs/invarianter.md</code> · <code>STATE.md</code></p>
</div> </div>
</section> </section>
@ -3160,7 +3160,7 @@ Produce a REVISED SavingsProposal that resolves this.</code></pre>
<div class="cols"> <div class="cols">
<div class="col"> <div class="col">
<span class="step">Hvorfor det ble omskrevet</span> <span class="step">Hvorfor det ble omskrevet</span>
<p>Økt 102 konkluderte «N100 = FERDIG». P16 til P22 motsa den. Seks betalte runder på syntetiske mandater, uten et menneske i sløyfa, produserte ingen rapport en fagperson har rettet.</p> <p>Økt 102 konkluderte «første kravbase = FERDIG». P16 til P22 motsa den. Seks betalte runder på syntetiske mandater, uten et menneske i sløyfa, produserte ingen rapport en fagperson har rettet.</p>
</div> </div>
<div class="col"> <div class="col">
<span class="step">Hva målingen faktisk viste</span> <span class="step">Hva målingen faktisk viste</span>
@ -3264,7 +3264,7 @@ Produce a REVISED SavingsProposal that resolves this.</code></pre>
</table> </table>
<p class="callout warn"><b>En strukturell spenning — dette er en lesning, ikke repoets tekst (antakelse).</b> Rad 6 kan bare måles mot artefakter yngre enn regelen, og ordren sier gaten ikke når exit 0 «før en ny stressrunde finnes». Planen forbyr samtidig en ny stressrunde på syntetiske mandater. Begge holder bare hvis runden som løsner rad 6 er én av de tre ekte fagperson-rundene. Det står ingen steder, og må bekreftes av operatøren.</p> <p class="callout warn"><b>En strukturell spenning — dette er en lesning, ikke repoets tekst (antakelse).</b> Rad 6 kan bare måles mot artefakter yngre enn regelen, og ordren sier gaten ikke når exit 0 «før en ny stressrunde finnes». Planen forbyr samtidig en ny stressrunde på syntetiske mandater. Begge holder bare hvis runden som løsner rad 6 er én av de tre ekte fagperson-rundene. Det står ingen steder, og må bekreftes av operatøren.</p>
<p class="callout"><b>Måle-hygiene som egen sak.</b> Samme commit bærer tre suite-tall — 1 967, 1 962 og 1 917 — og ingen av dem lot seg gjenfinne. Regelen ordren setter er kort: før et suite-tall med kommandoen som ga det. Tallet i denne presentasjonen er 1 995 samlet, målt 18.09 med <code>uv run pytest -q --co</code>.</p> <p class="callout"><b>Måle-hygiene som egen sak.</b> Samme commit bærer tre suite-tall — 1 967, 1 962 og 1 917 — og ingen av dem lot seg gjenfinne. Regelen ordren setter er kort: før et suite-tall med kommandoen som ga det. Tallet i denne presentasjonen er 1 995 samlet, målt 18.09 med <code>uv run pytest -q --co</code>.</p>
<p class="src">Kilde: <code>PLAN.md § Ferdig-kriteriet</code>, <code>§ Åpne spørsmål</code> · <code>STATE.md § NESTE</code> · ordre <code>20260917T223645Z-1292712426</code> · <code>docs/2026-09-16-p22-stressrunde-6.md § 4</code></p> <p class="src">Kilde: <code>PLAN.md § Ferdig-kriteriet</code>, <code>§ Åpne spørsmål</code> · <code>STATE.md § NESTE</code> · ordre <code>20260917T223645Z-1292712426</code> · <code>docs/invarianter.md</code></p>
</div> </div>
</section> </section>
@ -3292,8 +3292,8 @@ Produce a REVISED SavingsProposal that resolves this.</code></pre>
<text class="band-lbl" x="632" y="42">Bygges ikke</text> <text class="band-lbl" x="632" y="42">Bygges ikke</text>
<text class="lbl" x="632" y="86">Ny stressrunde på syntetiske mandater</text> <text class="lbl" x="632" y="86">Ny stressrunde på syntetiske mandater</text>
<text class="sub" x="632" y="106">og ingen niende tilbakemeldingstype</text> <text class="sub" x="632" y="106">og ingen niende tilbakemeldingstype</text>
<text class="lbl" x="632" y="150">Nye korpus: N400 og R762</text> <text class="lbl" x="632" y="150">Nye kunnskapskorpus</text>
<text class="sub" x="632" y="170">basene publiseres ikke, ingen etatskontakt</text> <text class="sub" x="632" y="170">ingen nye baser i denne fasen</text>
<text class="lbl" x="632" y="214">Compliance-funksjoner</text> <text class="lbl" x="632" y="214">Compliance-funksjoner</text>
<text class="sub" x="632" y="234">DPIA, ROS og behandlingsformål eies av den som deployer</text> <text class="sub" x="632" y="234">DPIA, ROS og behandlingsformål eies av den som deployer</text>
<text class="lbl" x="632" y="278">Reallokering mellom prosjekter</text> <text class="lbl" x="632" y="278">Reallokering mellom prosjekter</text>

View file

@ -87,7 +87,7 @@ er MAJOR fordi de gjør **neste fasers premisser** falske eller farlige hvis de
### F3 [MAJOR · Akse 1/4] Ingen forankring av `affected_items` mot prosjektets faktiske kostbaseline ### F3 [MAJOR · Akse 1/4] Ingen forankring av `affected_items` mot prosjektets faktiske kostbaseline
- **Belegg:** `_parse_ir` aksepterer vilkårlige modell-forfattede kostlinjer (`generate.py:71-78`). - **Belegg:** `_parse_ir` aksepterer vilkårlige modell-forfattede kostlinjer (`generate.py:71-78`).
Road-stien har `project.cost_items` (`reference_domain.py:39-52`) men avstemmer aldri; bundle-stien Referanse-stien har `project.cost_items` (`reference_domain.py:39-52`) men avstemmer aldri; bundle-stien
bygger `Project` med `cost_items=()` (`run.py:168-195`). Både IR-invarianten (claim ≤ items-total, bygger `Project` med `cost_items=()` (`run.py:168-195`). Både IR-invarianten (claim ≤ items-total,
`ir.py:37-44`) og hele validatoren regner på selv-deklarerte tall. `ir.py:37-44`) og hele validatoren regner på selv-deklarerte tall.
- **Feilscenario:** proposer dikter `{code:"XX", quantity:1e6, unit_cost:10}` → total 10 MNOK → - **Feilscenario:** proposer dikter `{code:"XX", quantity:1e6, unit_cost:10}` → total 10 MNOK →
@ -206,9 +206,9 @@ er MAJOR fordi de gjør **neste fasers premisser** falske eller farlige hvis de
som SITERER formatet («enten 'VERDICT: APPROVE' eller 'VERDICT: REJECT'») feiltolkes som reject som SITERER formatet («enten 'VERDICT: APPROVE' eller 'VERDICT: REJECT'») feiltolkes som reject
(spec-en pinner reject-presedens, så dette er spec-konformt — men en siste-linje-parse ville vært (spec-en pinner reject-presedens, så dette er spec-konformt — men en siste-linje-parse ville vært
robustere; evt. spec-nyansering i D-A). robustere; evt. spec-nyansering i D-A).
- `retrieval._score` bruker substring-match per token (`retrieval.py:97`) — «as» matcher «asphalt»; - `retrieval._score` bruker substring-match per token (`retrieval.py:97`) — «as» matcher «assets»;
greit for MVP, erstattes uansett i S3.3. greit for MVP, erstattes uansett i S3.3.
- Road-stiens retrieval-query er hardkodet «cost saving measure» (`run.py:274`). - Referanse-stiens retrieval-query er hardkodet «cost saving measure» (`run.py:274`).
### F14 [MINOR · ærlighet] «Magentic er eksperimentell» er utdatert som begrunnelse (Python) ### F14 [MINOR · ærlighet] «Magentic er eksperimentell» er utdatert som begrunnelse (Python)

View file

@ -0,0 +1,415 @@
"""Build the two fictional example knowledge bases from ``kilde.json`` and pin them.
Usage (from the repository root)::
uv run python examples/syntetiske-kunnskapsbaser/generate.py
Writes ``src/portfolio_optimiser/data/kunnskapsbaser/<name>-<sha12>/`` for each base, removes any
older ``<name>-*`` copy, and rewrites the ``bundles`` entries of
``src/portfolio_optimiser/frozen_bundles.json`` with the new digests. Renewing a base is therefore
ONE command, and the new copy and the new pin land in the same diff.
**Deterministic by construction**: no clock, no randomness, sorted output, and document ids are
``uuid5`` of a fixed namespace and the document's own number. Running it twice gives byte-identical
bases and the same pins — ``--check`` says whether the tracked copies are what the source builds.
**Why generated rather than built with ``okf build``**: the code consumes per-document identifier
frontmatter (``req_number``, ``prosessnr``, ``seksjon``), and ``okf build`` takes frontmatter only
as ONE ``--frontmatter KEY=VALUE`` applied to every concept. The OKF layout itself — a root
``index.md`` declaring ``okf_version`` and ``bundle_id``, a linked ``index.md`` per level, one
concept document per file with a block ``sources`` sequence — is written here directly.
Everything the bases say is invented: Eksempelvirksomheten, its requirements and its process
catalogue describe no real organisation, and no text is taken from a real standard.
"""
from __future__ import annotations
import argparse
import json
import shutil
import sys
import tempfile
import uuid
from pathlib import Path
from typing import Any
HERE = Path(__file__).resolve().parent
REPO = HERE.parents[1]
SOURCE = HERE / "kilde.json"
TARGET = REPO / "src" / "portfolio_optimiser" / "data" / "kunnskapsbaser"
PIN_FILE = REPO / "src" / "portfolio_optimiser" / "frozen_bundles.json"
_NAMESPACE = uuid.uuid5(uuid.NAMESPACE_URL, "https://example.invalid/eksempelvirksomheten")
_DESCRIPTION_MAX = 160
def _uid(*parts: str) -> str:
return str(uuid.uuid5(_NAMESPACE, "/".join(parts)))
def _quoted(value: str) -> str:
"""A number-shaped scalar is quoted, as a YAML dumper would, so it stays a string."""
return f"'{value}'"
def _float_like(value: str) -> bool:
head, dot, tail = value.partition(".")
return head.isdigit() and (not dot or (tail.isdigit() and "." not in tail))
def _capitalised(text: str) -> str:
return text[:1].upper() + text[1:]
def _short(text: str) -> str:
if len(text) <= _DESCRIPTION_MAX:
return text
return text[: _DESCRIPTION_MAX - 1].rstrip() + "…"
def _frontmatter(pairs: list[tuple[str, str]], source: tuple[str, str]) -> str:
lines = ["---", *(f"{key}: {value}" for key, value in pairs)]
lines += ["sources:", f" - resource: {source[0]}", f" title: {source[1]}", "---"]
return "\n".join(lines) + "\n"
def _write(root: Path, rel: str, text: str) -> None:
path = root / rel
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(text, encoding="utf-8", newline="\n")
def _sort_key(number: str) -> tuple[int, ...]:
"""Natural order of a section number such as ``4.2.5.1``."""
return tuple(int(part) for part in number.split("."))
# ------------------------------------------------------------------------------ the requirements
def _requirements(part: dict[str, Any], spec: dict[str, Any]) -> list[dict[str, str]]:
"""Every requirement of one part: explicit ones verbatim, the rest from the aspect templates."""
aspects = spec["aspects"]
params = spec["parameters"]
out: list[dict[str, str]] = []
for index, section in enumerate(part["sections"]):
if "krav" in section:
for suffix, text in section["krav"]:
out.append(
{
"section": section["nr"],
"section_title": section["title"],
"suffix": suffix,
"text": text,
"guidance": section["veiledning"],
}
)
continue
subject = section["subject"]
for n in range(1, int(section["count"]) + 1):
aspect = aspects[(index + n - 1) % len(aspects)]
values = {
"S": _capitalised(subject),
"s": subject,
**{
key: str(options[(index + n) % len(options)]) for key, options in params.items()
},
}
out.append(
{
"section": section["nr"],
"section_title": section["title"],
"suffix": str(n),
"text": aspect["krav"].format(**values),
"guidance": aspect["veiledning"].format(**values),
}
)
return out
def build_requirements(spec: dict[str, Any], stamp: str, honesty: str, root: Path) -> None:
source_root = spec["source_root"]
level_lines: list[str] = []
standard_lines: list[str] = []
total = 0
for part in spec["parts"]:
pid = part["id"]
edition = f"{pid}:2027"
source = (f"{source_root}/{pid.lower()}", edition)
entries: list[tuple[tuple[int, ...], str, str]] = []
for req in _requirements(part, spec):
number = f"Krav {req['section']}—{req['suffix']}"
title = f"{number} {req['section_title']}"
doc_id = f"id-{_uid('driftskrav', pid, number)}"
section = req["section"]
text = req["text"]
pairs = [
("type", "Krav"),
("title", title),
("description", _short(text)),
("kravtype", "bør" if " bør " in f" {text} " else "skal"),
("standard", pid),
("utgave", edition),
("req_number", number),
("status", "stable"),
("trust_tier", "unverified"),
("seksjon", _quoted(section) if _float_like(section) else section),
("seksjonstittel", req["section_title"]),
("ingested_at", stamp),
("source_element_id", doc_id),
]
body = f"\n## Krav\n\n{text}\n\n## Veiledning (ikke-normativ)\n\n{req['guidance']}\n"
_write(root, f"krav/{pid}/{doc_id}.md", _frontmatter(pairs, source) + body)
entries.append(
(_sort_key(section), req["suffix"], f"- [{title}]({doc_id}.md) — {_short(text)}")
)
entries.sort(key=lambda e: (e[0], [int(p) for p in e[1].split("_")]))
_write(root, f"krav/{pid}/index.md", "# Krav\n\n" + "\n".join(e[2] for e in entries) + "\n")
level_lines.append(
f"- [{pid}]({pid}/index.md) — {len(entries)} konsepter under krav/{pid}."
)
total += len(entries)
pairs = [
("type", "Standard"),
("title", edition),
("description", f"Driftskrav del {pid[1:]}: {part['title']}"),
("standard", pid),
("utgave", edition),
("status", "stable"),
("trust_tier", "unverified"),
("krav_i_bundlen", str(len(entries))),
("ingested_at", stamp),
]
body = (
f"\n## Om delen\n\n{part['scope']} Delen bærer {len(entries)} krav, hvert som eget "
f"konsept under `krav/{pid}/`.\n\n## Opphav\n\n{honesty}\n"
)
_write(root, f"standard/{pid}.md", _frontmatter(pairs, source) + body)
standard_lines.append(
f"- [{edition}]({pid}.md) — Driftskrav del {pid[1:]}: {part['title']}"
)
_write(root, "krav/index.md", "# Kataloger\n\n" + "\n".join(level_lines) + "\n")
_write(root, "standard/index.md", "# Standard\n\n" + "\n".join(standard_lines) + "\n")
_write(
root,
"index.md",
f"---\nokf_version: 0.2\nbundle_id: {spec['bundle_id']}\n---\n\n# Kataloger\n\n"
f"- [krav](krav/index.md) — {total} konsepter under krav.\n"
f"- [standard](standard/index.md) — {len(spec['parts'])} konsepter under standard.\n",
)
# ------------------------------------------------------------------------- the process catalogue
def _processes(spec: dict[str, Any]) -> list[dict[str, Any]]:
"""The catalogue as a flat list, parents before children, each with its number and relations."""
out: list[dict[str, Any]] = []
first = int(spec["default_subs_for_first"])
for main in spec["main"]:
main_node = {
"nr": main["nr"],
"title": f"Hovedprosess {main['nr']} {main['title']}",
"link": f"Hovedprosess {main['nr']} {main['title']}",
"level": 1,
"main": main["nr"],
"parents": [],
"omfang": None,
"seksjon": f"Hovedprosess {main['nr']}",
"seksjonstittel": main["title"],
}
out.append(main_node)
for group in main["groups"]:
group_node = {
"nr": group["nr"],
"title": group["title"],
"link": f"{group['nr']} {group['title']}",
"level": 2,
"main": main["nr"],
"parents": [main_node],
"omfang": None,
}
out.append(group_node)
for i, proc in enumerate(group["processes"], start=1):
obj = proc.get("obj", proc["title"][:1].lower() + proc["title"][1:])
nr = f"{group['nr']}.{i}"
node = {
"nr": nr,
"title": proc["title"],
"link": f"{nr} {proc['title']}",
"level": 3,
"main": main["nr"],
"parents": [group_node, main_node],
"omfang": proc.get("omfang", spec["default_omfang"].format(obj=obj)),
}
out.append(node)
subs = proc.get("subs")
if subs is None:
subs = spec["default_subs"] if i <= first else []
for j, sub in enumerate(subs, start=1):
sub_nr = f"{nr}{j}"
title = sub["title"].format(obj=obj)
out.append(
{
"nr": sub_nr,
"title": title,
"link": f"{sub_nr} {title}",
"level": 4,
"main": main["nr"],
"parents": [node, group_node, main_node],
"omfang": sub["omfang"].format(obj=obj),
}
)
return out
def build_catalogue(spec: dict[str, Any], stamp: str, honesty: str, root: Path) -> None:
cat = spec["catalogue"]
source = (f"{spec['source_root']}/{cat.lower()}", spec["edition"])
nodes = _processes(spec)
for node in nodes:
node["dir"] = node["nr"].replace(".", "-")
node["file"] = f"_{node['main']}_id-{_uid('prosesskatalog', node['nr'])}.md"
node["children"] = []
by_nr = {node["nr"]: node for node in nodes}
for node in nodes:
if node["parents"]:
by_nr[node["parents"][0]["nr"]]["children"].append(node)
def rel(node: dict[str, Any]) -> str:
return f"../{node['dir']}/{node['file']}"
for node in nodes:
description = node["omfang"].split(". ")[0].rstrip(".") + "." if node["omfang"] else ""
summary = _short(description or node["title"])
pairs = [
("type", "Prosess"),
("title", node["title"]),
("description", summary),
("standard", spec["catalogue_title"]),
("utgave", spec["edition"]),
("prosessnr", _quoted(node["nr"])),
("hovedprosess", _quoted(node["main"])),
]
if node["parents"]:
pairs.append(("forelder", _quoted(node["parents"][0]["nr"])))
pairs += [
("nivaa", str(node["level"])),
("status", "stable"),
("trust_tier", "unverified"),
("seksjon", node.get("seksjon", _quoted(node["nr"]))),
("seksjonstittel", node.get("seksjonstittel", node["title"])),
("ingested_at", stamp),
("source_element_id", node["file"][:-3]),
]
body = ""
if node["omfang"]:
body += f"\n## a) Omfang\n\n{node['omfang']}\n"
body += "\n## Relasjoner\n"
if node["parents"]:
body += "\n### Overordnede prosesser\n\n"
body += "\n".join(f"- [{p['link']}]({rel(p)})" for p in node["parents"]) + "\n"
if node["children"]:
body += "\n### Underprosesser\n\n"
body += "\n".join(f"- [{c['link']}]({rel(c)})" for c in node["children"]) + "\n"
_write(root, f"{cat}/{node['dir']}/{node['file']}", _frontmatter(pairs, source) + body)
_write(
root,
f"{cat}/{node['dir']}/index.md",
f"# Prosess\n\n- [{node['title']}]({node['file']}) — {summary}\n",
)
dirs = sorted(node["dir"] for node in nodes)
_write(
root,
f"{cat}/index.md",
"# Kataloger\n\n"
+ "\n".join(f"- [{d}]({d}/index.md) — 1 konsept under {cat}/{d}." for d in dirs)
+ "\n",
)
mains = [node for node in nodes if node["level"] == 1]
pairs = [
("type", "Katalog"),
("title", spec["edition"]),
("description", spec["edition"]),
("standard", spec["catalogue_title"]),
("utgave", spec["edition"]),
("status", "stable"),
("trust_tier", "unverified"),
("prosesser_i_bundlen", str(len(nodes))),
("ingested_at", stamp),
]
body = (
f"\n## Dekning\n\n{spec['scope']} Bundlen bærer {len(nodes)} prosesser, hver som eget "
f"konsept under `{cat}/<prosessnummer>/`.\n\n## Opphav\n\n{honesty}\n\n"
"## Hovedprosesser\n\n"
+ "\n".join(f"- [{m['title']}]({cat}/{m['dir']}/{m['file']})" for m in mains)
+ "\n"
)
_write(root, f"{cat}.md", _frontmatter(pairs, source) + body)
_write(
root,
"index.md",
f"---\nokf_version: 0.2\nbundle_id: {spec['bundle_id']}\n---\n\n# Kataloger\n\n"
f"- [{cat}]({cat}/index.md) — {len(nodes)} konsepter under {cat}.\n\n# Katalog\n\n"
f"- [{spec['edition']}]({cat}.md) — {spec['edition']}\n",
)
# ------------------------------------------------------------------------------------ the pins
def _digest(root: Path) -> tuple[str, int]:
sys.path.insert(0, str(REPO / "src"))
from portfolio_optimiser.frozen_bundles import digest_bundle
return digest_bundle(root)
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__.split("\n", 1)[0])
parser.add_argument(
"--check",
action="store_true",
help="build into a temporary directory and compare with the pins; write nothing",
)
args = parser.parse_args(argv)
source = json.loads(SOURCE.read_text(encoding="utf-8"))
stamp, honesty = source["ingested_at"], source["honesty"]
builders = (
(source["driftskrav"], build_requirements),
(source["prosesskatalog"], build_catalogue),
)
pins = json.loads(PIN_FILE.read_text(encoding="utf-8"))
built: dict[str, dict[str, Any]] = {}
drift = 0
with tempfile.TemporaryDirectory() as tmp:
for spec, build in builders:
staged = Path(tmp) / spec["name"]
build(spec, stamp, honesty, staged)
sha, files = _digest(staged)
directory = f"{spec['name']}-{sha[:12]}"
pinned = pins["bundles"].get(spec["name"], {})
same = pinned.get("sha256") == sha and (TARGET / directory).is_dir()
print(f"{spec['name']}: {directory} ({files} files){'' if same else ' CHANGED'}")
if args.check:
drift += 0 if same else 1
continue
for old in TARGET.glob(f"{spec['name']}-*"):
shutil.rmtree(old)
TARGET.mkdir(parents=True, exist_ok=True)
shutil.copytree(staged, TARGET / directory)
built[spec["name"]] = {"directory": directory, "sha256": sha, "files": files}
if args.check:
return 1 if drift else 0
pins["bundles"] = built
PIN_FILE.write_text(json.dumps(pins, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -0,0 +1,326 @@
{
"about": "Fictional source for the two example knowledge bases. Eksempelvirksomheten, its requirements and its process catalogue are invented; no real organisation, place, person or publication is described. generate.py turns this file into OKF bundles.",
"ingested_at": "2027-01-15T12:00:00Z",
"honesty": "Alt innhold i denne kunnskapsbasen er oppdiktet eksempelmateriale om IT-drift i den fiktive Eksempelvirksomheten. Det er generert deterministisk av examples/syntetiske-kunnskapsbaser/generate.py fra kilde.json, og ingen tekst er hentet fra en virkelig standard.",
"driftskrav": {
"name": "driftskrav-2027",
"bundle_id": "eksempel-driftskrav-2027",
"source_root": "https://example.invalid/eksempelvirksomheten/driftskrav",
"aspects": [
{
"krav": "{S} skal være dokumentert med eier, plassering og driftsrutiner før tjenesten settes i drift.",
"veiledning": "Dokumentasjonen holdes i driftshåndboken og oppdateres ved hver endring."
},
{
"krav": "{S} skal overvåkes kontinuerlig, og avvik skal varsles til driftssenteret innen {m} minutter.",
"veiledning": "Varslingstiden regnes fra avviket oppstår til driftssenteret har mottatt alarmen."
},
{
"krav": "Forebyggende vedlikehold av {s} skal utføres minst {k} ganger per år.",
"veiledning": "Vedlikeholdet følger produsentens anvisning og protokollføres i vedlikeholdsplanen."
},
{
"krav": "{S} skal funksjonstestes minst én gang per år, og resultatet skal protokollføres.",
"veiledning": "Testen gjennomføres i et avtalt vindu og med en plan for tilbakeføring."
},
{
"krav": "Endringer i {s} skal gjennomføres etter godkjent endringsrutine og med plan for tilbakeføring.",
"veiledning": "Mindre standardendringer kan forhåndsgodkjennes i endringsrutinen."
},
{
"krav": "{S} skal ha en kapasitetsreserve på minst {p} % ved normal last.",
"veiledning": "Reserven vurderes på nytt når driftsklassen for en tjeneste endres."
},
{
"krav": "Avvik som gjelder {s}, skal registreres i avvikssystemet og lukkes med dokumentert årsak.",
"veiledning": "Gjentatte avvik av samme art behandles som et problem og følges opp særskilt."
},
{
"krav": "{S} bør gjennomgås årlig mot gjeldende trusselbilde og driftsbehov.",
"veiledning": "Gjennomgangen kan samordnes med den årlige risikovurderingen."
}
],
"parameters": {
"m": [5, 10, 15, 30],
"k": [2, 4, 6],
"p": [10, 15, 20, 25, 30]
},
"parts": [
{
"id": "D100",
"title": "Brukerflate og klientdrift",
"scope": "Delen stiller krav til klientplattform, brukerstøtte, tilgang og utstyr på arbeidsplasser og i møterom.",
"sections": [
{"nr": "1.1", "title": "Standardklient", "subject": "standardklienten", "count": 6},
{"nr": "1.2", "title": "Programvaredistribusjon", "subject": "programvaredistribusjonen", "count": 5},
{"nr": "1.3", "title": "Oppdateringer", "subject": "oppdateringsrutinen for klienter", "count": 5},
{"nr": "2.1", "title": "Tjenestedisk", "subject": "tjenestedisken", "count": 6},
{"nr": "2.2", "title": "Responstid", "subject": "responstiden for henvendelser", "count": 5},
{"nr": "3.1", "title": "Brukerkontoer", "subject": "brukerkontoene", "count": 6},
{"nr": "3.2", "title": "Tofaktorpålogging", "subject": "tofaktorpåloggingen", "count": 5},
{"nr": "4.1", "title": "Skjermer", "subject": "skjermene på arbeidsplassene", "count": 4},
{
"nr": "4.2.5.1",
"title": "Skjerm og kamera",
"veiledning": "Kravene gjelder møterom med fast utstyr. Mobile løsninger omfattes ikke.",
"krav": [
["1", "Hvert møterom med fast utstyr skal ha skjerm og kamera som dekker alle sitteplasser."],
["2", "Skjermen skal kunne vise innhold fra både bærbar klient og videomøte uten omkobling av kabler."],
["3", "Skjerm og kamera skal kunne styres fra ett panel, og oppsettet skal være likt i alle møterom av samme størrelse."]
]
}
]
},
{
"id": "D200",
"title": "Serverrom og infrastruktur",
"scope": "Delen stiller krav til serverrom, strømforsyning, kjøling, lagring, nettverk og overvåking.",
"sections": [
{"nr": "1.1", "title": "Omfang", "subject": "driftsmiljøet", "count": 6},
{"nr": "1.2", "title": "Driftsklasser", "subject": "driftsklassene", "count": 6},
{"nr": "1.3", "title": "Dokumentasjon", "subject": "driftsdokumentasjonen", "count": 6},
{"nr": "2.1", "title": "Plassering", "subject": "serverrommet", "count": 7},
{"nr": "2.2", "title": "Gulv og bæreevne", "subject": "det tekniske gulvet", "count": 5},
{"nr": "2.3", "title": "Adgangskontroll", "subject": "adgangskontrollen til serverrommet", "count": 7},
{"nr": "2.4", "title": "Brannsikring", "subject": "brannslokkeanlegget", "count": 7},
{"nr": "2.5", "title": "Vannlekkasje", "subject": "lekkasjedeteksjonen", "count": 5},
{"nr": "3.1", "title": "Hovedtilførsel", "subject": "hovedtilførselen", "count": 6},
{
"nr": "3.2.1",
"title": "Autonomitid",
"veiledning": "Autonomitiden gjelder UPS-anlegget alene, uten bidrag fra nødstrømsaggregatet.",
"krav": [
["1", "UPS-anlegget skal gi en autonomitid på minst 15 minutter ved full last i driftsklasse 1."],
["2", "Autonomitiden skal være lang nok til at nødstrømsaggregatet kan starte og overta lasten, med minst 5 minutters margin."],
["3", "Autonomitiden skal verifiseres ved belastningstest minst én gang per år."],
["4", "Der nødstrømsaggregat mangler, skal autonomitiden være lang nok til kontrollert nedstenging av alle tjenester i driftsklasse 1 og 2."]
]
},
{"nr": "3.2.2", "title": "Batterier", "subject": "batteribanken", "count": 7},
{"nr": "3.2.3", "title": "Bypass", "subject": "bypass-bryteren", "count": 5},
{"nr": "3.3", "title": "Nødstrømsaggregat", "subject": "nødstrømsaggregatet", "count": 8},
{"nr": "3.4", "title": "Strømfordeling", "subject": "strømfordelingen i rackradene", "count": 7},
{"nr": "3.5", "title": "Jording", "subject": "jordingsanlegget", "count": 5},
{
"nr": "4.1.1",
"title": "Dimensjonering",
"veiledning": "Målt varmelast er grunnlaget. Installert merkeeffekt overvurderer som regel lasten.",
"krav": [
["1", "Kjøleanlegget skal dimensjoneres etter målt varmelast pluss dokumentert vekst i avtaleperioden, ikke etter installert merkeeffekt."],
["2", "Kjølekapasiteten skal ha en reserve på minst 20 % over dimensjonerende varmelast."],
["3", "Dimensjonerende varmelast skal måles over minst fire uker før kapasiteten fastsettes."],
["4", "Kjølekapasiteten skal kunne utvides trinnvis uten at serverrommet må tas ut av drift."],
["5", "Dimensjoneringsgrunnlaget skal dokumenteres og oppdateres når varmelasten endres med mer enn 10 %."]
]
},
{"nr": "4.1.2", "title": "Redundans", "subject": "redundansen i kjøleanlegget", "count": 6},
{"nr": "4.1.3", "title": "Kuldemedium", "subject": "kuldemediet", "count": 5},
{"nr": "4.2.1", "title": "Kaldgang", "subject": "kaldgangen", "count": 6},
{"nr": "4.2.2", "title": "Varmgang", "subject": "varmgangen", "count": 5},
{"nr": "4.2.3", "title": "Blindplater", "subject": "blindplatene i rackskapene", "count": 5},
{"nr": "4.3", "title": "Temperatur og fukt", "subject": "temperaturen og fuktigheten i serverrommet", "count": 7},
{
"nr": "4.4",
"title": "Frikjøling",
"veiledning": "Frikjøling reduserer driften av kompressorer i de kalde delene av året.",
"krav": [
["1", "Frikjøling skal benyttes når utetemperaturen tillater det, og styringen skal skifte automatisk mellom frikjøling og mekanisk kjøling."],
["2", "Frikjølingsanlegget skal ha filter som hindrer støv og fukt i å nå serverrommet."],
["3", "Omkoblingen mellom frikjøling og mekanisk kjøling skal ikke gi temperaturavvik utenfor tillatt område i serverrommet."]
]
},
{"nr": "5.1", "title": "Lagringssystemer", "subject": "lagringssystemene", "count": 8},
{"nr": "5.2.1", "title": "Kopifrekvens", "subject": "sikkerhetskopieringen", "count": 7},
{
"nr": "5.2.2",
"title": "Lagringsklasser",
"veiledning": "En lagringsklasse beskriver hvor raskt en kopi kan hentes fram igjen, ikke hvilket utstyr den ligger på.",
"krav": [
["1", "Sikkerhetskopier skal lagres i minst to lagringsklasser, der minst én er atskilt fra produksjonsmiljøet."],
["1_1", "Sikkerhetskopier eldre enn 90 dager kan flyttes til en lagringsklasse med lengre gjenopprettingstid, dersom gjenopprettingstiden for tjenestens driftsklasse fortsatt overholdes."],
["2", "Lagringsklassen for hver tjeneste skal fremgå av driftsdokumentasjonen."]
]
},
{"nr": "5.2.3", "title": "Gjenoppretting", "subject": "gjenopprettingen fra sikkerhetskopi", "count": 7},
{"nr": "5.3", "title": "Arkivering", "subject": "arkivlagringen", "count": 6},
{"nr": "6.1", "title": "Kjernenett", "subject": "kjernenettet", "count": 7},
{"nr": "6.2", "title": "Kabling", "subject": "kablingen", "count": 7},
{"nr": "6.3", "title": "Merking", "subject": "merkingen av kabler og porter", "count": 5},
{
"nr": "6.4",
"title": "Rackskap",
"veiledning": "Gjenbrukte rackskap omfattes av de samme kravene som nye.",
"krav": [
["1", "Rackskap skal ha dokumentert bæreevne og være forankret til gulv eller vegg."],
["2", "Rackskap skal ha lås, og nøkler skal forvaltes etter rutinen for adgangskontroll."],
["3", "Rackskap som gjenbrukes, skal kontrolleres for skader og bæreevne før montering."],
["4", "Hvert rackskap skal ha to uavhengige strømskinner i driftsklasse 1."]
]
},
{"nr": "7.1", "title": "Overvåking", "subject": "overvåkingssystemet", "count": 8},
{"nr": "7.2", "title": "Alarmer", "subject": "alarmhåndteringen", "count": 7},
{"nr": "7.3", "title": "Vedlikehold", "subject": "vedlikeholdsplanen", "count": 6},
{"nr": "7.4", "title": "Endringer", "subject": "endringsrutinen", "count": 6},
{"nr": "7.5", "title": "Avvik", "subject": "avvikshåndteringen", "count": 6}
]
},
{
"id": "D500",
"title": "Informasjonssikkerhet og kontinuitet",
"scope": "Delen stiller krav til sikkerhetsstyring, tilgang, logging, beredskap og oppfølging av leverandører.",
"sections": [
{"nr": "1.1", "title": "Sikkerhetsstyring", "subject": "styringssystemet for informasjonssikkerhet", "count": 5},
{"nr": "1.2", "title": "Risikovurdering", "subject": "risikovurderingen", "count": 5},
{"nr": "2.1", "title": "Privilegerte kontoer", "subject": "de privilegerte kontoene", "count": 5},
{"nr": "2.2", "title": "Tilgangsgjennomgang", "subject": "tilgangsgjennomgangen", "count": 4},
{"nr": "3.1", "title": "Sikkerhetslogg", "subject": "sikkerhetsloggen", "count": 5},
{"nr": "4.1", "title": "Beredskapsplan", "subject": "beredskapsplanen", "count": 5},
{"nr": "4.2", "title": "Gjenopprettingstid", "subject": "gjenopprettingstiden for kritiske tjenester", "count": 5},
{"nr": "5.1", "title": "Leverandøroppfølging", "subject": "leverandøroppfølgingen", "count": 4}
]
}
]
},
"prosesskatalog": {
"name": "prosesskatalog-2027",
"bundle_id": "eksempel-prosesskatalog-2027",
"catalogue": "P900",
"catalogue_title": "P900 Prosesskatalogen",
"edition": "P900 Prosesskatalogen:2027",
"source_root": "https://example.invalid/eksempelvirksomheten/prosesskatalog",
"scope": "Prosesskatalogen beskriver hva hver prosess i en driftsleveranse omfatter, slik at en avtale kan beskrives og gjøres opp prosess for prosess. Katalogen bærer ingen priser.",
"default_omfang": "Omfatter {obj} i avtaleperioden, med planlegging, gjennomføring, kontroll og dokumentasjon.",
"default_subs": [
{"title": "Etablering av {obj}", "omfang": "Omfatter etablering av {obj} fram til driftsklar tilstand, med konfigurasjon, test og overlevering til drift."},
{"title": "Drift av {obj}", "omfang": "Omfatter drift av {obj} i avtaleperioden. Arbeid som ikke inngår i egne prosesser, forutsettes inkludert i enhetsprisene."},
{"title": "Avvikling av {obj}", "omfang": "Omfatter avvikling av {obj}, med sikker sletting av data og tilbakelevering av utstyr."}
],
"default_subs_for_first": 2,
"main": [
{
"nr": "1",
"title": "Forberedende tiltak og generelle kostnader",
"groups": [
{"nr": "11", "title": "PROSJEKTLEDELSE OG ADMINISTRASJON", "processes": [
{"title": "Prosjektledelse"}, {"title": "Leveransedokumentasjon"}, {"title": "Rapportering"}]},
{"nr": "12", "title": "DRIFTSMILJØ OG GENERELLE DRIFTSKOSTNADER", "processes": [
{
"title": "Midlertidig driftsmiljø",
"omfang": "Omfatter etablering, drift og avvikling av midlertidig driftsmiljø i overgangsperioden, med servere, lagring og nettverk som trengs mens tjenestene flyttes. Omfatter også administrasjon av driftsmiljøet i den grad dette ikke inngår i egne prosesser eller er inkludert i enhetsprisene.",
"subs": [
{"title": "Etablering av midlertidig driftsmiljø", "omfang": "Omfatter oppsett av servere, lagring, nettverk og tilganger for det midlertidige driftsmiljøet fram til driftsklar tilstand."},
{"title": "Drift av midlertidig driftsmiljø", "omfang": "Omfatter overvåking, oppdatering, sikkerhetskopi og brukerstøtte for det midlertidige driftsmiljøet så lenge det er i bruk. Prosessen gjøres opp per måned."},
{"title": "Avvikling av midlertidig driftsmiljø", "omfang": "Omfatter nedstenging av det midlertidige driftsmiljøet, sikker sletting av data og tilbakelevering av utstyr."}
]
},
{"title": "Testmiljø"}, {"title": "Opplæringsmiljø"}]},
{"nr": "13", "title": "KARTLEGGING", "processes": [
{"title": "Kartlegging av tjenester"}, {"title": "Kartlegging av avhengigheter"}, {"title": "Kartlegging av lisenser"}]},
{"nr": "14", "title": "SIKKERHET I PROSJEKTPERIODEN", "processes": [
{"title": "Tilgangsstyring i prosjektet"}, {"title": "Sikkerhetsvurdering"}, {"title": "Beredskap i prosjektperioden"}]},
{"nr": "15", "title": "KVALITETSSIKRING", "processes": [
{"title": "Kvalitetsplan"}, {"title": "Revisjon"}, {"title": "Sluttkontroll"}]}
]
},
{
"nr": "2",
"title": "Migrering og dataflytting",
"groups": [
{"nr": "21", "title": "FORBEREDELSE AV MIGRERING", "processes": [
{"title": "Migreringsplan"}, {"title": "Prøvemigrering"}, {"title": "Datavask"}]},
{"nr": "22", "title": "UTTAK OG FLYTTING", "processes": [
{
"title": "Uttak av maskinvare fra eksisterende miljø",
"omfang": "Omfatter demontering og uttak av servere, disker og nettverksutstyr fra eksisterende miljø, med sikker sletting av data før utstyret flyttes. Utstyr som skal gjenbrukes, skal testes og merkes ved uttak.",
"subs": []
},
{"title": "Flytting av data"}, {"title": "Flytting av tjenester"}]},
{"nr": "23", "title": "VERIFISERING", "processes": [
{"title": "Datakontroll"}, {"title": "Funksjonstest"}, {"title": "Ytelsestest"}]},
{"nr": "24", "title": "PARALLELLDRIFT", "processes": [
{"title": "Parallelldrift"}, {"title": "Tilbakefallsplan"}, {"title": "Avslutning av parallelldrift"}]},
{"nr": "25", "title": "AVVIKLING AV GAMMELT MILJØ", "processes": [
{"title": "Sletting av data"}, {"title": "Tilbakelevering av utstyr"}, {"title": "Gjenvinning av utstyr"}]}
]
},
{
"nr": "3",
"title": "Nettverk og kabling",
"groups": [
{"nr": "31", "title": "KJERNENETT", "processes": [
{"title": "Kjernesvitsjer"}, {"title": "Ruting"}, {"title": "Nettverkssegmentering"}]},
{"nr": "32", "title": "KABLING", "processes": [
{"title": "Horisontal kabling"}, {"title": "Fiberkabling"}, {"title": "Patching"}]},
{"nr": "33", "title": "TRÅDLØST NETT", "processes": [
{"title": "Tilgangspunkter"}, {"title": "Dekningsmåling"}, {"title": "Gjestenett"}]},
{"nr": "34", "title": "FJERNTILGANG", "processes": [
{"title": "Fjerntilgangsløsning"}, {"title": "Tofaktorpålogging"}, {"title": "Tilgangslogging"}]},
{"nr": "35", "title": "NETTVERKSOVERVÅKING", "processes": [
{"title": "Trafikkovervåking"}, {"title": "Kapasitetsrapportering"}, {"title": "Feilsøking"}]}
]
},
{
"nr": "4",
"title": "Klientdrift og brukerstøtte",
"groups": [
{"nr": "41", "title": "KLIENTER", "processes": [
{"title": "Utrulling av klienter"}, {"title": "Klientoppdateringer"}, {"title": "Innsamling av klienter"}]},
{"nr": "42", "title": "PROGRAMVARE", "processes": [
{"title": "Programvarepakking"}, {"title": "Lisensforvaltning"}, {"title": "Programvaredistribusjon"}]},
{"nr": "43", "title": "BRUKERSTØTTE", "processes": [
{"title": "Tjenestedisk"}, {"title": "Feltstøtte"}, {"title": "Opplæring av brukere"}]},
{"nr": "44", "title": "MØTEROM", "processes": [
{"title": "Møteromsutstyr"}, {"title": "Videokonferanse"}, {"title": "Romstyring"}]},
{"nr": "45", "title": "UTSKRIFT", "processes": [
{"title": "Skrivere"}, {"title": "Utskriftskø"}, {"title": "Forbruksmateriell"}]}
]
},
{
"nr": "5",
"title": "Plattform og infrastruktur",
"groups": [
{"nr": "51", "title": "PLATTFORMGRUNNLAG", "processes": [
{
"title": "Standardisering av plattform",
"omfang": "Omfatter samordning av eksisterende servere til én felles plattformversjon, med oppgradering av operativsystem og fastvare, i stedet for utskifting av maskinvaren. Maskinvare som ikke kan standardiseres, skal navngis og behandles i egen prosess.",
"subs": []
},
{"title": "Virtualisering"}, {"title": "Containerplattform"}]},
{"nr": "52", "title": "LAGRINGSLAG OG DISKER", "processes": [
{
"title": "Lagringslag",
"omfang": "Omfatter levering, montering og konfigurasjon av lagringslag for virtuelle maskiner og databaser. Lagringslaget skal ha dokumentert ytelse og redundans før det tas i bruk.",
"subs": [
{"title": "Lagringslag av nye diskhyller", "omfang": "Omfatter levering og montering av nye diskhyller med disker til lagringslaget. Disker skal være iht. produsentens spesifikasjon, avsnitt 1.10.4."},
{"title": "Lagringslag av gjenbrukt maskinvare", "omfang": "Omfatter oppsett av lagringslag av disker og diskhyller tatt ut av eksisterende miljø. Gjenbrukt utstyr skal være testet, slettet og merket ved uttak, og ytelsen skal dokumenteres før bruk."},
{"title": "Drift av lagringslag", "omfang": "Omfatter drift av lagringslaget i avtaleperioden, med kapasitetsoppfølging og utskifting av feilede disker."}
]
},
{"title": "Diskutskifting"}, {"title": "Kapasitetsutvidelse"}]},
{"nr": "53", "title": "SERVERROM", "processes": [
{"title": "Rackskap"}, {"title": "Strømfordeling"}, {"title": "Kjøling av serverrom"}]},
{"nr": "54", "title": "RESERVEKRAFT", "processes": [
{"title": "UPS-anlegg"}, {"title": "Nødstrømsaggregat"}, {"title": "Omkoblingstest"}]},
{"nr": "55", "title": "OVERVÅKING", "processes": [
{"title": "Overvåkingsplattform"}, {"title": "Alarmmottak"}, {"title": "Hendelseslogg"}]}
]
},
{
"nr": "6",
"title": "Lagringssystemer og arkiv",
"groups": [
{"nr": "61", "title": "ARKIVTJENESTER", "processes": [
{"title": "Arkivlagring"}, {"title": "Arkivuttrekk"}, {"title": "Arkivkontroll"}]},
{"nr": "62", "title": "SIKKERHETSKOPI", "processes": [
{"title": "Kopiering"}, {"title": "Kopi utenfor hovedlokasjonen"}, {"title": "Kopikontroll"}]},
{"nr": "63", "title": "GJENOPPRETTING", "processes": [
{"title": "Gjenopprettingstest"}, {"title": "Gjenopprettingsplan"}, {"title": "Fullskala gjenoppretting"}]},
{"nr": "64", "title": "DATASLETTING", "processes": [
{"title": "Sletterutiner"}, {"title": "Sletting av lagringsmedier"}, {"title": "Slettebevis"}]},
{"nr": "65", "title": "LAGRINGSSYSTEMER", "processes": [
{"title": "Blokklagring"}, {"title": "Fillagring"}, {"title": "Objektlagring"}]}
]
}
]
}
}

View file

@ -125,7 +125,7 @@ def seed_store() -> VerdictStore:
"scope_reduction", "scope_reduction",
180_000, 180_000,
"approved", "approved",
"asphalt + base course trimmed within feasible range", "licences + office network ports trimmed within feasible range",
), ),
( (
"V02", "V02",
@ -133,7 +133,7 @@ def seed_store() -> VerdictStore:
"rate_renegotiation", "rate_renegotiation",
60_000, 60_000,
"approved", "approved",
"renegotiated asphalt unit rate", "renegotiated office-suite licence unit price",
), ),
( (
"V03", "V03",
@ -141,7 +141,7 @@ def seed_store() -> VerdictStore:
"material_substitution", "material_substitution",
120_000, 120_000,
"rejected", "rejected",
"granite kerb substitution unsafe", "cheaper monitor model fails the ergonomics spec",
), ),
( (
"V04", "V04",
@ -149,7 +149,7 @@ def seed_store() -> VerdictStore:
"scope_reduction", "scope_reduction",
240_000, 240_000,
"approved", "approved",
"fewer LED masts on low-traffic stretch", "fewer meeting-room screens in low-use rooms",
), ),
( (
"V05", "V05",
@ -157,16 +157,16 @@ def seed_store() -> VerdictStore:
"scope_reduction", "scope_reduction",
350_000, 350_000,
"rejected", "rejected",
"soil replacement is load-bearing, cannot cut", "user-data migration is mandatory, cannot cut",
), ),
("V06", {"21.2"}, "rate_renegotiation", 90_000, "approved", "blasting rate renegotiated"), ("V06", {"21.2"}, "rate_renegotiation", 90_000, "approved", "cabling rate renegotiated"),
( (
"V07", "V07",
{"22.4"}, {"22.4"},
"material_substitution", "material_substitution",
700_000, 700_000,
"rejected", "rejected",
"fiber shotcrete spec is mandated", "access-switch port spec is mandated",
), ),
( (
"V08", "V08",
@ -174,7 +174,7 @@ def seed_store() -> VerdictStore:
"scope_reduction", "scope_reduction",
150_000, 150_000,
"approved", "approved",
"concrete repair area re-measured smaller", "archive clean-up volume re-measured smaller",
), ),
( (
"V09", "V09",
@ -182,7 +182,7 @@ def seed_store() -> VerdictStore:
"material_substitution", "material_substitution",
130_000, 130_000,
"approved", "approved",
"alternative membrane qualified", "alternative backup storage class qualified",
), ),
( (
"V10", "V10",
@ -190,7 +190,7 @@ def seed_store() -> VerdictStore:
"rate_renegotiation", "rate_renegotiation",
200_000, 200_000,
"approved", "approved",
"combined paving rate discount", "combined licence and network rate discount",
), ),
( (
"V11", "V11",
@ -198,7 +198,7 @@ def seed_store() -> VerdictStore:
"scope_reduction", "scope_reduction",
95_000, 95_000,
"rejected", "rejected",
"rigging is fixed cost, no scope to cut", "project management is fixed cost, no scope to cut",
), ),
( (
"V12", "V12",
@ -206,7 +206,7 @@ def seed_store() -> VerdictStore:
"scope_reduction", "scope_reduction",
110_000, 110_000,
"approved", "approved",
"drainage length reduced after survey", "fibre run length reduced after survey",
), ),
] ]
return VerdictStore( return VerdictStore(

View file

@ -2,7 +2,7 @@
type: index type: index
okf_version: 0.1 okf_version: 0.1
title: "Bygg-energi mikro-A — repo-lokal fixture (kryssprosjekt-læring)" title: "Bygg-energi mikro-A — repo-lokal fixture (kryssprosjekt-læring)"
description: "Minimal repo-lokal OKF-bundle under data/ for S2.0 kryssprosjekt-lærings-tester. Backer prosjekt k+1 i road-k + bundle-k+1-topologien, så dens bundle_dir-gatede Step-1 ExpeL-fold fyrer." description: "Minimal repo-lokal OKF-bundle under data/ for S2.0 kryssprosjekt-lærings-tester. Backer prosjekt k+1 i ref-k + bundle-k+1-topologien, så dens bundle_dir-gatede Step-1 ExpeL-fold fyrer."
tags: [fixture, S2.0, kryssprosjekt-laering] tags: [fixture, S2.0, kryssprosjekt-laering]
timestamp: 2026-07-15 timestamp: 2026-07-15
--- ---

View file

@ -1,5 +1,5 @@
{ {
"_note": "SYNTHETIC repo-local mini-bundle IR-projeksjon (Fase 2a S2.0-fixture) — ikke ekte data. Backer prosjekt k+1 i road-k + bundle-k+1-topologien; project_id matcher det bundle-backede prosjektets id (run._project_from_bundle fail-faster ved mismatch).", "_note": "SYNTHETIC repo-local mini-bundle IR-projeksjon (Fase 2a S2.0-fixture) — ikke ekte data. Backer prosjekt k+1 i ref-k + bundle-k+1-topologien; project_id matcher det bundle-backede prosjektets id (run._project_from_bundle fail-faster ved mismatch).",
"project_id": "BYGG-ENERGI-MIKRO-A", "project_id": "BYGG-ENERGI-MIKRO-A",
"measure": "LED-retrofit av 120 lysrorarmaturer i kontorflA (90 W -> 40 W)", "measure": "LED-retrofit av 120 lysrorarmaturer i kontorflA (90 W -> 40 W)",
"affected_items": [ "affected_items": [

View file

@ -0,0 +1,10 @@
{
"_note": "Prosjektets kostdata for DRIFTSSENTER-KJOLING, i det formatet den konsumerende implementasjonen definerer (dens akse - bundelen normerer ikke dette formatet). Raden er driftssenterets arlige energikostnad: hovedkjoling 60 kW x 2/3 x 4 500 t = 180 000 kWh/ar, lavlast-/utlopssone 21 kW x (4 500 t x 1,00 + 4 260 t x 0,50) = 139 230 kWh/ar, ovrige tekniske anlegg 16 020 kWh/ar, sum 335 250 kWh/ar a 1,00 NOK/kWh eks. mva. quantity og unit_cost er BYTE-IDENTISKE med affected_items-raden i validator-input.json fordi begge filene er skrevet fra denne ene summen - 5 %-toleransen er lukket ved konstruksjon, ikke ved avstemming. Investeringskostnad er BEVISST utelatt: eiendomsavdelingens prisbok (publikasjon 4, fiktiv) gir 1 000-3 000 NOK per m2 for kjoleanlegg, men publikasjonen er UDATERT (et belop uten arstall kan ikke prisjusteres) og prisen dekker HELE kjoleanlegget, mens tiltaket bytter bare styringen. En utledet verdi horer ikke hjemme i en kostbase. Se driftssenter-kjoling.md og tiltak-trinnstyring-kjoling.md.",
"project_id": "DRIFTSSENTER-KJOLING",
"items": {
"ENERGI-DRIFTSSENTER-EL": {
"quantity": 335250,
"unit_cost": 1.0
}
}
}

View file

@ -0,0 +1,98 @@
---
type: project
title: "Driftssenteret — kjøling"
description: "Fiktivt driftssenter med to serverhaller à 2 400 m², driftsklasse 80, IT-last under 4 000 kWh/døgn. Energibaseline for kjølingen sone for sone, og rammene kjølekravene setter."
resource: DRIFTSSENTER-KJOLING
tags: [driftssenter, kjoling, hovedkjoling, energibaseline, DS-09, B-500]
timestamp: 2026-08-09
---
# Driftssenteret (DRIFTSSENTER-KJOLING)
**Fiktivt anlegg.** Tallene er illustrative, og geometrien og kjølekravene er hentet fra den
oppdiktede Eksempelvirksomhetens egne standarder med årstall. En produksjons-deployer erstatter
dette laget med sin egen anleggsdatabase.
Driftssenteret har **to serverhaller à 2 400 m², hver med egen kjølekrets**, i **driftsklasse
80**, med **IT-last(10) under 4 000 kWh/døgn**. Geometrien er ikke tilfeldig: den er valgt slik
at den faller innenfor referanseanlegget konsernprosjektet ENTR D2.1 modellerer på
(`>500 m², 2 haller, 2 kretser`), slik at det ene eksterne kryss-sjekk-tallet vi har, faktisk
gjelder samme anleggstype. Se [kilder-kjoling-realisering.md](kilder-kjoling-realisering.md).
## Soneinndeling
Driftsstandard DS-09 (2021) § 9.2: «Kjøleteknisk sett inndeles en serverhall i høylastsone,
overgangssone, lavlastsone og utløpssone». Høylastsonens dybde er lik avstanden fra
kjøleaggregatet til målepunktet for utetemperaturen — **99 m² ved driftsklasse 80** (DS-09
tabell 9.1).
| Sone | Utstrekning | Merknad |
|---|---|---|
| Høylast- + overgangssone («hovedkjølingen») | **300 m² per hall**, 2 haller = 600 m² | [I] høylastsone 99 m² [V] + overgangssone; DS-09 § 9.6.1 styrer dem som **ett** objekt |
| Lavlastsone + utløpssone | 2 100 m² per hall, 2 haller = 4 200 m² | beregnet: 2 400 − 300 |
**Hovedkjølingen er den eneste sonen som er utetemperaturavhengig,** og derfor den eneste der en
styringsforbedring kan hente energi. Det er også der nesten all installert effekt sitter.
## Energibaseline
| Størrelse | Verdi | Merknad |
|---|---|---|
| Kjølemoduler, hovedkjøling | 300 à **200 W** = **60 kW** | [I] illustrativt (én rad per hall, ca. hver 4. rackplass i to rekker) |
| Viftemoduler, lavlast-/utløpssone | 350 à **60 W** = **21 kW** | [I] illustrativt (ca. hver 12. rackplass) |
| Timer høylasttrinn aktivt | **4 500 t/år** | [I-avledet] se «Om de 4 500 timene» under |
| Timer natt-/lavlastdrift | 4 260 t/år | beregnet: 8 760 − 4 500 |
| **Hovedkjøling, slik den drives i dag (3-trinn)** | **180 000 kWh/år** | beregnet: 60 kW × 2/3 × 4 500 t |
| **Lavlastsone + utløpssone** | **139 230 kWh/år** | beregnet: 21 kW × (4 500 t × 1,00 + 4 260 t × 0,50) |
| **Øvrige tekniske anlegg** | **16 020 kWh/år** | [I] pumper, adgangskontroll, nødlys, SD-anlegg, UPS-tap, periodisk avfuktingsdrift |
| **TOTALT ELFORBRUK** | **335 250 kWh/år** | beregnet: sum |
| Variabel energikostnad | **1,00 NOK/kWh** ekskl. mva | [V-forankret] kraftpris + nettleie energiledd + elavgift |
| **Total årlig energikostnad** | **335 250 NOK/år** | beregnet |
Faktoren **2/3** på hovedkjølingen er ikke en reguleringsinnstilling — det er **midlere servert
nivå** for et 3-trinns kontaktorstyrt anlegg. Utledningen står i
[tiltak-trinnstyring-kjoling.md](tiltak-trinnstyring-kjoling.md); det er
nettopp den faktoren tiltaket angriper.
Nattnivået i lavlastsonen er satt til 50 % fordi DS-09 tabell 9.4 halverer kravet: 1,00 dag
mot 0,50 natt (relativ kjøleleveranse) for denne last- og driftsklassen.
### Om de 4 500 timene — og hvorfor de er merket [I], ikke [V]
Høylasttrinnet er aktivt når utetemperaturen ligger over frikjølingsgrensen. I virksomhetens
klimasone er det omtrent halve året, altså ≈ **4 380 t/år**, og sikkerhetsmarginen rundt
grensen der hovedkjølingen fortsatt trenger forhøyet nivå ligger oppå det. **4 500 t/år er valgt
innenfor det båndet.**
Valget er ikke nøytralt, og det skal stå: det er tatt slik at
`realiseringsgrad × modellert besparelse` **lukker i heltall**. Det er samme konvensjon som
klientpark-bundelen brukte da den valgte antall arbeidsstasjoner, og den hører hjemme i teksten,
ikke i en fotnote. **Ingen kilde i materialet gir en målt timekurve for høylasttrinnet i
driftssenteret.**
## Rammer (constraints)
- **Tilluftmengden i høylastsonen skal ikke være under 50 m³/s** (DS-09 tabell 9.4, merknad;
normativ kilde Byggstandard B-500 «Tekniske rom»). Det er et hardt gulv — ingen besparelse kan
hentes under det.
- **Kjølekrav i høylastsonen = 3,00 % av IT-lasten per grad over frikjølingsgrensen** for
IT-last(10) < 4 000 i driftsklasse 80 (DS-09 tabell 9.4). Nivået er altså ikke fast, men
**følger T20, utetemperaturen ved luftinntaket** — det er hele grunnen til at sonen kan
trappes ned, og hele grunnen til at gevinsten avhenger av hvor godt styringen følger kurven.
- **Utetemperaturen skal kontinuerlig måles med kalibrert temperaturmåler** (DS-09 § 9.6,
normativ kilde B-500). Måleren finnes altså allerede — men den måler **inngangssignalet**, ikke
energien. Se [metode-ipmvp-a.md](metode-ipmvp-a.md).
- **Hysteresetid minimum 60 sekunder** ved nivåendringer (DS-09 § 9.6.1). Den er et
driftssikkerhetskrav, og den koster energi. Den er ikke valgfri, og tiltaket kan ikke regne den
bort.
- Lavlastsonen kan halveres etter 60 minutters stabil drift i store haller, dog ikke under
1,00 på dagtid (DS-09 tabell 9.4, merknad). **Ikke modellert som besparelse her** — om
driftssenteret kvalifiserer som «svært stort» er en vurdering kilden ikke avgjør for oss.
- Budsjett og anskaffelsesrammer eies av deployer; her holdes de minimale.
## Kandidat-tiltak
- [tiltak-trinnstyring-kjoling.md](tiltak-trinnstyring-kjoling.md) —
oppgradering fra 3-trinns kontaktorstyring til 13-trinns styring av hovedkjølingen.
- [tiltak-kaldgangsinnkapsling.md](tiltak-kaldgangsinnkapsling.md) — passiv
kaldgangsinnkapsling som senker varmetilskuddet og dermed kravet i høylastsonen.

View file

@ -0,0 +1,91 @@
---
type: index
okf_version: 0.1
title: "Driftssenteret — trinnstyring av hovedkjølingen og kaldgangsinnkapsling"
description: "OKF-bundle for kjølingen i et fiktivt driftssenter med to kandidat-tiltak: oppgradering fra 3-trinns til 13-trinns styring av hovedkjølingen, og passiv kaldgangsinnkapsling. Bygget rundt et gap som oppstår i drift, ikke i parameterne — og rundt fire premisser fra forarbeidet som ble målt feil."
tags: [energieffektivisering, driftssenter, kjoling, kjolestyring, M&V, IPMVP, realiseringsgrad]
timestamp: 2026-08-09
---
# Driftssenteret
En OKF-bundle for **kjølingen i Eksempelvirksomhetens driftssenter**: ett anlegg, to
kandidat-tiltak. Den deler lærings-overflate med klientpark- og bygg-bundlene, men står på egne
ben: metode- og kildelaget er **materialisert inn her**, ikke lenket på tvers av bundler.
> Framework-nøytral artefakt (null kode-avhengighet). Den bor i pakkens `data/bundles/`, ikke i
> den pull-only `shared/`-subtreet.
**Både prosjektlaget og litteraturlaget er fiktive.** Driftssenteret tilhører den oppdiktede
«Eksempelvirksomheten»; geometrien, sonekravene og trinnrekkene det er bygget av er hentet fra
virksomhetens egne — like oppdiktede — standarder og rapporter med årstall, merket `[V]` der de i
fortellingen er verifisert. Ingen ekte organisasjon, publikasjon eller person er sitert. En
produksjons-deployer erstatter begge lagene med en ekte kunnskapsbase og ekte kilder.
## Hvorfor kjøling
Domenet ble valgt fordi gapet mellom modellert og realisert besparelse her har **en annen
årsak** enn i de to andre bundlene — og en lærings-sløyfe som bare har sett én årsak, har
ikke lært noe generelt.
I kontorbygget og i klientparken er gapet en **parameterfeil**: driftstimene var
overvurdert. Anlegget gjorde det det skulle; tallet vi matet inn var galt.
Her er parameterne kjent og modellen aritmetisk lukket. Gapet oppstår **i drift**: en
hysterese standarden krever, en variabel soneutstrekning standarden ber om å få implementert, og
en kalibreringsmargin ingen driftsorganisasjon setter for lavt. Utstyret kan levere; anlegget
gjør det ikke. Derfor bærer frøet `gap_source: control-tracking-overestimation` og ikke
`hours-of-use-overestimation` — se [verdict-trinnstyring-fro.md](verdict-trinnstyring-fro.md).
## ⛔ Fire premisser fra forarbeidet som ble målt feil
Bundelen ble bestilt på antakelsen om at driftssenteret hadde et **ekte eget ex-post-par** og
dermed ikke trengte å låne sin realiseringsgrad slik klientpark-bundelen måtte. **Den antakelsen
holdt ikke.** Tallene fra konsernprosjektet står under `MODEL INPUTS` og er modellerte, ikke
målte; de gjelder søsterselskapets referanseanlegg, og Eksempelvirksomheten er medfinansiør av
prosjektet, ikke datakilde; og prosjektlogg-sitatet om vifter på full hastighet gjelder
byggefasen, ikke drift.
**Driftssenteret låner altså også sin rate.** Fullstendig oppgjør i
[kilder-kjoling-realisering.md](kilder-kjoling-realisering.md).
Det som faktisk skiller denne bundelen fra klientpark-bundelen er tre andre ting: **geometrien
og kravene er virksomhetens egne, daterte og normative** (Driftsstandard DS-09, april 2021, som
beskriver tiltaket ved navn), **gap-mekanismen er en annen**, og **M&V-asymmetrien er omvendt** —
her åpner ex-post seg i det tiltaket settes i drift, mens ex-ante lukket seg da anlegget ble
bygget.
## Innhold (progressiv disclosure)
- [driftssenter-kjoling.md](driftssenter-kjoling.md) — `type: project` — anlegget,
soneinndelingen, energibaselinen og rammene kjølekravene setter.
- [tiltak-trinnstyring-kjoling.md](tiltak-trinnstyring-kjoling.md) —
`type: hypothesis` — kandidat-tiltak 1: fra 3-trinns kontaktorstyring til 13-trinns
styring av hovedkjølingen. **Det er dette tiltaket som er projisert inn i validatoren.**
- [tiltak-kaldgangsinnkapsling.md](tiltak-kaldgangsinnkapsling.md) — `type: hypothesis` —
kandidat-tiltak 2: passiv innkapsling som senker varmetilskuddet og dermed selve kravet. Høyere
modellert besparelse, langt høyere investering, og **ingenting som kan overstyres** — derfor en
kontrast, ikke en dom.
- [metode-ipmvp-a.md](metode-ipmvp-a.md) — `type: methodology` — M&V-metoden (IPMVP Option A),
og baseline-asymmetrien som stenger Option B bakover i tid.
- [kilder-kjoling-realisering.md](kilder-kjoling-realisering.md) —
`type: reference` — virksomhetens (fiktive) kilder, de fire korrigerte premissene, og
metastudien realiseringsgraden er lånt fra.
- [verdict-trinnstyring-fro.md](verdict-trinnstyring-fro.md) — `type: verdict` — frøsatt
ekspert-dom. **ExpeL-frøet loopens steg 1 henter fra.**
## Hvordan den kjøres i dag
`validator-input.json` er IR-projeksjonen den eksisterende deterministiske validatoren
konsumerer uendret; `cost-baseline.json` bærer det samme tallgrunnlaget som prosjektets
kostdata. **De to filene er bygget fra samme linje aritmetikk og bærer identisk `code`,
`quantity` og `unit_cost`** — se
[tiltak-trinnstyring-kjoling.md](tiltak-trinnstyring-kjoling.md),
§«Mapping til validatoren».
Bundelen ships **uten `golden.json`**, av samme grunn som klientpark-bundelen: den blokken er
kryss-implementasjons-fasit produsert av en seedet Monte Carlo, og det finnes ingen kjørbar
pipeline å produsere den med. En fasit ingen gate leser er verre enn ingen fasit.
Lærings-overflaten går ikke tapt: de strukturerte feltene ExpeL-folden faktisk henter
(`realization_rate`, `expected_actual_saving_nok`) ligger i frontmatteren til
[verdict-trinnstyring-fro.md](verdict-trinnstyring-fro.md), som er der loopen leser dem.

View file

@ -0,0 +1,223 @@
---
type: reference
title: "Kilder: kjøling, kjølestyring og realisering av styringsbesparelser"
description: "Virksomhetens (fiktive) kilder bak driftssenter-bundelen. Egne, daterte standarder for geometri og krav; konsernprosjektets modellerte tall for kryss-sjekk; og metastudien realiseringsgraden er lånt fra. Fører også de fire premissene som ble målt FEIL i forarbeidet."
resource: DRIFTSSENTER-KJOLING
tags: [kilder, DS-09, B-500, ENTR, prisbok, MS-2011, IPMVP, provenienss]
timestamp: 2026-08-09
---
# Kilder
**Alle kildene i denne fila er fiktive.** De er dokumenter i den oppdiktede
Eksempelvirksomheten og dens konsern — standarder, prisbøker, prosjektlogger og rapporter — og
ingen av dem finnes utenfor denne bundelen. Ingen ekte organisasjon, publikasjon eller person er
sitert.
Konvensjonen er den samme som i de øvrige bundlene: **`[V]` verifisert mot virksomhetens eget
dokument, `[V-forankret]` utledet av en verifisert verdi, `[I]` illustrativt, `[U]`
uverifisert.** Metode- og kildelaget er **materialisert inn i denne bundelen** — ingen lenker
til andre bundler.
---
## ⛔ FIRE PREMISSER SOM BLE MÅLT FEIL — og som står korrigert her
Forarbeidet til denne bundelen bar fire påstander som **ikke holdt** da dokumentene ble hentet.
De føres her fordi en kildeliste som bare viser det som overlevde, skjuler hvordan den ble til.
**1. ENTR-paret `150 059 → 33 114 kWh/år` er IKKE en måling.**
Tallene står i D2.1 under overskriften **`MODEL INPUTS`**, som «Før tiltak» og
«Etter tiltak», og resultatlinjen heter **`ASSESSMENT RESULTS: Energy saving potential
116,945 kWh/year`**. Det er et **modellert ex-ante-anslag for et generisk referanseanlegg**
(`>500 m², 2 haller, 2 kretser`), ikke et ex-post-par fra et virkelig anlegg.
**2. ENTR-tallene er IKKE driftssenterets egne.**
D2.1 er skrevet av fire rådgivere i søsterselskapene og to innleide konsulenter (navnene er
utelatt her). **Eksempelvirksomheten er medfinansiør** av konsernprosjektet sammen med fem
søsterselskaper — ikke datakilde. Utbredelsen oppgis som «ca. 10 % i konsernet».
**3. Prosjektloggens «viftene på full hastighet» gjelder BYGGEFASEN.**
Sitatet — «Viftene ble holdt på full hastighet mesteparten av tiden, så sparepotensialet ble
ikke realisert» — står i avsnittet om **Bygg 2 under oppføring**, om byggtørking og
støvavsug mens hallen ble reist. Det er byggventilasjon på en byggeplass, **ikke
temperaturstyrt kjøling i et driftssenter i drift**. Årsaken er heller ikke den samme: på
byggeplassen kjøres full hastighet for arbeidsmiljø og framdrift. **Mekanismen er derfor ikke
båret over til driftsfasen i denne bundelen.**
**4. `≈ €400 000 per hall` er kostnaden for kaldgangsinnkapsling**, ikke for «kjøling ved
hallinngang».
**Konsekvensen for bundelen, uttalt:** driftssenteret har **ingen egen ex-post-måling** av en
realiseringsgrad. Raten er **lånt**, akkurat som i klientpark-bundelen, og lånet er merket i
`provenance`. Det som skiller denne bundelen fra klientparken er ikke en egen måling — det er at
**geometrien og kravene er virksomhetens egne, daterte og normative**, og at gap-mekanismen er
en annen.
---
## Virksomhetens egne standarder [V]
### Eksempelvirksomheten, Driftsstandard DS-09 — «Teknisk planlegging av kjøling i tekniske rom»
**Veiledning, eiendomsavdelingen, april 2021.** Internt dokument, ikke publisert.
Dette er bundelens viktigste kilde. Den er intern, datert, normativ i virksomheten — og den
beskriver tiltaket vårt ved navn.
| Ankeret | Ordrett / verdi | Sted |
|---|---|---|
| Soneinndeling | «Kjøleteknisk sett inndeles en serverhall i høylastsone, overgangssone, lavlastsone og utløpssone» | § 9.2 |
| Høylastsonens dybde | **99 m² ved driftsklasse 80** (avstand aggregat → målepunkt for utetemperatur) | tabell 9.1 |
| Krav høylastsone dag | **3,00 %** av IT-lasten per grad over frikjølingsgrensen (IT-last(10) < 4 000, driftsklasse 80) | tabell 9.4 |
| Krav lavlastsone | 1,00 dag, 0,50 natt og kl. 00–05 (relativ kjøleleveranse, samme klasse) | tabell 9.4 |
| Hardt gulv | «Tilluftmengden i høylastsonen skal ikke være under 50 m³/s» | tabell 9.4, merknad |
| Kontinuerlig måling | «Utetemperaturen for kjøling i høylast- og overgangssonene **skal kontinuerlig måles** ved bruk av kalibrert temperaturmåler» *(normativ kilde: B-500)* | § 9.6 |
| Dagens praksis | «I utførelse har dette vært begrenset til **3 trinn** arrangert med oppdeling i kurser styrt via kontaktorer» | § 9.6.1 |
| Anbefalt tiltak | «Det anbefales å definere høylast-/overgangssonen i **13 trinn** henholdsvis **0-5-10-15-20-25-30-40-50-60-70-80-90-100 %** alternativt dynamisk» | § 9.6.1 |
| Hysterese | «Det bør som minimum legges til en **hysteresetid på 60 sekunder** for endringer i nivåene» | § 9.6.1 |
| Restforutsetning | «Ved varierende trinn vil også **utstrekningen av høylastsonen variere**, og dette er viktig å få implementert for å utnytte energisparepotensialet mest mulig» | § 9.6.1 |
| Energisynlighet ved frekvensstyring | «Måling av motorstrøm vil i tillegg gi mulighet for å følge med i aggregatets energiforbruk, samt innstilt nivå ved behovsstyrt regulering» | § 5.2, pkt. 3 |
| Varmereduserende grep | luftinntak i skygge, skjermende vegetasjon, kaldgangsinnkapsling, blindplater, lyse flater på tak og yttervegger | § 9.2.1 |
**Ett anker til, fra IT-driftshåndboken, som gjelder klientparken og ikke driftssenteret — men
som er verdt å notere presist:** håndboken sier at eldre arbeidsstasjoner **«i
regionkontorene»** er «vanligvis umålte, og energikostnadene blir beregnet ut fra et bestemt
antall brukstimer per år (4 000 – 4 100)». De 4 000–4 100 er altså **en avregningskonvensjon
for umålt utstyr**, ikke en målt driftstimekurve — og teksten avgrenser dem til
**regionkontorene**. Det er en presisering mot hvordan tallet ellers siteres.
### Eksempelvirksomheten, Byggstandard B-500 «Tekniske rom» [V — sekundært]
Normativ kilde for kjølekravene DS-09 gjengir. Sitert her via DS-09s egne
marginhenvisninger, ikke hentet direkte.
---
## Interne kostnads- og anleggsdata [V, men udatert]
### Eiendomsavdelingens prisbok, publikasjon 4
Internt dokument, ikke publisert.
| Verdi | Ordrett |
|---|---|
| Andel rom med egen kjøling | «Bare 5 % av våre tekniske rom har egen kjøling (20 % av samlet areal)» |
| Enhetspris kjøling | «For serverhaller større enn ca. 300 m² kan gjennomsnittsprisen per m² variere mellom **NOK 1000 og NOK 3000**. (Prisen inkluderer aggregater, kabelbroer, installasjon av trafo og nettilknytning)» |
**⚠️ Prisen er IKKE brukt i `cost-baseline.json`, og grunnen skal stå:** publikasjonen er
**udatert** i vårt uttrekk (den omtaler «mer enn 700 tekniske rom i virksomheten», et tall
virksomheten passerte for mange år siden), og et beløp uten årstall kan ikke prisjusteres. Den
dekker dessuten **hele kjøleanlegget** per m², mens vårt tiltak bytter **bare styringen**.
Å skalere den ned til en styringsandel ville vært å produsere et tall og kalle det et anker.
### Prosjektlogg, publikasjon 13
Internt dokument, ikke publisert.
Brukt **kun** som korreksjon (se punkt 3 øverst). Beskriver byggventilasjon under oppføringen
av Bygg 1 og Bygg 2: to vifter à 230–250 kW, ca. 100 m³/s, PLS-styring på støv og lufttrykk.
**Ingen av tallene er brukt i bundelen.**
---
## Konsernets modellerte tall (kryss-sjekk) [V som modell, ikke som måling]
### Konsernprosjektet ENTR, leveranse D2.1 — «Vurdering av tiltak med potensial for energireduksjon»
**Leveranse 2.1, februar 2015.** Konsernprosjektet ENTR (energi i tekniske rom), finansiert av
Eksempelvirksomheten og fem søsterselskaper. Internt dokument, ikke publisert.
Referanseanlegg for begge tiltak: **`>500 m², 2 haller, 2 kretser`**.
| Tiltak | Før | Etter | Reduksjon | Kostnad | Utbredelse |
|---|---|---|---|---|---|
| Innkapsling/skjermer ved rackradene (senker varmetilskuddet) | 150 059 kWh/år | 33 114 kWh/år | **77,9 %** | «ca. €400k per hall» | «ca. 10 % i konsernet» |
| Frekvensstyrt kjøling med «lukket» tilbakekobling | 158 059 kWh/år | 136 893 kWh/år | **13,4 %** | «ca. €35k per kjølekrets» | «ca. 15 % (mest i to søsterselskaper)» |
**⚠️ Felle i kilden:** de to tiltakene oppgir **ulik** baseline før tiltak for nominelt
samme referanseanlegg — **150 059** mot **158 059**. Baselinen er ikke felles på tvers av
tiltakene i D2.1, og de to radene kan ikke settes i samme regnestykke. Bundelen setter dem
ikke sammen.
---
## Realiseringsgraden — hvor den er lånt fra [V, men LÅNT]
### Eksempelvirksomheten, internrapport MS-2011 — «Metaanalyse av energibesparelser fra styring i egne kontorbygg»
Energiavdelingens analysegruppe (navnene er utelatt her). **September 2011.** Internt dokument,
ikke publisert.
**240 besparelsesanslag fra 88 prosjektrapporter og case**, sortert på styringsstrategi og
deretter filtrert suksessivt for å avdekke skjevheter i analysemetoden.
For **utetemperaturstyrt regulering** — styring som følger forholdene ute, som er nøyaktig
strategien i vårt tiltak:
| Filter | Gjennomsnittlig besparelse | n |
|---|---|---|
| Kun styringstiltak | 39 % | 73 |
| Kun energi til det styrte utstyret | 39 % | 73 |
| **Kun faktiske installasjoner** | **28 %** | **32** |
Rapportens egne konklusjoner, ordrett:
> «de beste anslagene på gjennomsnittlig sparepotensial er 24 % for tilstedeværelsesstyring,
> **28 % for utetemperaturstyring**, 31 % for individuell innstilling, 36 % for sentral
> innstilling og 38 % for kombinasjoner»
> «Resultatene tyder på at **simuleringer overvurderer betydelig (med minst 10 %) den
> gjennomsnittlige besparelsen utetemperaturstyring gir i faktiske bygg.**»
> «energibeslutninger og besparelsesanslag bør ikke bygge på simuleringer alene, men bør
> inkludere feltmåling eller i det minste **nedjustering av besparelser predikert fra
> simuleringer**»
**Forholdet 28 / 39 = 0,718** er ankeret realiseringsgraden **0,72** er lånt fra.
**Hva lånet IKKE er, og det må stå like tydelig som hva det er:**
- Det er **ikke** en prosjekt-realiseringsgrad (målt ÷ predikert for de samme prosjektene).
Det er forholdet mellom **to filtrerte populasjonsgjennomsnitt** i samme metastudie — anslag
som inkluderer simuleringer, mot anslag fra faktiske installasjoner. Antallet faller fra
73 til 32 mellom de to.
- Det gjelder **kontorbygg**, ikke serverhaller. Utetemperaturstyring av ventilasjonen i et
kontorlokale og av hovedkjølingen i et driftssenter deler mekanisme og feilmodus, men ikke
geometri, krav eller driftsorganisasjon.
- Det er **fra 2011**.
Lånet er valgt fordi det er den nærmeste treffende kilden vi har: **samme styringsstrategi**
(utetemperaturstyrt regulering), og et eksplisitt, tallfestet funn om at modellerte anslag
ligger over det faktiske installasjoner leverer. **Det finnes ingen ex-post-evaluering av
realiseringsgrad for kjølestyring i driftssenteret i materialet vårt.**
---
## Metoderammeverk [V]
### IPMVP
**International Performance Measurement and Verification Protocol**, et utbredt
metoderammeverk. De fire opsjonene (A/B/C/D) og utgangspunktet om at besparelse er fravær av
energibruk er gjengitt i [metode-ipmvp-a.md](metode-ipmvp-a.md).
### Virksomhetens M&V-veileder — måleterskel
Veiledningen om at en besparelse bør overstige **~10 % av baseline** for å skilles pålitelig
fra støy i en hovedmåler. Brukt i [metode-ipmvp-a.md](metode-ipmvp-a.md).
---
## Kilder som er vurdert og IKKE brukt
- **Et konferanseinnlegg om energisparing i driftssentre (i virksomhetens arkiv)** — oppga
236–453 MWh/år for et helt anlegg. Uttrekk feilet (arkivet utilgjengelig), tallet er udatert,
og bundelen bygger sin egen baseline fra parametere. **Ikke brukt.**
- **En masteroppgave i virksomhetens arkiv** — «opptil 40 %» modellert for adaptiv
ventilasjonsstyring i kontorlokaler. Kontorventilasjon er ikke et tiltak i denne bundelen.
**Ikke brukt.**
- **En leverandørs referansecase fra et annet driftssenter** — relevant anleggstype, men
leverandørkilde. **Ikke brukt.**
- **Et britisk prisanslag i materialet** — £1 000 per 50 m² for ettermontert trinnstyring.
Udatert i materialet og gjelder et annet marked. **Ikke brukt.**

View file

@ -0,0 +1,98 @@
---
type: methodology
title: "IPMVP Option A for kjølestyring — anlegget måler inngangssignalet, ikke energien"
description: "M&V-metoden for å verifisere besparelsen fra en styringsoppgradering i driftssenteret. Option A er valgt fordi baselinen ikke kan måles i etterkant — ikke fordi måling mangler. Tiltaket installerer selv den målingen som ville gjort Option B mulig, ett år for sent."
methodology: IPMVP
option: A
tags: [IPMVP, M&V, retrofit-isolation, kjoling, baseline-asymmetri]
timestamp: 2026-08-09
---
# M&V-metode: IPMVP Option A for en styringsoppgradering
**IPMVP** (International Performance Measurement and Verification Protocol) er et utbredt
metoderammeverk for å måle og verifisere energibesparelser. Kjerneinnsikten som begrunner hele
lærings-sløyfa er metodens eget utgangspunkt: besparelse kan ikke måles direkte, fordi den er
**fravær** av energibruk.
Besparelse er en **kontrafaktisk** størrelse — det finnes ingen måler for «det som ikke ble
brukt». Den *beregnes*: `Baseline-energi − Rapporterings-energi ± justeringer` (metodens
grunnligning).
## De fire opsjonene (metodens egne navn)
- **Option A — Retrofit Isolation: Key Parameter Measurement.** Måler nøkkelparameteren på det
berørte utstyret; øvrige parametere *estimeres*.
- **Option B — Retrofit Isolation: All Parameter Measurement.** Måler alle relevante parametere.
- **Option C — Whole Facility.** Besparelse fra anleggets hovedmåler, med rutinejustering.
- **Option D — Calibrated Simulation.** Besparelse via simuleringsmodell kalibrert mot måledata.
## Asymmetrien som avgjør valget
Driftssenteret er **ikke** et umålt anlegg. DS-09 § 9.6 stiller et normativt krav:
> «Utetemperaturen for kjøling i høylast- og overgangssonene skal kontinuerlig måles ved bruk
> av kalibrert temperaturmåler.» *(normativ kilde: Byggstandard B-500)*
Anlegget måler altså **kontinuerlig** — men det måler **inngangssignalet** (T20 ved
luftinntaket), ikke energien. Og det er nettopp den forskjellen som stenger opsjonene:
- **Option C er stengt av oppløsning, ikke av målermangel.** Driftssenteret har hovedmåler, men
kjølingen er 95 % av forbruket sammen med lavlastsonen, pumper og øvrige anlegg på samme
linje. Et tiltak på **10,00 %** av totalen skal skilles fra sesongvariasjon i pumpedrift og
avfukting på den samme måleren. Signalet drukner ikke helt — men det er ikke et rent kutt.
- **Option D er stengt av kalibreringsdata.** En simulering av hovedkjølingen må kalibreres mot
en målt T20-fordeling over året. Temperaturmåleren produserer den dataen **i sanntid for
styringsformål**, men ingen kilde i materialet dokumenterer at den **logges og lagres**.
Uten historikk finnes det ingenting å kalibrere mot.
- **Option B er stengt bakover i tid, ikke framover.** Og det er den interessante.
## Baseline-asymmetrien
Nøkkelparameteren for dette tiltaket er **midlere servert nivå over året** — hvor høyt
styringen faktisk legger seg i forhold til kjølekurven.
- **Etter tiltaket kan den måles.** DS-09 § 5.2 punkt 3 beskriver det selv: med
frekvensomformere gir «Måling av motorstrøm (…) i tillegg mulighet for å følge med i
aggregatets energiforbruk, samt innstilt nivå ved behovsstyrt regulering».
- **Før tiltaket kan den ikke måles.** Dagens 3-trinns kontaktorstyring kobler kurser av og på.
Den har ingen omformer som rapporterer nivå, og den logger ikke hvilket trinn som sto inne når.
**Tiltaket installerer altså selv den målingen som ville gjort Option B mulig — ett år for
sent til å måle sin egen baseline.** Det er ikke en svakhet ved dette anlegget; det er den
normale formen på en styringsoppgradering, og grunnen til at baselinen for slike tiltak nesten
alltid er **stipulert**.
**Kontrast verdt å merke seg:** i en umålt klientpark er ex-post *permanent* stengt. Her er
det motsatt — ex-post åpner seg i det tiltaket settes i drift, men ex-ante lukket seg da
anlegget ble bygget. Realiseringsgapet overlever begge veier, av motsatte grunner.
## Der metoden lekker: den estimerte parameteren
Option A måler det som er billig og presist (installert effekt per trinn) og **stipulerer
hvordan nivået fordeler seg over året**. For dette tiltaket er stipulatet svakt på et bestemt
punkt:
Modellen i [tiltak-trinnstyring-kjoling.md](tiltak-trinnstyring-kjoling.md)
antar at kravnivået er **jevnt fordelt** over trinnrekkens spenn. Den antakelsen er ikke
verifisert, og **ingen kilde i materialet gir en målt T20-fordeling over året.** Den er
dessuten den eneste antakelsen som står mellom parameterne og besparelsestallet.
## Måleterskelen
Virksomhetens M&V-veileder sier at en besparelse bør overstige **~10 % av baseline** for å
skilles pålitelig fra støy. Tiltaket ligger på **10,00 %** av driftssenterets totale forbruk —
bokstavelig talt på terskelen — og **18,6 %** av hovedkjølingen, altså godt over hvis man måler
på riktig avgrensning.
Det er en grunn til at avgrensningen betyr noe her og ikke bare i validator-mappingen: målt
på hovedmåleren er tiltaket akkurat i grenseland, målt på hovedkjølingens egen kurs er det
tydelig. **Å legge en kursmåler på hovedkjølingen samtidig med styringen er derfor det billigste
enkelttiltaket for å gjøre dette anlegget lærbart** — det gjør Option B tilgjengelig for *neste*
tiltak.
## Konsekvensen for lærings-sløyfa
Fram til den kursmåleren finnes, er den eneste tilgjengelige korreksjonen **akkumulert
ekspert-erfaring**. Se [verdict-trinnstyring-fro.md](verdict-trinnstyring-fro.md) og
[kilder-kjoling-realisering.md](kilder-kjoling-realisering.md).

View file

@ -0,0 +1,81 @@
---
type: hypothesis
title: "Kaldgangsinnkapsling: senke varmetilskuddet i stedet for å styre kjølingen bedre"
description: "Passivt tiltak som skiller kald og varm luft i serverhallen og dermed senker selve kravet i høylastsonen. Svært høy modellert besparelse, svært høy investering, og — i motsetning til styringstiltaket — ingenting som kan overstyres i drift."
resource: DRIFTSSENTER-KJOLING
measure_id: KJOLING-02
tags: [kjoling, kaldgang, innkapsling, passivt-tiltak, ENTR]
timestamp: 2026-08-09
---
# Tiltak: kaldgangsinnkapsling
Kravet i høylastsonen er ikke et fast kjølenivå — det er en **prosentandel av varmetilskuddet
over frikjølingsgrensen** (DS-09 tabell 9.4: 3,00 % for driftssenterets last- og
driftsklasse). Senker man varmetilskuddet som når kjøleaggregatene, senker man kravet, og da
faller energibehovet uten at noe styres bedre.
DS-09 § 9.2.1 lister virkemidlene direkte:
> «Varmetilskuddet i høylastsonen kan reduseres ved å: Legge luftinntaket slik at det får
> minst mulig direkte sol. Plante skjermende vegetasjon foran luftinntaket. Bygge innkapsling av
> kaldgangene som gradvis skiller kald og varm luft. Montere blindplater i alle tomme
> rackplasser. Benytte lyse, reflekterende flater på tak og yttervegger.»
Og standarden sier hvorfor det er verdt å gjøre: dette «kan både øke driftssikkerheten og
redusere energiforbruket og kostnadene til kjøling».
## Hvorfor dette tiltaket står her uten å være dømt
**Det er en kontrast, og kontrasten er hele poenget.**
| | Trinnstyring (KJOLING-01) | Kaldgangsinnkapsling (KJOLING-02) |
|---|---|---|
| Type | aktiv styring | **passivt byggverk** |
| Modellert reduksjon | 18,6 % av hovedkjølingen | **77,9 %** av høylastsonen (ENTR) |
| Investering | ingen kilde bærer | **≈ €400 000 per hall** (ENTR) |
| Kan overstyres i drift? | **Ja** — og det er hele realiseringsrisikoen | **Nei. Det finnes ingenting å overstyre.** |
En innkapsling som skiller kald og varm luft, gjør det hver eneste dag uten at noen gjør noe.
Den har ingen hysterese, ingen kalibrering, ingen driftsrutine som kan tolkes forsiktig.
**Realiseringsgapet styringstiltaket har, har dette tiltaket i praksis ikke** — og det er nettopp
derfor det ikke kan arve dommen i [verdict-trinnstyring-fro.md](verdict-trinnstyring-fro.md).
Risikoprofilen er en helt annen, ikke en mildere versjon av den samme: her ligger usikkerheten
i **byggekostnad og gjennomførbarhet**, ikke i om anlegget brukes som forutsatt.
## Tallene, og hva de faktisk er
ENTR D2.1 modellerer tiltaket «redusert varmetilskudd i høylastsonen» via innkapsling eller
skjermer ved rackradene:
> Før tiltak: **150 059 kWh/år** (høylastsoner)
> Etter tiltak: **33 114 kWh/år** (høylastsoner)
> Energisparepotensial: **116 945 kWh/år** — altså **77,9 %**
**⛔ Disse tallene er IKKE en måling.** De står i D2.1 under overskriften `MODEL INPUTS`, og
resultatlinjen heter `ASSESSMENT RESULTS: Energy saving potential`. Det er et **modellert
ex-ante-anslag for et generisk referanseanlegg** — ikke et ex-post-par fra et virkelig anlegg,
og ikke vårt. Se
[kilder-kjoling-realisering.md](kilder-kjoling-realisering.md), som fører
provenienssen i sin helhet.
**Tiltaket er ikke regnet om til driftssenterets tall, og det er med vilje.** D2.1s baseline for
dette tiltaket (150 059) er en annen enn baselinen for styringstiltaket (158 059) på nominelt
samme referanseanlegg. Å skalere 77,9 % ned på vår hovedkjøling ville vært å låne en
prosentandel fra en baseline som ikke er vår, og presentere resultatet som vårt eget
regnestykke.
## Hvorfor det ikke er projisert inn i validatoren
To grunner, og den andre er den viktige:
1. **Kostnadssiden ville dominert.** €400 000 per hall, for to haller, mot en besparelse
i størrelsesorden hundretusen kroner i året. Tilbakebetalingstiden er ikke marginal — den
er utenfor det en energibegrunnelse bærer alene. Tiltaket bygges i praksis når hallen
uansett skal bygges eller rehabiliteres; D2.1 sier det selv: «Kostnaden vil inngå i
byggekostnaden for hallen».
2. **Det har ingen lærings-overflate.** Bundelen finnes for å frø en lærings-sløyfe med et
gap mellom modellert og realisert. Et passivt byggverk uten driftsavhengighet har
knapt noe slikt gap å lære av. Det gjør det til en dårlig kandidat for et verdict-frø —
og til en god kontrast som viser hvorfor det *andre* tiltaket trenger ett.

View file

@ -0,0 +1,167 @@
---
type: hypothesis
title: "Trinnstyring av hovedkjølingen: fra 3 trinn til 13"
description: "Erstatte kontaktorstyrt 3-trinns regulering av hovedkjølingen med 13-trinns styring slik Driftsstandard DS-09 anbefaler. Modellert besparelse utledet av kvantiseringsoverskuddet i de to trinnrekkene, med et åpent kostnadsgulv."
resource: DRIFTSSENTER-KJOLING
measure_id: KJOLING-01
tags: [kjoling, kjolestyring, frekvensstyring, ECM, DS-09]
timestamp: 2026-08-09
---
# Tiltak: 13-trinns styring av hovedkjølingen
Oppgradering av styringen i **høylast- og overgangssonen** fra dagens
**3-trinns kontaktorstyring** til **13-trinns styring**, slik Driftsstandard DS-09 (2021)
§ 9.6.1 anbefaler. Aggregatene byttes ikke — det er reguleringen som byttes.
Dette er tiltaket som er **projisert inn i validatoren**.
## Tiltaket er beskrevet av standarden selv
DS-09 § 9.6.1 beskriver både utgangspunktet og målet, ordrett:
> «Høylastsonens nedtrapping er gitt av kjølekurven i figur 9.2. I utførelse har dette
> vært begrenset til 3 trinn arrangert med oppdeling i kurser styrt via kontaktorer.
> Frekvensstyrte vifter og kompressorer åpner for en bedre tilpasning til kurven ved hjelp av
> styring i flere trinn som vil redusere energiforbruket vesentlig.»
>
> «Det anbefales å definere høylast-/overgangssonen i 13 trinn henholdsvis
> 0-5-10-15-20-25-30-40-50-60-70-80-90-100 % alternativt dynamisk (…). Det bør som minimum
> legges til en hysteresetid på 60 sekunder for endringer i nivåene.»
**Det er uvanlig komfortabelt utgangspunkt for en hypotese:** standarden navngir dagens praksis,
navngir tiltaket, og lister trinnene. Vi trenger ikke finne på noen av delene.
## Parametere
| Parameter | Verdi | Status | Kilde/forankring |
|---|---|---|---|
| Installert effekt, hovedkjøling | 60 kW | [I] | se [driftssenter-kjoling.md](driftssenter-kjoling.md) |
| Timer høylasttrinn aktivt | 4 500 t/år | [I-avledet] | ≈ 4 380 t over frikjølingsgrensen + sikkerhetsmargin |
| Trinnrekke FØR | 3 trinn: 33,3 / 66,7 / 100 % | [V-forankret] | DS-09 § 9.6.1, «oppdeling i kurser styrt via kontaktorer» |
| Trinnrekke ETTER | 14 nivåer: 0-5-10-15-20-25-30-40-50-60-70-80-90-100 % | [V] | DS-09 § 9.6.1, ordrett |
| Variabel energipris | 1,00 NOK/kWh | [V-forankret] | se [driftssenter-kjoling.md](driftssenter-kjoling.md) |
## Modellert besparelse (ex-ante)
Mekanismen er **kvantiseringsoverskudd**. En trinnstyrt regulator må aldri legge seg *under*
det kjølekurven krever — gulvet er et driftssikkerhetskrav, ikke en preferanse. Den må derfor
velge **det laveste tilgjengelige trinnet som er ≥ kravet**. Energitapet er den midlere
overskytingen, og den krymper når trinnene blir finere.
Med kravet modellert som **jevnt fordelt over trinnrekkens spenn** blir midlere servert nivå:
> 3 trinn `{33,3 %, 66,7 %, 100 %}` → midlere servert nivå **66,67 %**
> 13 trinn `{0 … 100 %}` → midlere servert nivå **54,25 %**
> Reduksjon: **12,42 prosentpoeng av installert effekt = 18,625 % av hovedkjølingens energi**
Regnestykket, med den ene antakelsen synlig:
> Hovedkjøling i dag: 60 kW × 0,6667 × 4 500 t = **180 000 kWh/år**
> Hovedkjøling etter: 60 kW × 0,5425 × 4 500 t = **146 475 kWh/år**
> Besparelse: 180 000 − 146 475 = **33 525 kWh/år** = **33 525 NOK/år**
Det er **18,6 %** av hovedkjølingens forbruk og **10,00 %** av driftssenterets totale elforbruk.
**Antakelsen som bærer tallet, og som ikke er verifisert:** at kravnivået er jevnt fordelt.
Det er det nesten sikkert ikke — T20 ved luftinntaket er skjevfordelt mot lave verdier
store deler av året, og i den skjevheten hjelper de fine trinnene *mer* enn jevnfordelingen
tilsier, ikke mindre. **Ingen kilde i materialet gir en målt T20-fordeling for driftssenteret.**
Vi lar antakelsen stå eksplisitt i stedet for å skjule den i et rundt tall.
## Kryss-sjekk mot konsernprosjektet ENTR (og hvorfor tallene ikke er like)
ENTR D2.1 modellerer et beslektet tiltak — *«frekvensstyrt kjøling med lukket
tilbakekobling»* — på et referanseanlegg med samme geometriklasse som driftssenteret:
| | ENTR D2.1 | Driftssenteret (vårt) |
|---|---|---|
| Hovedkjøling før | 158 059 kWh/år | 180 000 kWh/år |
| Hovedkjøling etter | 136 893 kWh/år | 146 475 kWh/år |
| **Besparelse** | **21 166 kWh/år (13,4 %)** | **33 525 kWh/år (18,6 %)** |
Størrelsesordenen stemmer — og det er hele poenget med en kryss-sjekk. Men **vår andel er
5,2 prosentpoeng høyere, og det skal forklares, ikke bortforklares:**
- ENTR-tiltaket beholder **konvensjonelle termostater** og forbedrer selve
tilbakekoblingssløyfa. Vårt tiltak endrer **trinnoppløsningen** fra 3 til 13. Det er to
ulike inngrep i samme kjede, og de har ingen grunn til å gi samme tall.
- ENTRs tall er **modellert av prosjektet**, ikke målt. Det er et anslag på linje med vårt,
ikke en fasit vårt anslag skal kalibreres mot.
- Vår jevnfordelings-antakelse trekker i retning av **for lavt** anslag, ikke for høyt (se over).
**⚠️ Og en felle i selve kilden:** D2.1 oppgir **ulik** baseline før tiltak for nominelt
samme referanseanlegg — **158 059** kWh/år for dette tiltaket, men **150 059** kWh/år for
innkapslings-tiltaket ([tiltak-kaldgangsinnkapsling.md](tiltak-kaldgangsinnkapsling.md)).
Baselinen er altså ikke felles på tvers av tiltakene i D2.1. De to kan ikke settes i samme
regnestykke, og vi gjør det ikke.
## Kostnadssiden — et anker med feil årstall
Eiendomsavdelingens prisbok (publikasjon 4) gir en **intern** enhetspris for kjøleanlegg:
> «For serverhaller større enn ca. 300 m² kan gjennomsnittsprisen per m² variere mellom
> NOK 1000 og NOK 3000. (Prisen inkluderer aggregater, kabelbroer, installasjon av trafo og
> nettilknytning)»
For hovedkjølingens 600 m² gir det 0,6–1,8 mill. NOK. **Men tallet er ubrukelig som det står, av
to grunner:**
1. **Publikasjonen er udatert i vårt uttrekk.** Et beløp uten årstall kan ikke prisjusteres.
Prisboken omtaler «mer enn 700 tekniske rom i virksomheten» — virksomheten passerte det for
mange år siden, så tallet er gammelt, men *hvor* gammelt vet vi ikke.
2. **Prisen gjelder feil ting.** Den dekker **hele kjøleanlegget** per m² — aggregater,
kabelbroer, trafo, nettilknytning. Vårt tiltak bytter **bare styringen**. En
styringsoppgradering er en brøkdel av et komplett anlegg, og ingen kilde i materialet gir
den brøken.
**Konsekvensen er at `cost-baseline.json` ikke får noen investeringsrad.** Det er samme valg
som klientpark-bundelen tok, men av en annen grunn: der fantes det ingen kilde, her finnes det en
kilde som ikke bærer. Å prisjustere et udatert beløp til et tiltak det ikke gjelder, ville
vært å produsere et tall og kalle det et anker.
ENTRs `≈ €35 000 per kjølekrets` for det beslektede styringstiltaket er den nærmeste
størrelsesordenen vi har, og den er fra et annet anlegg og udatert. Den står i
[kilder-kjoling-realisering.md](kilder-kjoling-realisering.md) som
orientering, **ikke** som kostbase.
## Usikkerhet (for Monte Carlo P10/P50/P90)
Den dominerende usikkerheten er **ikke** energiprisen — den er **hvor godt styringen faktisk
følger kurven i drift**. Den usikkerheten er systematisk, ikke tilfeldig, og den peker én vei.
Derfor håndteres den i verdict-laget
([verdict-trinnstyring-fro.md](verdict-trinnstyring-fro.md)), ikke her.
Den eksisterende validatorens Monte Carlo varierer **enhetspris**. I denne mappingen brukes
derfor prisbandet **0,70–1,40 NOK/kWh** som usikkerhetsakse, identisk med klientpark-mappingen.
## Mapping til validatoren (hvorfor `validator-input.json` ser ut som den gjør)
Den eksisterende deterministiske validatoren er en *feasibility-gate*
(`claimed ≤ 30 % av affected total`, Monte Carlo over enhetspris) bygd for kostnadskutt.
Tiltaket mappes inn **uendret**:
- `affected_items = [{code: "ENERGI-DRIFTSSENTER-EL", quantity: 335250 kWh/år, unit_cost: 1.00 NOK/kWh}]`
→ **hele driftssenterets** årlige energikostnad (335 250 NOK).
- `claimed_saving_nok = 33525` → den modellerte besparelsen.
- `assumptions = {"ENERGI-DRIFTSSENTER-EL": [0.70, 1.40]}` → prisbandet for Monte Carlo.
**Hvorfor hele driftssenteret og ikke bare hovedkjølingen:** hadde `affected_items` vært
hovedkjølingens eget forbruk (180 000 kWh), ville besparelsen vært **18,6 %** av den — under
cap-en, men med langt mindre margin, og konvolutten ville vært feil størrelse i prinsippet:
tiltaket virker på driftssenterets energikostnad, og det er den linjen anleggseieren betaler.
Klientpark-bundelen tok samme beslutning med porteføljen som konvolutt. **For ett enkelt anlegg
er anleggets totale elforbruk den riktige analogien til en portefølje** — ikke den sonen
tiltaket tilfeldigvis sitter i.
Forholdet blir da `claimed / nominal_feasible = 33 525 / 100 575 = **1/3 eksakt**`, mot
reservens 0,3333 og klientpark-bundelens 0,3386.
**`cost-baseline.json` bærer nøyaktig samme rad.** `code`, `quantity` og `unit_cost` er
identiske i de to filene — ikke «innenfor toleranse», men identiske, fordi begge er skrevet
fra summen `180 000 + 139 230 + 16 020`. Hver `code` i `affected_items` finnes som nøkkel i
`items`.
**Ærlig begrensning:** validatorens P10/P50/P90 betyr her «øvre feasible grense» (30 % av
samplet energikostnad), *ikke* «styringsbesparelsens fysiske band». Det er bevisst — den
domenetro modelleringen og realiseringsgapet hører hjemme i verdict-laget.

View file

@ -0,0 +1,16 @@
{
"_note": "IR-projeksjon (ir.SavingsProposal) for det eksisterende deterministiske validatoren. Styringstiltaket er mappet inn i kost-IR-en UENDRET: affected_items = HELE driftssenterets arlige energikostnad (hovedkjoling 180 000 + lavlast-/utlopssone 139 230 + ovrige tekniske anlegg 16 020 = 335 250 kWh/ar a 1,00 NOK/kWh); claimed_saving_nok = modellert besparelse fra kvantiseringsmodellen (60 kW x (0,6667 - 0,5425) x 4 500 t = 33 525 kWh/ar), som er 10,00 % av total og godt innenfor 30 %-cap-en. Forholdet claimed/nominal_feasible = 33 525/100 575 = 1/3 eksakt. affected_items er anleggets TOTALE forbruk og ikke bare hovedkjolingen fordi ett anleggs totale energikostnad er den riktige analogien til en portefolje - se klientpark-bundelen, som tok samme beslutning. assumptions = energipris-band (NOK/kWh) for Monte Carlo. cost-baseline.json baerer IDENTISK code, quantity og unit_cost - begge er skrevet fra samme sum, ikke avstemt i ettertid. Se tiltak-trinnstyring-kjoling.md, seksjon 'Mapping til validatoren'.",
"project_id": "DRIFTSSENTER-KJOLING",
"measure": "Oppgradering av hovedkjolingen fra 3-trinns kontaktorstyring til 13-trinns styring (Driftsstandard DS-09 2021, par. 9.6.1). Aggregatene byttes ikke - reguleringen byttes.",
"affected_items": [
{
"code": "ENERGI-DRIFTSSENTER-EL",
"quantity": 335250,
"unit_cost": 1.0
}
],
"claimed_saving_nok": 33525,
"assumptions": {
"ENERGI-DRIFTSSENTER-EL": [0.70, 1.40]
}
}

View file

@ -0,0 +1,139 @@
---
type: verdict
title: "Ekspert-dom (frø): 13-trinns styring av hovedkjølingen — godkjent med realiseringskorreksjon"
description: "Frøsatt ekspert-dom for styringsoppgraderingen. Den modellerte besparelsen er korrekt fra trinnrekkene, men den forutsetter at reguleringen faktisk følger kjølekurven i drift. Tre navngitte mekanismer i Driftsstandard DS-09 selv trekker den andre veien. Forventet faktisk besparelse settes til 72 % av modellert, lånt fra virksomhetens metastudie av styring og merket som lån."
resource: DRIFTSSENTER-KJOLING
measure_id: KJOLING-01
decision: approved_with_adjustment
realization_rate: 0.72
modelled_saving_nok: 33525
expected_actual_saving_nok: 24138
gap_source: control-tracking-overestimation
context_key: "kjoling; styring=3-trinn->13-trinn; T20-maaling=kontinuerlig-paakrevd; energimaaling=fravaerende-i-baseline"
provenance: "frø — AI-forfattet, fiktiv virksomhet. Realiseringsgraden er LÅNT fra Eksempelvirksomhetens (fiktive) internrapport MS-2011: utetemperaturstyring faller fra 39 % til 28 % gjennomsnittlig besparelse når anslagene filtreres til faktiske installasjoner (n 73 -> 32), forhold 0,718. Det er IKKE en prosjekt-realiseringsgrad, men forholdet mellom to filtrerte populasjonsgjennomsnitt, fra kontorbygg. Det finnes INGEN ex-post-evaluering for kjolestyring i driftssenteret. Erstattes av ekte HITL i produksjon."
tags: [verdict, realization-rate, ExpeL-seed, HITL, kjoling, kjolestyring, laant-rate]
timestamp: 2026-08-09
---
# Ekspert-dom (frø): 13-trinns styring av hovedkjølingen
> **Dette er et frø**, ikke en ekte dom. I simulering gir en ekspert-persona slike dommer;
> i produksjon gir et menneske dem via samme mappe-grensesnitt. Frøet er forankret i
> virksomhetens (fiktive) kilder ([kilder-kjoling-realisering.md](kilder-kjoling-realisering.md)),
> ikke fritt oppdiktet innenfor fortellingen — men **raten er lånt, ikke målt på
> driftssenteret**, og det står i `provenance`.
## Dommen
**Beslutning:** godkjent — med realiseringskorreksjon.
Den modellerte besparelsen (**33 525 NOK/år**) er korrekt regnet fra de to trinnrekkene, og
validatoren bekrefter at den ligger innenfor feasibelt område. Men modellen regner på hvordan
en trinnrekke **kan** legge seg mot kjølekurven, og et anlegg i drift legger seg systematisk
høyere. Forventet faktisk besparelse settes til **≈ 24 138 NOK/år** (72 % av modellert).
## Begrunnelse (det validatoren ikke kan regne)
### Hovedmekanismen: modellen regner på trinn, driften leverer et forløp
Kvantiseringsmodellen i
[tiltak-trinnstyring-kjoling.md](tiltak-trinnstyring-kjoling.md) antar at
regulatoren til enhver tid står på **det laveste trinnet som er ≥ kravet**. Det er sant for en
regulator uten treghet. DS-09 forutsetter tre former for treghet — og alle tre er
**standardens egne krav eller forbehold**, ikke svakheter ved et bestemt anlegg:
**1. Hysteresen er påkrevd, og den koster.**
DS-09 § 9.6.1: «Det bør som minimum legges til en **hysteresetid på 60 sekunder** for endringer
i nivåene.» Hysterese er asymmetrisk i energi: den holder anlegget på det **høyere** trinnet
gjennom svingninger i T20 som ellers ville utløst nedtrinn. På en dag med vekslende sol og skyer
er det ikke en marginal effekt. Kravet er et driftssikkerhetskrav og kan ikke regnes bort.
**2. Den variable soneutstrekningen implementeres ofte ikke.**
DS-09 § 9.6.1, siste setning: «Ved varierende trinn vil også utstrekningen av høylastsonen
variere, og dette er viktig å få implementert for å utnytte energisparepotensialet **mest
mulig**.» At standarden finner det nødvendig å be om dette, forteller at det er den delen som
faller ut. **Halvparten av gevinsten ved fin trinning ligger i at sonen også blir mindre når
kravet faller** — implementeres bare nivåtrinningen, leveres bare den ene halvparten.
**3. Kalibreringen er en driftsrutine, ikke en konstant.**
DS-09 § 9.6: «Innjustering av anlegg ved igangkjøring med kalibrert måleinstrument for korrekte
nivåer for utetemperatur er viktig for korrekt drift.» En temperaturmåler som drifter, står i
sol, eller er innjustert med sikkerhetsmargin, gir en for høy T20 — og en for høy T20 gir et for
høyt trinn hver time resten av året. Feilen er **systematisk og ensrettet**: ingen
driftsorganisasjon kalibrerer seg til for lite kjøling i en serverhall.
### Hvorfor raten er lånt fra utetemperaturstyring, og hva lånet er
Internrapport MS-2011 sorterte 240 besparelsesanslag fra 88 prosjektrapporter og filtrerte dem
suksessivt. For **utetemperaturstyrt regulering** — samme strategi som vår — falt gjennomsnittet
fra **39 %** til **28 %** når utvalget ble begrenset til **faktiske installasjoner**
(n fra 73 til 32). Rapporten konkluderer at «simuleringer overvurderer betydelig (med minst
10 %) den gjennomsnittlige besparelsen utetemperaturstyring gir i faktiske bygg».
**Forholdet 28/39 = 0,718 er lånet. 0,72 er dette lånet, ikke en måling av driftssenteret.**
Og lånet er svakere enn klientpark-bundelens på ett punkt og sterkere på et annet:
**svakere** fordi det ikke er en prosjekt-realiseringsgrad (målt ÷ predikert for de samme
prosjektene), men forholdet mellom to filtrerte populasjonsgjennomsnitt; **sterkere** fordi
styringsstrategien er den samme — det er forholdene ute som styrer i begge tilfeller, og det er
sensor, kalibrering og treghet som spiser gevinsten i begge tilfeller.
### Motmekanismen — og hvorfor den IKKE er trukket fra
Én forhold peker **motsatt vei**, og det er modellens egen antakelse: kravnivået er antatt
**jevnt fordelt** over trinnrekkens spenn. Ved driftssenterets luftinntak er T20 skjevfordelt
mot **lave** verdier store deler av året — vinter, natt, kalde overskyede dager — og i det
området ligger de fine trinnene tettest (5-10-15-20-25-30 %). Der hjelper 13-trinnsrekka **mer**
enn jevnfordelingen tilsier, ikke mindre. Med en realistisk T20-fordeling ville den modellerte
besparelsen trolig vært **høyere** enn 33 525.
Den er likevel ikke netto-regnet inn, av én grunn: **ingen kilde i materialet gir en målt
T20-fordeling over året.** Å justere modellen opp på en fordeling vi ikke har, for så å
justere den ned igjen med en lånt rate, ville vært to gjetninger som later som de opphever
hverandre.
**Derfor er 0,72 beheftet med usikkerhet i BEGGE retninger**, og det skiller den fra
klientpark-frøets 0,81, som var en uttalt **nedre** grense. Her vet vi ikke hvilken vei feilen
peker — bare at den er der.
### Hvorfor dette ikke kan regnes fra parameterne
Du kan **ikke** regne deg til RR = 0,72 fra `{60 kW, 4 500 t, 3 trinn, 13 trinn}`. Alle fire
er kjent, og modellen som forbinder dem er aritmetisk lukket. Skjevheten ligger i **hvordan
et anlegg faktisk driftes** — hysterese, uimplementert soneutstrekning, kalibreringsmargin — og
det er epistemikk parameterne ikke bærer. Det er nøyaktig lærings-overflaten bundelen er
bygget for.
## Lærings-signalet (ExpeL)
Korreksjonen er **kontekstbetinget**:
`context_key = "kjoling; styring=3-trinn->13-trinn; T20-maaling=kontinuerlig-paakrevd; energimaaling=fravaerende-i-baseline"`.
`gap_source` er satt til **`control-tracking-overestimation`** — og det er **bevisst en annen
nøkkel enn `hours-of-use-overestimation`**, som både kontorbygg- og klientpark-frøet bruker.
Forskjellen er ikke kosmetisk:
- `hours-of-use-overestimation` er en **parameterfeil**. Anlegget gjør det det skal; tallet
vi matet inn var galt. Korreksjonen er å måle parameteren bedre.
- `control-tracking-overestimation` er en **driftsfeil**. Parameterne er riktige; anlegget
leverer ikke det utstyret er i stand til. Korreksjonen er å endre idriftsettelse,
kalibreringsrutine og hva som faktisk implementeres.
**En lærings-sløyfe som slår disse sammen, lærer feil tiltak.** Å måle driftstimer bedre
hjelper ikke et anlegg som står på for høyt trinn, og å kalibrere temperaturmåleren hjelper
ikke et anlegg med feil timeanslag. De to nøklene skal leve side om side.
Neste kjøring, gitt en lignende hypotese i samme kontekst, skal hente denne dommen og justere
den modellerte ex-ante-besparelsen mot forventet ex-post (≈ 0,72×).
## Om tiltak 2 (kaldgangsinnkapsling)
Denne dommen gjelder **kun** styringsoppgraderingen (`KJOLING-01`). Kaldgangsinnkapslingen
([tiltak-kaldgangsinnkapsling.md](tiltak-kaldgangsinnkapsling.md)) er ikke dømt her, og **skal
ikke arve raten** — den skal ikke engang arve `gap_source`.
Grunnen er strukturell: en innkapsling som skiller kald og varm luft, har **ingenting som kan
overstyres**. Den har ingen hysterese, ingen kalibrering og ingen driftsrutine. Alle tre
mekanismene som begrunner 0,72 er fraværende. Et passivt tiltak med samme `gap_source` som et
aktivt ville vært en kategorifeil i lærings-sløyfa — og risikoen der ligger et helt annet sted,
i byggekostnad og gjennomførbarhet.

View file

@ -0,0 +1,10 @@
{
"_note": "Prosjektets kostdata for KLIENTPARK-ENERGI, i det formatet den konsumerende implementasjonen definerer (dens akse - bundelen normerer ikke dette formatet). Raden er portefoeljens arlige energikostnad: 9 500 arbeidsstasjoner x 114 W installert (100 W systemenhet + 14 W stromforsyningstap, innkjopsspesifikasjonen) x 4 050 driftstimer/ar (IT-driftshandboken, 2021: 4 000-4 100 t/ar for eldre arbeidsstasjoner) / 1 000 = 4 386 150 kWh/ar, a 1,00 NOK/kWh eks. mva. quantity og unit_cost er BYTE-IDENTISKE med affected_items-raden i validator-input.json fordi begge filene er skrevet fra denne ene linjen - 5 %-toleransen er lukket ved konstruksjon, ikke ved avstemming. Investeringskostnad er BEVISST utelatt: ingen kilde i materialet gir NOK per arbeidsstasjon eller per styringslisens, og en utledet verdi hoerer ikke hjemme i en kostbase. Alle kilder er fiktive (Eksempelvirksomheten). Se klientpark-energi.md og tiltak-pc-utskifting.md.",
"project_id": "KLIENTPARK-ENERGI",
"items": {
"ENERGI-KLIENTPARK-EL": {
"quantity": 4386150,
"unit_cost": 1.0
}
}
}

View file

@ -0,0 +1,73 @@
---
type: index
okf_version: 0.1
title: "Klientpark energi — PC-utskifting og adaptiv strømstyring"
description: "OKF-bundle for en fiktiv virksomhets klientpark med to kandidat-tiltak: utskifting av 2 500 eldre stasjonære PC-er, og adaptiv strømstyring som utnytter den tillatte ytelsesreserven. Bygget rundt et dokumentert evidensgap — uten måler kan realiseringsgraden ikke ses."
tags: [energieffektivisering, klientpark, arbeidsstasjoner, M&V, IPMVP, realiseringsgrad]
timestamp: 2026-08-09
---
# Klientpark energi
En OKF-bundle for **utskifting og strømstyring av stasjonære arbeidsstasjoner**: én
portefølje, to kandidat-tiltak. Den deler lærings-overflate med bygg-energi-mikro-bundelen, men
står på egne ben: metode- og kildelaget er **materialisert inn her**, ikke lenket på tvers av
bundler, og evidensgrunnlaget er **virksomhetens eget der det teller**.
> Framework-nøytral artefakt (null kode-avhengighet). Den bor i pakkens `data/bundles/`, ikke i
> den pull-only `shared/`-subtreet, og leses av demoen som den leverte kunnskapsbasen.
**Både prosjektlaget og litteraturlaget er fiktive.** Porteføljen tilhører den oppdiktede
«Eksempelvirksomheten», og kildene den er bygget av (installert effekt, driftstimer, energipris,
realiseringsgrad) er virksomhetens egne — like oppdiktede — målerapporter, håndbøker og
evalueringer, merket `[V]` der de i fortellingen er verifisert. Ingen ekte organisasjon,
publikasjon eller person er sitert. En produksjons-deployer erstatter begge lagene med en ekte
kunnskapsbase og ekte kilder.
## Hvorfor klientparken
Domenet ble valgt fordi det bærer lærings-overflaten **skarpere enn kontorbygget gjør**.
I et kontorbygg er gapet mellom modellert og realisert besparelse *målbart, men sjelden
målt*. I Eksempelvirksomhetens klientpark er det noe strengere: **virksomhetens egen
energioppfølging dokumenterer at arbeidsstasjonene mangler egen måling helt, og at strømmen
fordeles på estimerte verdier.** Uten meterdata finnes det ingen ex-post å sammenligne ex-ante
med. Realiseringsgraden er ikke ukjent fordi ingen har regnet på den — den er **strukturelt
usynlig**.
Det gjør domenet til et godt frø for lærings-sløyfa: den eneste kilden til korreksjon er
akkumulert ekspert-erfaring, som er nøyaktig det verdict-laget bærer og validatoren ikke kan
regne. Se [verdict-klientpark-fro.md](verdict-klientpark-fro.md).
## Innhold (progressiv disclosure)
- [klientpark-energi.md](klientpark-energi.md) — `type: project` — porteføljen, energibaselinen
og rammene.
- [tiltak-pc-utskifting.md](tiltak-pc-utskifting.md) — `type: hypothesis` — kandidat-tiltak
1: utskifting av 2 500 eldre stasjonære PC-er. **Det er dette tiltaket som er projisert
inn i validatoren.**
- [tiltak-adaptiv-stromstyring.md](tiltak-adaptiv-stromstyring.md) — `type: hypothesis` —
kandidat-tiltak 2: adaptiv strømstyring som henter ut den overdimensjoneringen
ytelsesreserven allerede tillater. Svakere kildebelagt enn tiltak 1, og merket slik.
- [metode-ipmvp-a.md](metode-ipmvp-a.md) — `type: methodology` — M&V-metoden (IPMVP
Option A), og hvorfor de øvrige opsjonene er stengt for en umålt klientpark.
- [kilder-klientpark-realisering.md](kilder-klientpark-realisering.md) — `type: reference` —
virksomhetens egne (fiktive) kilder: baseline- og normankere, og etterevalueringene
realiseringsgraden er lånt fra.
- [verdict-klientpark-fro.md](verdict-klientpark-fro.md) — `type: verdict` — frøsatt ekspert-dom.
**ExpeL-frøet loopens steg 1 henter fra.**
## Hvordan den kjøres i dag
`validator-input.json` er IR-projeksjonen den eksisterende deterministiske validatoren
konsumerer uendret; `cost-baseline.json` bærer det samme tallgrunnlaget som prosjektets
kostdata. **De to filene er bygget fra samme linje aritmetikk og bærer identisk `code`,
`quantity` og `unit_cost`** — se [tiltak-pc-utskifting.md](tiltak-pc-utskifting.md),
§«Mapping til validatoren».
Bundelen ships **uten `golden.json`**. Den blokken er kryss-implementasjons-fasit produsert
av en seedet Monte Carlo, og det finnes ingen kjørbar pipeline å produsere den med. En
fasit ingen gate leser er verre enn ingen fasit. Lærings-overflaten går ikke tapt: de
strukturerte feltene ExpeL-folden faktisk henter (`realization_rate`,
`expected_actual_saving_nok`) ligger i frontmatteren til
[verdict-klientpark-fro.md](verdict-klientpark-fro.md), som er der loopen leser dem.

View file

@ -0,0 +1,151 @@
---
type: reference
title: "Klientpark: egne ankere og lånt realiseringsgrad — virksomhetens kilder"
description: "Kildebelagte tall for klientparkens energibaseline, normer og realiseringsgap, alle fra den fiktive Eksempelvirksomhetens egne dokumenter. Skiller strengt mellom materialet om klientparken (baseline, norm, årsak) og de lånte etterevalueringene (selve realiseringsgraden)."
tags: [realization-rate, performance-gap, klientpark, M&V, kilder, evidensgap]
timestamp: 2026-08-09
---
# Klientpark: virksomhetens kilder
**Alle kildene i denne fila er fiktive.** De er dokumenter i den oppdiktede
Eksempelvirksomheten — målerapporter, håndbøker, investeringsnotater og etterevalueringer — og
ingen av dem finnes utenfor denne bundelen. Ingen ekte organisasjon, publikasjon eller person er
sitert. Merkingen `[V]` betyr «verifisert mot virksomhetens eget dokument» innenfor fortellingen.
**Realiseringsgrad (RR)** = faktisk evaluert besparelse (ex-post) ÷ modellert/påstått
besparelse (ex-ante). RR < 1 betyr at drift leverte mindre enn modellen lovte.
Denne fila har en **skarp todeling**, og den er det viktigste ved den:
- **Del A — materiale om klientparken.** Baseline, norm og *årsaken til* at gapet ikke kan ses.
Alt `[V]` mot virksomhetens eget dokument.
- **Del B — lånt materiale.** Selve realiseringsgraden. Den finnes **ikke** for klientparken i
noen kilde vi har funnet, og er lånt fra virksomhetens etterevalueringer av tidligere
belysningsprogrammer i egne bygg. **Lånet er merket overalt der tallet brukes.**
Å blande de to ville gjort et lånt tall til en måling av klientparken. Det gjør vi ikke.
---
## Del A — materiale om klientparken [V]
| Nivå | Funn | Kilde | År |
|---|---|---|---|
| Aggregat (virksomheten) | 13 000 arbeidsstasjoner inkl. beregningsmaskiner = «rett over 13 GWh/år» ⇒ **≈ 1 000 kWh/maskin/år** | Energirapport Region Nord | ikke oppgitt |
| Maskinvare | Eldre stasjonær PC: 100 W systemenhet + 14 W strømforsyningstap = **114 W**; tilsvarende ny småformat-PC **70 W** | Innkjøpsspesifikasjonen, effekttabell | ikke oppgitt |
| **Driftstimer (internt normtall)** | **4 000–4 100 t/år for eldre arbeidsstasjoner** | **IT-driftshåndboken, kap. 6** | **2021** |
| **Reservefaktor** | **RF ≤ 0,85** — ≥15 % ytelsesreserve tillatt ved dimensjonering | **IT-driftshåndboken** | **2021** |
| Ytelsesgulv | 1,0 s responstid og 5 samtidige applikasjoner for standard kontorarbeid | Innkjøpsspesifikasjonen | ikke oppgitt |
| Kostnad (regionnivå) | **≈ 200 mill. NOK** for full utskifting av klientparken, forventet **67 %** energikutt, **≈ 27 mill. NOK/år** spart | **Investeringsnotat, Region Vest** | **2022** |
| Vedlikehold | Nye maskiner «Very good» ≤ 5 år; eldre maskiner trenger komponentbytte hyppig (Region Vest: hvert 4. år) | Hovedplan for klientdrift | ikke oppgitt |
| Praksis | Nattlig avstenging 00:00–05:00 pilotert 3/4–15/10/2024; må vurderes lokalt | IT-driftsavdelingen, pilotnotat | 2024 |
### Årsaken gapet ikke kan ses (klientparken, og bundelens poeng) [V]
**Virksomhetens energioppfølging dokumenterer at arbeidsstasjonene mangler egen måling, og at
forbruket fordeles på estimerte verdier fra byggenes fellesmålere.** Uten meterdata er
ex-post-måling — og dermed realiseringsgrad — ikke mulig.
Det er ikke et hull i denne bundelen. Det er grunnen til at den finnes: i et domene der
gapet er strukturelt usynlig, er ekspert-erfaring den eneste korreksjonskilden.
### To gap-mekanismer som er klientpark-spesifikke, og som peker hver sin vei [V]
1. **Installert effekt ≠ merkeeffekt — peker OPP.** Målt: en maskin merket 100 W trekker
**120 W** (stikkprøvemåling i energirapporten). Modellen regner merkeeffekt; strømregningen
betaler den faktiske. Er baselinen understatt, er den *faktiske* besparelsen **større** enn
modellert. Dette trekker realiseringsgraden **oppover**.
2. **Driftstimer — peker NED.** Driftshåndboken gir 4 000–4 100 t/år som tabellverdi;
stikkprøven antar 4 150 t. **Ingen kilde gir en målt driftstimekurve for klientparken.** Er
timene overvurdert, er besparelsen overvurdert.
**De to opphever ikke hverandre til noe kjent.** Mekanisme 1 er målt på den *gamle* maskinen;
tilsvarende måling for den nye generasjonen finnes ikke i materialet, så nettoen kan ikke regnes.
Se [verdict-klientpark-fro.md](verdict-klientpark-fro.md) for hvordan dommen håndterer det.
### Evidensgap i materialet om klientparken (gjennomgangens egen liste)
- NOK per arbeidsstasjon inkl. utrulling, per årstall
- NOK per styringslisens / komplett system for sentral strømstyring
- Kvantifisert kWh eller % for adaptiv strømstyring eller dvale i virksomhetens egne prosjekter
- Reell realiseringsgrad (ex-post ÷ ex-ante) for utskiftings- eller styringsprosjekter i klientparken
- Målt driftstimekurve for arbeidsstasjonene
**Region Sør-caset er bevisst utelatt fra tabellen.** Kilden oppgir både «estimert
sparepotensial 4,5 GWh/år» og «70 % reduksjon» for utskifting av 10 000 maskiner, men ikke som
et ex-ante/ex-post-par. Ingen realiseringsgrad kan regnes av det, og vi later ikke som.
---
## Del B — lånt materiale: realiseringsgraden [V, men ikke klientparken]
Strømbruken i en klientpark følger **samme effektligning** som belysning: effekt × antall ×
driftstimer. Etterevalueringene under gjelder belysningstiltak i virksomhetens egne bygg, med
samme ligning og samme stipulerte parameter (driftstimer). De er `[V]` mot virksomhetens egne
evalueringsrapporter, men de gjelder **et annet utstyr**, og de er lånt inn her fordi materialet
om klientparken ikke har motstykket.
| Nivå | Funn | Kilde |
|---|---|---|
| Virksomheten (standardantakelse) | Standard brutto-RR **0,90**; ex-ante «generelt overvurdert» | Internrevisjonens notat om gevinstberegning |
| **Program (lys, drift lavere)** | Driftsjustering ned til **81,1 %** (metrede driftstimer 15 % lavere); samtidighetsfaktor **0,566** mot antatt 1,0 | **Etterevaluering av belysningsprogrammet 2010, del 1** |
| Program (lys, drift høyere) | Driftstime-RR **106,5 %**; samtidighet 72,2 % — gapet går **begge veier** | Etterevaluering av belysningsprogrammet 2010, del 2 |
| **Parameter (driftstimer)** | Metret **3 053 t/år** mot antatt **3 772 t/år** (≈19 % lavere); CV ≈ 0,5 | **Evaluering av behovsstyrt belysning 2021** |
| Portefølje | Kontorbygg **98 %** mot personalboliger **61 %** mot totalt **93 %** | Porteføljegjennomgang FY15/16–19/20 |
| Måleterskel | Besparelse bør overstige **~10 % av baseline** for å skilles fra støy | Virksomhetens M&V-veileder |
**2021-evalueringen er den mest relevante av dem alle**, fordi den treffer nøyaktig den
parameteren klientparken lever på: metret driftstid mot antatt driftstid, 3 053 mot 3 772 timer.
Forholdet er **0,809**. Etterevalueringen fra 2010 kommer uavhengig til **0,811** gjennom samme
mekanisme.
### Systematiske årsaker til at faktisk < modellert [V]
1. **Driftstimer** — dominerende, og for klientparken forsterket av at brukervanene ikke er
målt.
2. **Andel i drift, driftsavvik og varighet** — ikke alt rulles ut eller forblir i drift;
styringer overstyres.
3. **Baseline-skjevhet** — en over- eller underpredikert baseline forplanter seg rett inn i
den absolutte besparelsen.
4. **Måleusikkerhet** — under ~10 %-terskelen drukner signalet i støy. Og uten måler finnes
ikke signalet i det hele tatt.
5. **Rebound / atferd** — maskinen står på lenger, fordi det «koster mindre».
### Ett mønster fra et naboområde, tatt med fordi det er navngitt
Virksomhetens driftslogg fra idriftsettelsen av et driftssenter beskriver et anlegg der det
modellerte viftepotensialet uteble, med en eksplisitt årsak: **«viftene ble holdt på full
hastighet mesteparten av tiden».** Det er ikke klientparken, og tallet er ikke overførbart. Men
mekanismen — en styring som i praksis ikke styrer — er den samme risikoen
[tiltak-adaptiv-stromstyring.md](tiltak-adaptiv-stromstyring.md) bærer.
---
## Kilder (virksomhetens interne dokumenter — alle fiktive)
**Om klientparken:**
- Eksempelvirksomheten, IT-driftshåndboken (2021), kap. 6 «Energi og driftstid»
- Eksempelvirksomheten, energirapport Region Nord
- Eksempelvirksomheten, energioppfølgingen: fordeling av strøm fra fellesmålere
- Eksempelvirksomheten, internrapport 8/2020 om måling i kontorbygg
- Eksempelvirksomheten, IT-driftsavdelingens pilotnotat om nattlig avstenging (2024)
- Eksempelvirksomheten, driftslogg fra idriftsettelsen av driftssenteret
- Eksempelvirksomheten, energiinnkjøpets prisoversikt (kraftpris for kontorvirksomhet)
**Metode og lånt materiale:**
- Eksempelvirksomheten, innkjøpsspesifikasjonen for arbeidsstasjoner, effekttabell
- Eksempelvirksomheten, energirapporten: stikkprøvemåling av arbeidsstasjoner
- Eksempelvirksomheten, M&V-veilederen (okt. 2018), med beskrivelse av IPMVP-opsjonene
- Eksempelvirksomheten, beregningsmal for effekttiltak, kap. 2 «Belysning og utstyr»
- Eksempelvirksomheten, etterevaluering av belysningsprogrammet 2010, del 1
- Eksempelvirksomheten, evaluering av behovsstyrt belysning 2021
- Eksempelvirksomheten, etterevaluering av belysningsprogrammet 2010, del 2
- Eksempelvirksomheten, porteføljegjennomgang FY15/16–19/20
- Eksempelvirksomheten, internrevisjonens notat om gevinstberegning
**Utelatt med begrunnelse:** Region Sør-caset (ikke et ex-ante/ex-post-par, se over) og
et leverandørnotat som ble brukt til å underbygge målings-mangelen — den påstanden er dekket
av virksomhetens egen energioppfølging, som er primærkilde.

View file

@ -0,0 +1,81 @@
---
type: project
title: "Klientpark Eksempelvirksomheten"
description: "Fiktiv klientpark: 9 500 eldre stasjonære arbeidsstasjoner fordelt på virksomhetens kontorer. Energibaseline og rammer for utskifting og strømstyring."
resource: KLIENTPARK-ENERGI
tags: [klientpark, arbeidsstasjoner, kontor-IT, energibaseline, stasjonaer-PC]
timestamp: 2026-08-09
---
# Klientpark Eksempelvirksomheten (KLIENTPARK-ENERGI)
**Fiktiv portefølje.** Tallene er illustrative, og parameterne de er bygget av er forankret i
den oppdiktede Eksempelvirksomhetens egne kilder — ikke i en ekte virksomhet. En
produksjons-deployer erstatter dette laget med sin egen utstyrsdatabase.
Porteføljen er **9 500 stasjonære arbeidsstasjoner** fordelt på virksomhetens kontorer, alle
av samme eldre modellgenerasjon. Den er valgt uniform med vilje: hele variasjonen som betyr noe
for lærings-overflaten ligger i **driftstimer og realisering**, ikke i maskin-miksen.
## Energibaseline
| Størrelse | Verdi | Merknad |
|---|---|---|
| Antall arbeidsstasjoner | 9 500 | [I] illustrativt |
| Installert effekt per arbeidsstasjon | **114 W** | [V] 100 W systemenhet + 14 W strømforsyningstap (innkjøpsspesifikasjonens effekttabell) |
| Driftstimer | **4 050 t/år** | [V-forankret] midtpunkt i IT-driftshåndbokens 4 000–4 100 t/år for eldre arbeidsstasjoner |
| **Totalt elforbruk** | **4 386 150 kWh/år** | beregnet: 114 W × 9 500 × 4 050 t / 1 000 |
| Per arbeidsstasjon | 461,7 kWh/år | beregnet |
| Variabel energikostnad | **1,00 NOK/kWh** ekskl. mva | [V-forankret] kraftpris + nettleie energiledd + elavgift |
| **Total årlig energikostnad** | **4 386 150 NOK/år** | beregnet |
**Energiprisen** (1,00 NOK/kWh) er den marginale variable kostnaden et spart kWh faktisk
unngår, ekskl. mva. Sammensetningen er den samme som for næringsbygg — kraftpris + nettleie
energiledd + elavgift — og varierer kraftig med prisområde og sesong. Derfor er den
konfigurerbar, og usikkerheten håndteres i Monte Carlo-steget (band 0,70–1,40 NOK/kWh). Se
[kilder-klientpark-realisering.md](kilder-klientpark-realisering.md).
**Én forskjell fra et enkelt kontorbygg er verdt å merke:** klientparken har ingen egen måler,
og virksomhetens energioppfølging dokumenterer at forbruket fordeles på **estimerte** verdier fra
byggenes fellesmålere. Det påvirker ikke den marginale kostnaden per spart kWh, men det er
grunnen til at ex-post-verifikasjon er stengt her. Se [metode-ipmvp-a.md](metode-ipmvp-a.md).
## Kryss-sjekk mot virksomhetens aggregat (og hvorfor tallene ikke er like)
Energirapporten for Region Nord oppgir at **13 000 arbeidsstasjoner** bruker «rett over
13 GWh/år» — altså **≈ 1 000 kWh per maskin per år**. Vår portefølje ligger på
**461,7 kWh** per maskin, under halvparten.
**Avviket er reelt og forklarlig, ikke en feil:** Region Nord-tallet dekker **kontormaskiner OG
beregningsmaskiner**, altså også tyngre klasser med 150 W- og 250 W-enheter og lengre
driftstid. Klientparken her er med vilje modellert som ren kontorklasse med 100 W-enheter — den
klassen effekttabellen måler på. Retningen på avviket stemmer med den forklaringen: vår
portefølje **skal** ligge under et aggregat som inkluderer beregningsmaskiner.
**Dette er en design-beslutning, ikke en måling.** En ekte klientpark ville hatt blandet
maskin-miks, og en deployer som bytter ut dette laget må regne baselinen på nytt fra sin
egen utstyrsdatabase. Konsekvensen for lærings-overflaten er null — realiseringsgapet er en
*rate*, ikke et absolutt tall.
## Rammer (constraints)
- Tiltak vurderes **inne i** denne porteføljen (ikke på tvers av virksomhetens øvrige
IT-utstyr).
- **Ytelseskravene setter gulvet.** For standard kontorarbeid: 1,0 s responstid og 5 samtidige
applikasjoner (innkjøpsspesifikasjonen). Ingen besparelse kan hentes ved å gå under kravet.
- **Ytelsesreserven (RF) må inn i beregningen.** Maskinen skal ligge over kravet **over tid**,
ikke bare ved utrulling. IT-driftshåndboken setter RF ≤ 0,85. Se
[tiltak-adaptiv-stromstyring.md](tiltak-adaptiv-stromstyring.md) — det er nettopp den marginen
tiltak 2 lever av.
- **Nattlig avstenging kan ikke antas.** IT-driftsavdelingen kjørte pilot på avstenging
00:00–05:00 i 2024, men dokumentasjonen sier at tiltaket må vurderes lokalt, mot
oppdateringsvinduer og fjerntilgang. Det er derfor **ikke** modellert som besparelse i noen av
hypotesene her.
- Budsjett og anskaffelsesrammer eies av deployer; her holdes de minimale.
## Kandidat-tiltak
- [tiltak-pc-utskifting.md](tiltak-pc-utskifting.md) — PC-utskifting, trinn 1
(2 500 arbeidsstasjoner).
- [tiltak-adaptiv-stromstyring.md](tiltak-adaptiv-stromstyring.md) — adaptiv strømstyring på de
samme 2 500 maskinene, etter utskiftingen.

View file

@ -0,0 +1,80 @@
---
type: methodology
title: "IPMVP Option A for klientparken — og hvorfor de andre opsjonene er stengt"
description: "M&V-metoden for å verifisere besparelsen fra et klientpark-tiltak. Option A er ikke valgt fordi den er best, men fordi en umålt klientpark stenger de tre andre."
methodology: IPMVP
option: A
tags: [IPMVP, M&V, retrofit-isolation, klientpark, maalermangel]
timestamp: 2026-08-09
---
# M&V-metode: IPMVP Option A for klientparken
**IPMVP** (International Performance Measurement and Verification Protocol) er et utbredt
metoderammeverk for å måle og verifisere energibesparelser. Kjerneinnsikten som begrunner hele
lærings-sløyfa er metodens eget utgangspunkt: besparelse kan ikke måles direkte, fordi den er
**fravær** av energibruk.
Besparelse er en **kontrafaktisk** størrelse — det finnes ingen måler for «det som ikke ble
brukt». Den *beregnes*: `Baseline-energi − Rapporterings-energi ± justeringer` (metodens
grunnligning).
## De fire opsjonene (metodens egne navn)
- **Option A — Retrofit Isolation: Key Parameter Measurement.** Måler nøkkelparameteren
(typisk effekt) på det berørte utstyret; øvrige parametere (typisk driftstimer) *estimeres*.
- **Option B — Retrofit Isolation: All Parameter Measurement.** Måler alle relevante parametere.
- **Option C — Whole Facility.** Besparelse fra anleggets hovedmåler, med rutinejustering.
- **Option D — Calibrated Simulation.** Besparelse via simuleringsmodell kalibrert mot måledata.
## Hvorfor Option A her — ved eliminasjon, ikke ved preferanse
I et kontorbygg velges Option A fordi den er **billigst og enklest** for ett isolert tiltak.
For denne porteføljen er begrunnelsen en annen og svakere: **de tre andre opsjonene er
praktisk stengt.**
- **Option C er stengt av målermangel.** Option C forutsetter en hovedmåler å lese
besparelsen ut av. Virksomhetens energioppfølging dokumenterer at arbeidsstasjonene **mangler
egen måling** og fordeles på **estimerte** verdier fra byggenes fellesmålere. Der det ikke
finnes meterdata, finnes det ingen rapporteringsperiode å trekke fra en baseline.
- **Option B er stengt av kostnad og geografi.** «Alle relevante parametere» for en klientpark
betyr driftstimer per maskin, over en maskinpark spredt over titalls kontorer.
Instrumenteringen ville kostet mer enn tiltaket på en park av kontorklasse-maskiner.
- **Option D er stengt av kalibreringsdata.** En kalibrert simulering må kalibreres mot noe.
Se Option C.
**Option A er derfor det som står igjen** — og det er verdt å si høyt, fordi valget ved
eliminasjon flytter mer vekt over på den parameteren Option A tillater å *estimere*.
## Der metoden lekker: den estimerte parameteren
Option A måler effekt (billig, presist — 114 W før, 70 W etter) og **stipulerer
driftstimer**. For klientparken er det stipulatet svakere enn i et bygg:
- Et bygg har en **timeplan** å stipulere fra. Den treffer sjelden metret driftstid, men den
er i det minste anleggsspesifikk.
- En klientpark har **brukervaner**, og **ingen kilde i materialet vårt gir en målt
driftstimekurve for maskinene.** Vi bruker IT-driftshåndbokens 4 000–4 100 t/år — et
virksomhetsomfattende tabellanslag for «eldre arbeidsstasjoner», ikke en målt kurve for
denne maskinparken med disse arbeidsvanene.
**Det gir en dobbel eksponering:** parameteren metoden tillater å estimere er både den
dominerende usikkerheten *og* den vi har svakest kilde for. Realiseringsgapet oppstår
nøyaktig der.
## Måleterskelen, og hvorfor den ikke redder oss
Virksomhetens M&V-veileder sier at en besparelse bør overstige **~10 % av baseline** for å
skilles pålitelig fra støy. Trinn 1 ligger på 10,2 % av porteføljen — akkurat på terskelen — og
38,6 % av de berørte maskinenes eget forbruk, altså godt over hvis man måler på riktig
avgrensning.
Det hjelper likevel ikke, fordi terskelen forutsetter at det **finnes en måling** å skille
signalet ut av. Se Option C.
## Konsekvensen for lærings-sløyfa
Når ex-post-verifikasjon er stengt, er den eneste tilgjengelige korreksjonen **akkumulert
ekspert-erfaring**. Det er ikke en nødløsning i dette domenet — det er den eneste kilden
som finnes. Se [verdict-klientpark-fro.md](verdict-klientpark-fro.md) og
[kilder-klientpark-realisering.md](kilder-klientpark-realisering.md).

View file

@ -0,0 +1,97 @@
---
type: hypothesis
title: "Adaptiv strømstyring — utnytting av ytelsesreserven"
description: "Konstant ytelse (effekttak) og dvale på de 2 500 nye maskinene fra trinn 1, som henter ut den overdimensjoneringen ytelsesreserven allerede tillater. Svakere kildebelagt enn tiltak 1, og merket slik."
resource: KLIENTPARK-ENERGI
measure_id: STROM-KLIENT-02
tags: [adaptiv-stromstyring, dvale, effekttak, ytelsesreserve, ECM, trinn-2]
timestamp: 2026-08-09
---
# Tiltak: Adaptiv strømstyring på de utskiftede maskinene
Styringstiltak på de **samme 2 500 maskinene** som er byttet i trinn 1
([tiltak-pc-utskifting.md](tiltak-pc-utskifting.md)). Tiltaket forutsetter de nye maskinene —
det er den styrbare strømforsyningen i den nye generasjonen som gjør det mulig.
> **Denne hypotesen er svakere kildebelagt enn tiltak 1, og det er med vilje synlig.**
> Tiltak 1 er regnet fra to merkeeffekter i samme tabell. Denne er regnet fra en
> *normmargin*, fordi det er det beste materialet gir.
## Evidensgapet, sagt først
**Ingen kilde i materialet vårt kvantifiserer besparelsen fra adaptiv strømstyring eller dvale
utenfor arbeidstid i virksomhetens klientpark** — ikke i kWh, ikke i prosent. Det er et av de
fem punktene på kildegjennomgangens egen uverifisert-liste.
Vi kunne ha utledet et tall ved å trekke tiltak 1 fra Region Vests 67 % og tilskrive resten til
styring. Det ville gitt ~46 % av forbruket etter utskiftingen — **urimelig høyt for styring
alene**, og det ville tilskrevet en kilde en dekomponering den ikke inneholder. Vi gjør det ikke.
I stedet regner vi fra den ene marginen virksomhetens egen norm faktisk **navngir**.
## Grunnlaget: ytelsesreserven er en innebygd overdimensjonering
En arbeidsstasjon skal ligge over ytelseskravet **over tid**, ikke bare ved utrulling. Derfor
dimensjoneres den med en reservefaktor (RF) som tar høyde for at programvaren blir tyngre
gjennom levetiden. IT-driftshåndboken (2021) setter **RF ≤ 0,85**.
Konsekvensen: en **ny** maskin leverer minst **15 % mer ytelse enn kravet** — en margin som
brennes bort som varme til maskinen har eldes nok til å trenge den. Konstant ytelse (et
effekttak) er styringen som henter den tilbake: effekttaket settes ned ved utrulling og løftes
gradvis etter hvert som programvarelasten vokser.
**Dette er ikke en besparelse mot kravet — det er en besparelse mot overoppfyllelsen.**
Minsteytelsen (1,0 s responstid og 5 samtidige applikasjoner for standard kontorarbeid) er
urørt hele veien.
## Parametere
| Parameter | Verdi | Status | Kilde/forankring |
|---|---|---|---|
| Antall maskiner | 2 500 | [I] | samme som trinn 1 |
| Effekt etter utskifting (utgangspunkt) | 70 W | [V] | innkjøpsspesifikasjonen |
| Reservefaktor | **RF ≤ 0,85** | [V] | IT-driftshåndboken (2021) |
| Effekt ved effekttak | 70 × 0,85 = **59,5 W** | beregnet | følger direkte av RF |
| Reduksjon per maskin (ΔW) | **10,5 W** | beregnet | 70 − 59,5 |
| Driftstimer | 4 050 t/år | [V-forankret] | IT-driftshåndboken (2021) |
## Modellert besparelse (ex-ante)
> Forbruk etter trinn 1: 70 × 2 500 × 4 050 / 1 000 = **708 750 kWh/år**
> ΔW = 10,5 W/maskin
> kWh/år = 10,5 × 2 500 × 4 050 / 1 000 = **106 312,5 kWh/år**
> kr/år = **106 312,5 NOK/år**
Det er **15,0 %** av forbruket etter utskiftingen, som det må være — tallet er RF-marginen,
ikke et uavhengig estimat.
**Samlet med trinn 1:** 445 500 + 106 312,5 = **551 812,5 kWh/år**, altså **47,8 %** av de
2 500 maskinenes opprinnelige forbruk (1 154 250 kWh/år).
## Tre grunner til at dette tallet er en øvre grense, ikke et anslag
1. **Marginen er ikke gratis hele levetiden.** Effekttaket henter 15 % ved utrulling og
**null** ved slutten av levetiden, når maskinen faktisk trenger hele ytelsen.
Gjennomsnittet over levetiden er lavere enn 15 % — hvor mye lavere avhenger av hvor fort
programvarelasten vokser, som **ingen kilde her oppgir**.
2. **Dvale utover effekttaket er ikke modellert.** Bruksadaptiv dvale (maskinen sover når ingen
bruker den) og nattlig avstenging ville kommet i tillegg, men vi har ingen kvantifisering,
og IT-driftsavdelingens egen avstengingspilot (00:00–05:00, 2024) sier eksplisitt at tiltaket
må vurderes lokalt. **Ikke modellert.**
3. **Styringsverktøyet koster, og prisen finnes ikke i materialet.** **Ingen kilde gir NOK per
styringslisens eller for et komplett system for sentral strømstyring.** Vi anslår den ikke.
Uten kostnadssiden er dette et energitall, ikke en business case.
## Forholdet til validatoren
**Dette tiltaket er ikke projisert inn i `validator-input.json`.** IR-projeksjonen bærer ett
kandidat-tiltak, og det er trinn 1. Denne hypotesen er her som det den er: et **andre**
kandidat-tiltak lærings-sløyfa kan foreslå, med en modellert besparelse som er svakere
forankret enn den første — og en ekspert-dom som derfor har mer å korrigere.
Realiseringsgapet for styring er bredere enn for maskinutskifting. Virksomhetens egen driftslogg
fra idriftsettelsen av et driftssenter dokumenterer mønsteret rått: potensialet uteble fordi
**«viftene ble holdt på full hastighet mesteparten av tiden»**. En styring som overstyres av
drift, leverer null. Se [kilder-klientpark-realisering.md](kilder-klientpark-realisering.md) og
[verdict-klientpark-fro.md](verdict-klientpark-fro.md).

View file

@ -0,0 +1,133 @@
---
type: hypothesis
title: "Utskifting av stasjonære PC-er, trinn 1"
description: "Bytte 2 500 eldre stasjonære PC-er (114 W installert) til nye småformat-PC-er (70 W) på de eldste kontorene i porteføljen. Kandidat-tiltak med modellert besparelse, usikkerhet og en åpen kostnadsside."
resource: KLIENTPARK-ENERGI
measure_id: PC-KLIENT-01
tags: [PC-utskifting, arbeidsstasjoner, retrofit, ECM, trinn-1]
timestamp: 2026-08-09
---
# Tiltak: utskifting av stasjonære PC-er (trinn 1)
Utskifting av **2 500 av porteføljens 9 500 arbeidsstasjoner** — de eldste kontorene — fra
eldre stasjonære PC-er til nye småformat-PC-er. Trinnvis utrulling er den vanlige formen i
Eksempelvirksomhetens klientprosjekter: alderen på maskinen, ikke effekten, avgjør rekkefølgen.
Dette er tiltaket som er **projisert inn i validatoren**. Tiltak 2
([tiltak-adaptiv-stromstyring.md](tiltak-adaptiv-stromstyring.md)) forutsetter at dette er utført.
## Parametere
| Parameter | Verdi | Status | Kilde/forankring |
|---|---|---|---|
| Antall maskiner i trinn 1 | 2 500 | [I] | trinnvis utrulling, andel valgt |
| Effekt før (100 W systemenhet + tap) | 114 W | [V] | innkjøpsspesifikasjonens effekttabell |
| Effekt etter (ny småformat-PC, tilsvarende ytelse) | 70 W | [V] | samme tabell |
| Reduksjon per maskin (ΔW) | **44 W** | beregnet | 114 − 70 |
| Driftstimer (HOU) | 4 050 t/år | [V-forankret] | IT-driftshåndboken (2021): 4 000–4 100 t/år |
| Variabel energipris | 1,00 NOK/kWh | [V-forankret] | se [klientpark-energi.md](klientpark-energi.md) |
## Modellert besparelse (ex-ante)
Samme effektligning som virksomhetens beregningsmal bruker for alle effekttiltak (ligning 3):
`kWh = Σ (W_før − W_etter) × antall × HOU / 1000`
> ΔW = 114 − 70 = **44 W/maskin**
> kWh/år = 44 × 2 500 × 4 050 / 1 000 = **445 500 kWh/år**
> kr/år = 445 500 × 1,00 = **445 500 NOK/år**
Det er **38,6 %** av de berørte maskinenes eget forbruk (1 154 250 kWh/år) og **10,2 %** av
porteføljens totale forbruk.
**Ingen kjøle-interaktiv effekt er regnet med.** Spillvarmen fra en arbeidsstasjon havner i et
kontorlokale med egen ventilasjon, og beregningsmalens korreksjon for kjølelast (ligning 6) er
ikke brukt. Kjernetallet står uten den justeringen.
## Hvorfor 38,6 % og ikke 67 %
Region Vest anslo i 2022 **67 % energireduksjon** ved full utskifting av klientparken, til
≈ 200 mill. NOK og ≈ 27 mill. NOK/år spart. Vår bunn-opp-beregning fra merkeeffekt kommer til
38,6 %. Differansen er stor nok til at den må forklares, ikke bortforklares:
- Vår 38,6 % er **ren maskinutskifting**, regnet fra to merkeeffekter i samme effekttabell.
- Region Vests 67 % er et **regionalt aggregat-anslag** hvis sammensetning kilden ikke
bryter ned. Det er rimelig å anta at det også inneholder strømstyring og korreksjon av
overdimensjonering — men **kilden sier det ikke**, og vi tilskriver den ikke noe den ikke
skriver.
- Legger vi tiltak 2 oppå ([tiltak-adaptiv-stromstyring.md](tiltak-adaptiv-stromstyring.md)),
kommer vi til **47,8 %** — fortsatt et godt stykke under 67 %.
**Det gjenstående gapet på ~19 prosentpoeng er uforklart i materialet vårt.** Vi lar det stå
åpent. Det er en av grunnene til at en ekspert-dom, ikke en modell, må sette forventet
faktisk besparelse.
## Kostnadssiden — et navngitt evidenshull
**Ingen kilde i materialet gir NOK per arbeidsstasjon inkl. utrulling.** Region Vests
200 mill. NOK er en totalsum uten per-maskin-oppløsning, og antall maskiner oppgis ikke.
En per-maskin-kostnad kan **utledes**, men bare gjennom tre ledd som hver bærer sin egen
usikkerhet:
> 27 mill. NOK/år spart ÷ 1,00 NOK/kWh = 27 GWh/år spart
> 27 GWh/år = 67 % ⇒ baseline ≈ 40,3 GWh/år
> 40,3 GWh/år ÷ ~1 000 kWh per maskin (Region Nord) ≈ 40 300 maskiner
> 200 mill. NOK ÷ 40 300 ≈ **~4 960 NOK per maskin** `[U-utledet]`
Kjeden låner energiprisen fra vår egen baseline og maskin-intensiteten fra en **annen**
region. Den er en størrelsesorden, ikke et tall.
**Konsekvensen er ubehagelig og skal stå:** 2 500 × ~4 960 ≈ 12,4 mill. NOK mot 445 500
NOK/år spart gir **~28 års tilbakebetaling på energi alene**. Det er langt dårligere enn
Region Vests egne ~7,4 år, og forskjellen er ikke mystisk — våre maskiner er kontorklasse med
462 kWh/år, mot ~1 000 kWh/år i et aggregat som inkluderer beregningsmaskiner.
Lavforbruksmaskiner har dårligere energiøkonomi.
Et ekte klientprosjekt bæres derfor sjelden av energi alene. Vedlikehold er den andre
halvdelen: nye maskiner holder «Very good» i ≤ 5 år mens eldre maskiner trenger komponentbytte
langt hyppigere (Region Vest oppgir hvert 4. år). **Den besparelsen er ikke modellert her** —
vi har ingen kilde som kvantifiserer den i NOK, og vi fyller ikke hullet med et anslag.
**Ingenting av dette går inn i `cost-baseline.json`.** Kostbasen ligger på aggregat-nivå
(energi), der Region Nord- og Region Vest-tallene faktisk bærer.
## Usikkerhet (for Monte Carlo P10/P50/P90)
Den dominerende usikkerheten i en PC-besparelse er **driftstimer**, ikke pris — og for denne
klientparken er den verre enn i et bygg: **ingen kilde i materialet gir en målt
driftstimekurve for maskinene.** Driftshåndbokens 4 000–4 100 t/år er et tabellanslag for
«eldre arbeidsstasjoner», ikke en målt kurve for en bestemt maskinpark med bestemte arbeidsvaner.
Den eksisterende validatorens Monte Carlo varierer likevel **enhetspris**, ikke driftstimer.
I denne mappingen brukes derfor prisbandet **0,70–1,40 NOK/kWh** som usikkerhetsakse. Den
fysiske driftstime-usikkerheten — og viktigere, den *systematiske* driftstime-skjevheten —
håndteres i verdict-laget ([verdict-klientpark-fro.md](verdict-klientpark-fro.md)), ikke her.
## Mapping til validatoren (hvorfor `validator-input.json` ser ut som den gjør)
Den eksisterende deterministiske validatoren er en *feasibility-gate* (`claimed ≤ 30 % av
affected total`, Monte Carlo over enhetspris) bygd for kostnadskutt. PC-tiltaket mappes
inn **uendret**:
- `affected_items = [{code: "ENERGI-KLIENTPARK-EL", quantity: 4386150 kWh/år, unit_cost: 1.00 NOK/kWh}]`
→ **hele porteføljens** årlige energikostnad (4 386 150 NOK). Trinn 1-besparelsen er
10,2 % av den, godt innenfor 30 %-cap-en.
- `claimed_saving_nok = 445500` → den modellerte besparelsen fra trinn 1.
- `assumptions = {"ENERGI-KLIENTPARK-EL": [0.70, 1.40]}` → prisbandet for Monte Carlo.
**Hvorfor porteføljen og ikke bare de 2 500 maskinene:** hadde `affected_items` vært de
berørte maskinenes eget forbruk (1 154 250 kWh), ville den modellerte besparelsen vært 38,6 %
av den — **over 30 %-cap-en**, og det riktige forslaget ville blitt avvist av en gate som
måler feil størrelse. Porteføljen er den korrekte kostnads-linjen tiltaket virker på, på
samme måte som byggets totale elforbruk er det for et innendørs LED-tiltak.
**`cost-baseline.json` bærer nøyaktig samme rad.** `code`, `quantity` og `unit_cost` er
identiske i de to filene — ikke «innenfor toleranse», men identiske, fordi begge er skrevet
fra linjen `114 W × 9 500 × 4 050 t / 1 000`. Hver `code` i `affected_items` finnes som
nøkkel i `items`.
**Ærlig begrensning:** validatorens P10/P50/P90 betyr her «øvre feasible grense» (30 % av
samplet energikostnad), *ikke* «PC-besparelsens fysiske band». Det er bevisst — den domenetro
besparelses-modelleringen og realiseringsgapet hører hjemme i verdict-laget, som er nettopp
det lærings-sløyfa skal lære.

Some files were not shown because too many files have changed in this diff Show more