docs(p18): stress round 2 -- five paid runs, and the mutation that found a hole

P18 parts D and E (order 20260914T105139Z), plus the two things measuring
them turned up.

DEL D -- five paid runs (gpt-4-1-mini, azure, PACE_SECONDS=2), same four
context sets, SAME parameters on all four (--max-rounds 3 --max-tokens
600000), plus one variance repeat of gate-nordvik. All four free
--live-dry-runs first: rc 0, and Grounding-offer numbers IDENTICAL to round 1
(435/272/982/3) -- the control that the Grounding structure did not change
what the gate measures.

MEASURED, round 1 -> round 2:
- runs that died on the token cap: 3 of 7 -> 0 of 5, and two sets now fit a
  LOWER cap than round 1 had to give them;
- guessed read paths: 12 -> 0;
- wall time, the two sets whose parameters are directly comparable: n100
  140.1 s -> 70 s, n500 73.5 s -> 69 s;
- 5 of 31 read_file calls opened documents BEYOND the default window, so the
  window WAS widened -- the trace does not record which knob (finding 1);
- (a) grounded in a fasit concept: 0 of 26, UNCHANGED. That is the mission
  gap, and DEL A did not close it.

The a4 falsification arm fails once in each round, on a different set. r761's
a4 is now rejected -- but NOT by B1: the model proposed "Kontraktsum" this
time, so the "appears nowhere" arm caught it, and B1's effect on that row is
proven offline, not live. NEW failure: tunnel-hauglia a4 VALIDATED on
"impulsventilator" (3/270 documents), and fv412 a1 on "bituminost barelag"
(4/1133). Both are ordinary Norwegian words from the standard's prose, not
cost codes. B1 cannot and should not fell them: this is an ANCHORING defect,
not a grounding one, and it is finding 2 with two named remedies and a
recommendation.

C2 isolated by re-judging round 1 with the new judge: kontrakt-sorasen goes
named=3 -> named=1, and the survivor is named_in_measure -- the model's own
words. Two of the three were the whole-base snippet artefact.

DEL E -- docs/2026-09-14-p18-stressrunde-2.md: round 1 against round 2, what
each fix bought (measured, never attributed), the B2 table, variance, and for
EACH remaining ugly finding a NAMED solution with an estimate.

THE MUTATION THAT FOUND A HOLE. B6 (revert run.py to compose ONE blob instead
of one document per concept file) left the WHOLE suite green: 1698 passed / 5
skipped. The composition arm drives _grounding_text with a Grounding it
builds ITSELF, so it cannot see what the RUN handed over -- and a blob has
exactly one boundary, so the floor can never be reached, the share can never
fire, and the measured defect is back intact. The rule is only as good as the
boundaries it is given.

Arm (h) is the gate that was missing: a crafted base with TWELVE concept
files all carrying the same token -- per document 12 of 17 and inert, as one
blob 1 of 1 and grounding -- with a control on a code only ONE file carries,
which must still validate. Measured RED against exactly that mutation. The
mutation was not dropped and the seam was not declared unwitnessed: it got a
witness.

Also: the debate's own bundle pointer (run.py _bundle_pointer) now explains
the window and the filter, alongside the tool description and the navigator
instruction updated in 9b47e5a -- a description that lies about the body IS
the model's instruction (the Fase 3 class). Golden transcript unaffected.

Mutations, all against the FULL suite in an isolated worktree, one at a time:
DEL A 7 of 7 red (control 1685/5), DEL B+C 9 of 10 red (control 1698/5), the
tenth being B6 above. Tables in the report s 9.

Verification: uv run pytest -q 1699 passed / 5 skipped (1670 on cfd9079; +29,
0 removed). ruff check + format clean, mypy clean (38 files). Golden
demo-transcript.stdout BYTE-UNCHANGED, shasum -a 1 of the CONTENT =
ea8c534773acdbe41ae68f2c55724d69aaf8be4f. No version bump, no push.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-15 00:03:26 +02:00
commit e7ba367f9d
4 changed files with 432 additions and 5 deletions

View file

@ -2520,7 +2520,17 @@ Python ≥3.10. MAF (`agent-framework-core` 1.16.0, `-orchestrations` 1.1.1 —
at vinduet er fullstendig og ikke-overlappende. **Ærlighets-grenser, uttalt:** `navigate_bundle`
kalles fortsatt per verktøykall (I/O, ikke tokens); A3-nekten navigerer basen ÉN gang ekstra på
feilstien for å finne den nærmeste listbare katalogen; at en LEVENDE modell BRUKER `filter` er
ikke bevist (structured-output-grensens klasse) — DEL D er målingen.
ikke bevist (structured-output-grensens klasse) — DEL D er målingen. Load-bearing MÅLT
(`tests/test_navigation_window_loadbearing.py`, 13 armer), **sju mutasjoner alle røde mot HELE
suiten** + grønn kontroll 1685/5 og golden BYTE-UENDRET: ingen vindu (8 røde) · bundet men VAKUØST
(**26**, tolv filer hvorav ni eldre enn dette arbeidet — dét er hva som skiller bindingen fra en
gate som bare kan bli grønn) · `total` droppet (11) · filteret leser bare tittelen (1) · en
fraværende sti reiser igjen (4) · et filter uten treff NEKTER (1) · filteret snevrer ikke
kataloger (1). Verktøybeskrivelsen, navigatør-instruksjonen OG debattens `_bundle_pointer` flyttet
i SAMME commit-serie — en beskrivelse som lyver om kroppen ER modellens instruks (Fase 3-klassen).
**MÅLT LEVENDE (DEL D):** 0 gjettede lesestier i fem betalte kjøringer (runde 1: 12), og 5 av 31
`read_file`-kall åpnet dokumenter UTENFOR default-vinduet — altså ble vinduet utvidet, men sporet
sier ikke med hvilken knapp (funn 1 i rapporten).
- **En identifikator som står OVERALT identifiserer INGENTING — og grunnlaget bæres som DOKUMENTER,
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
@ -2551,8 +2561,19 @@ Python ≥3.10. MAF (`agent-framework-core` 1.16.0, `-orchestrations` 1.1.1 —
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
substitutt for B1; målt, ikke bygget. Load-bearing MÅLT
(`tests/test_inert_identifier_loadbearing.py`, 7 armer): kjent-positiv er P16s EGET artefakt
spilt av mot basen den fikk, kjent-negativ er **26 av 26** fasit-referanser som fortsatt grunner.
(`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
andels-konjunktet (2) · dropp det ABSOLUTTE gulvet (**74** — hver pre-P18-fixtur brekker, altså er
gulvet dét som gjør regelen URØRENDE for dem i stedet for å unnta dem) · dropp nevneren i grunnen
(1) · dropp lengde-konjunktet (1) · blob-komposisjonen i `run.py` (**0** — se under).
Kjent-positiv er P16s EGET artefakt spilt av mot basen den fikk; kjent-negativ **26 av 26**
fasit-referanser. **ÉN MUTASJON VAR GRØNN, OG DET ER ET FUNN (repoets vakuøs-gate-klasse,
SEKSTENDE gang):** å reversere `run.py` til ÉN blob lot HELE suiten stå grønn — 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 (gulvet kan aldri nås, andelen aldri fyre, defekten er
tilbake intakt). Arm (h) er gaten som mangler: 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. Målt rød mot nettopp den mutasjonen.
**Ærlighets-grenser, uttalt:** regelen er per-DOKUMENT, så en base med ett gigantisk dokument er
upåvirket; `assumptions`-nøkler er fortsatt utenfor (økt 109s grense); og ingen betalt kjøring
bekrefter at den endrer utfallet levende før DEL D.
@ -2565,13 +2586,18 @@ Python ≥3.10. MAF (`agent-framework-core` 1.16.0, `-orchestrations` 1.1.1 —
«`--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
`--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`,
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
§ 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
n100-bodyer» — holder for `Krav 4.1.2—1`, men **IKKE for R761**, der et prosessnummer som `12.1`
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
er PARET av to armer med SAMME snippet og SAMME merke, der eneste forskjell er scopet.
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
med den nye dommeren tar kontrakt-sorasen fra `named=3` til `named=1`, og den ene som står igjen
er `named_in_measure` — modellens egne ord.
- **STATE.md er local-only** (gitignored). Voyage session-state er efemert; STATE.md er kanonisk kontinuitet.
- Prosess: Voyage-plugin (`/trekbrief → /trekplan → /trekexecute → /trekreview`) per større fase.

View file

@ -0,0 +1,324 @@
# 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 121209 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"``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 853169 974 → 4791 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,
**denne rapporten kan ikke oppgi tokenforbruk per kjøring.** **Løsning:** legg `tokens_spent` og
`rounds``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.
---
## 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

@ -603,7 +603,11 @@ def _bundle_pointer(bundle: okf.Bundle, bundle_id: str, *, dimension: str | None
f"{scope}\n"
"It is NOT included here — read it with your tools: list_bundles() for the bases, "
f"read_bundle({bundle_id!r}) for its top level, read_dir({bundle_id!r}, path) for one "
f"directory, read_file({bundle_id!r}, path) for one document. Open what you need."
f"directory, read_file({bundle_id!r}, path) for one document. Open what you need.\n"
"A listing is a WINDOW: it reports 'total' for the level and gives you 'limit' entries "
"from 'offset'. When 'total' is large, do not page through it — narrow it: "
f"read_dir({bundle_id!r}, path, filter='<word>') answers with the entries whose title, "
"requirement number or path contains that word, and reports 'total_matches'."
)

View file

@ -219,3 +219,76 @@ def test_the_run_hands_the_gate_one_document_per_concept_file() -> None:
assert composed.text == "\n".join(
["dokument A", "dokument B", "dokument C", "PRJ-01", "BAS-01"]
)
@pytest.mark.asyncio
async def test_the_boundaries_survive_the_real_run_and_not_only_the_composer(
tmp_path: Path,
) -> None:
"""(h) BEHAVIOURAL, over the real ``run_project`` bundle arm — and it exists because a mutation
found the gap, not because it was foreseen.
Reverting ``run.py`` to compose ONE blob instead of one document per concept file left the WHOLE
suite green (1698 passed / 5 skipped). Arm (g) above drives ``_grounding_text`` with a
``Grounding`` it builds itself, so it can never see what the RUN handed over exactly the
vacuity this repo keeps measuring. The rule is only as good as the boundaries it is given, and
a blob has exactly one: with a single document the absolute floor can never be reached, so the
share can never fire and the measured defect returns intact.
The base is crafted so the two implementations must DISAGREE: twelve concept files all carrying
the same token, which is past ``_GROUNDING_MIN_INERT_DOCUMENTS``. Per document it is in 12 of 13
and inert; as one blob it is in 1 of 1 and grounds. The CONTROL is the same run with a code only
ONE file carries, which must still validate otherwise the arm would also pass on a run that
rejects everything.
"""
import shutil
from portfolio_optimiser.run import run_project
from portfolio_optimiser.simulation import ScriptedChatClient
base = tmp_path / "base"
shutil.copytree(Path("shared/examples/bygg-energi-mikro"), base)
links = []
for n in range(12):
rel = f"seksjon-{n:02d}.md"
(base / rel).write_text(
f"---\ntype: concept\ntitle: Seksjon {n:02d}\n---\n\n"
"KORPUSMERKE-77 gjelder overalt i denne basen.\n"
+ ("Kostlinjen EN-ENESTE-01 star bare her.\n" if n == 0 else ""),
encoding="utf-8",
)
links.append(f"- [Seksjon {n:02d}]({rel})")
index = base / "index.md"
index.write_text(
index.read_text(encoding="utf-8") + "\n" + "\n".join(links) + "\n", encoding="utf-8"
)
async def _outcome(code: str) -> object:
reply = (
f'{{"measure":"LED-retrofit","affected_items":'
f'[{{"code":"{code}","quantity":300000,"unit_cost":1.0}}],'
f'"claimed_saving_nok":30000}}'
)
result = await run_project(
"BYGG-KONTOR-NORD",
"local",
docs_dir=str(base),
bundle_dir=str(base),
client_factory=lambda role: ScriptedChatClient(
"Reasoning holds.\nVERDICT: APPROVE" if role == "checker" else reply, role=role
),
)
return result.outcome
control = await _outcome("EN-ENESTE-01")
assert isinstance(control, ValidatedProposal), (
f"CONTROL: a code only one concept file carries must still ground — {control}"
)
everywhere = await _outcome("KORPUSMERKE-77")
assert isinstance(everywhere, Rejection), (
"a token every concept file carries grounded a proposal — the run handed the gate one blob"
)
# The NUMERATOR is the discriminator: as one blob the token is in 1 of 1, so a refusal naming
# 12 can only come from a run that kept the concept files apart.
assert "appears in 12 of the " in everywhere.reason, everywhere.reason