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

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
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.
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 |
|---|---:|---:|---:|---:|---:|---:|
| 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, N200:2024 | 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 A | 40 | 63 375 | **33 889** | 11 679 | **5 017** | −85,2 % |
| Grener, kravbase B | 99 | 134 667 | **71 726** | 28 894 | **12 396** | −82,7 % |
| 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 %** |
| *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.
**Nevner-avvik mot ordren, uttalt:** ordren oppgir 34 grener for N100:2023. Målt på disk
(`ls ~/repos/vegnormal-okf/build | grep -c '^B-n100-2023-gren-.*-importert$'`) er tallet **40**.
N200:2024 = 99 og N500:2024 = 32 stemmer. Tallene over bruker den målte nevneren, ikke ordrens.
**Nevner-avvik mot ordren, uttalt:** ordren oppgir 34 grener for kravbase A. Målt på disk
(grenkatalogene i korpusbyggets `build/`, talt med `grep -c`) er tallet **40**.
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** —
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
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
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
faktisk velger bedre med et utdrag enn med hele indeksen er ikke målt (samme klasse som
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
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) ·
[misjonsreview 25.08](2026-08-25-fable-misjonsreview.md). Golden `demo-transcript.stdout`
**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 |
| 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 |
| 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/` |
| 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`
**Oppsett.** Fire baser: `shared/examples/{bygg-energi-mikro, veglys-fv-soer, tunnel-hauglia}` +
`~/repos/vegnormal-okf/build/B-n200-2024-gren-1-1-importert` (grenbasen katalogmålingen 26.08 brukte;
10 filer). Kontrakt: `max_rounds=6, max_tokens=100000, max_stall_count=2, max_reset_count=1,
**Oppsett.** Fire baser: `shared/examples/bygg-energi-mikro`, to energi-eksempelbaser som den gang
lå i commons (her `energi-a` og `energi-b`; i dag erstattet av de fiktive `klientpark-energi` og
`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
verktøykall ved å wrappe `FunctionTool.func` ETTER konstruksjon — verktøylisten agentene får er
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 |
|---|---:|---|---:|---|---|
| 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 |
| tunnel-hauglia | 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` ✅ |
| energi-a | 0 | 8 (4/3/1) | **0** | 1 / 0 / 32 | samme |
| energi-b | 0 | 8 (4/3/1) | **0** | 1 / 0 / 32 | samme |
| 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
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 |
|---|---|---|---:|---|---|---|
| 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 |
| tunnel | **1/1/1/1** | 12 (6/4/2) | 3 | 1 (…, `tunnel-hauglia`) | 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 |
| energi-a | **1/1/1/1** | 12 (6/4/2) | 3 | 1 (…, `energi-a`) | validated, anchored=true | 3 → 122 → 10 553 → 11 869 |
| energi-b | **1/1/1/1** | 12 (6/4/2) | 3 | 1 (…, `energi-b`) | validated, anchored=true | 3 → 148 → 12 767 → 14 429 |
| 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å
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 |
|---|---:|---:|---:|---:|---:|---|
| 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 %** |
| tunnel (ctx 12 595) | 40 605 | 25 376 (62 %) | 12 759 (31 %) | 2 243 (6 %) | 227 (1 %) | 3× = **93 %** |
| energi-a (ctx 10 406) | 34 001 | 20 984 (62 %) | 10 556 (31 %) | 2 243 (7 %) | 218 (1 %) | 3× = **92 %** |
| 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):**
| 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 %** |
| veglys | 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 %** |
| B-n200-gren (ctx 2 307) | 24 761 | 8 532 (34 %) | 6 254 (25 %) | 5 666 (23 %) | 3 493 (14 %) | 816 (3 %) | 7× = **65 %** |
| energi-a | 86 013 | 26 262 (31 %) | 23 984 (28 %) | 22 547 (26 %) | 12 404 (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 %** |
| 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
`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
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 ×**.
**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
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
(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.
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 |
|---|---:|---|---|
| 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 |
| 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 % |
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 =
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
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_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.
## 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`,
> `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`
> — `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
> 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).

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).
**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
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 |
|---|---:|---:|---:|---:|---:|---:|---:|
| `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 |
| `tunnel-hauglia` | 39 583 tegn / **12 595 tok** | 19 | 106 519 | **5** | 65 698 | 3 | 37 943 |
| `energi-a` | 32 201 tegn / **10 406 tok** | 19 | 88 881 | **5** | 54 623 | 3 | 31 376 |
| `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) ·
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
@ -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) |
|---|---:|---:|---:|---:|
| `bygg-energi-mikro` | 6 | 4 | 1 | 1 884 tegn |
| `veglys-fv-soer` | 7 | 5 | 1 | 3 646 tegn |
| `tunnel-hauglia` | 7 | 5 | 1 | 4 763 tegn |
| `energi-a` | 7 | 5 | 1 | 3 646 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
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
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
@ -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>)` |
| 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 |
| 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 |
|---|---|---:|---|---:|---|
| `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 %) |
| `tunnel-hauglia` | 12 595 → **259 tok** | 5 → 5 | 62 975 → **1 295** | 67 415 → **5 973** (−91 %) | 106 519 → 45 077 (−58 %) |
| `energi-a` | 10 406 → **237 tok** | 5 → 5 | 52 030 → **1 185** | 56 314 → **5 742** (−90 %) | 88 881 → 38 309 (−57 %) |
| `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;
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`).
**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
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.
@ -137,7 +139,7 @@ instrument man selv har erklært ødelagt, er nøyaktig dét dette avsnittet adv
## 6. Ærlighetsgrenser, uttalt
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
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.

View file

@ -32,7 +32,7 @@ Diagnosen stemmer derimot ikke. **Forslaget har aldri blitt lest fra den fila.**
tatt.
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
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
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
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

View file

@ -19,10 +19,11 @@ prosjektavhengighet.
| 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** |
| `bundle_context` over `tunnel-hauglia` | 12 595 tok (samme) | **12 595 tok** |
| `read_bundle` over `energi-b` | 259 tok (`docs/2026-09-02-read-bundle-kontekstkostnad.md`) | **259 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
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 |
|---|---|---|
| 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 |
| 3 | `N=43` ikke verifiserbart | ingen `log.md` i bundelen (`Path(base)/'log.md').exists()` → `False`) |
| 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å |
|---|---|---|
| `read_bundle` over `tunnel-hauglia` | 259 tok | **259 tok** |
| `bundle_context` over `tunnel-hauglia` | 12 595 tok | **12 595 tok** |
| `read_bundle` over `energi-b` | 259 tok | **259 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
`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 |
| (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** |
| (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** |
| (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
S7c viser nettopp at BANEN varierer (5 vs 11 prompter, `ValidatedProposal` vs `Rejection`) selv
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` /
`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

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'` |
| (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 |
| (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 |
---
@ -42,10 +42,11 @@ i det hele tatt.
## 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`,
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**).
| 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` 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` |
| 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:
@ -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
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
kjøringen produserte som ingen prompt bærer — FELLES. Uten den kontrollen ville en grønn
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
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å
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`
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

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`
(→ 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
`over_budget_after_knapsack`. Målt med `scratchpad/p11/bisect/` og `scratchpad/p11/exponent/`; tall
og kommandoer står i `docs/2026-09-11-p11-okf-081.md` § 4 og § 9.
`over_budget_after_knapsack`. Målt med `scratchpad/p11/bisect/` og `scratchpad/p11/exponent/`; P11-rapporten
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.
@ -215,7 +215,7 @@ uten et endret tall er støy.
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
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.
* 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.

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 |
| 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 |
| 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 |
| 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 |
@ -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 |
| 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) |
## § 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
**Beslutningen, ordrett** (`docs/2026-09-09-p8-forankringstilbudet.md` l. 162–166): «De leverte KUTTENE
(10 kB N100, 96 kB K2) er ikke sporet: å committe verbatim anbudstekst og standardtekst inn i et repo
**Beslutningen, ordrett** (P8-rapporten, nå fjernet; invarianten står i `docs/invarianter.md`): «De leverte KUTTENE
(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.»
**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
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
`shared/examples/veglys-fv-soer/cost-baseline.json` — **two bundled examples ship one** (that one
and `tunnel-hauglia`, checked 2026-08-21), and its shape is `ir.CostBaseline`: a `project_id` plus
`src/portfolio_optimiser/data/bundles/klientpark-energi/cost-baseline.json` — **two bundled
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
unbuilt, and is not on the 90 %-principle side of the line: what candidate to propose is the
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()`
calls**: each run's participants receive the PRIOR projects' prompts and replies too.
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
bleed (G2/B7)**.
- **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],
[BRU-LAKS-REHAB]]` → **zero cross-run contamination** (B7 mitigation works).
each participant sees ONLY its own project: `[[KONTOR-IT-E1], [NETT-SIKR-TP],
[ARKIV-LAGR-MIGR]]` → **zero cross-run contamination** (B7 mitigation works).
- **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
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`
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**.
- 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
med `quantity`/`unit_cost` utenfor `tolerance` (default 5 %, **konfig**) relativt til BASELINE-verdien.
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
`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
@ -389,7 +389,7 @@
`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
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.
**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
@ -1032,8 +1032,8 @@
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* —
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
**124 942 over de 171 grenbasene** som erstattet dem — grenformen (`vegnormal-okf` `8145c23`)
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 (korpusbyggets `8145c23`)
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).
**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
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
(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`.
- **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
@ -1116,12 +1116,12 @@
`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,
`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`** —
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
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
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:**
@ -1401,7 +1401,7 @@
ikke bevist (structured-output-grensens klasse).
- **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
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
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
@ -1477,7 +1477,7 @@
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
`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.
**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
@ -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
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
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
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
@ -2218,7 +2218,7 @@
basen og artefaktene økt 98 etterlot. Måling:
`docs/2026-09-08-f3-f4-nekten-og-forankringen.md` § 2.
- **`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 —
`PrepassExcerpt` var `extra="ignore"` ved parsing, og `_data_blocks` rendrer bare
`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
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**:
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
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
@ -2236,9 +2236,9 @@
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
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,
NOK 0,00** — (b′) forblir STRUKTURELT umålbar under flagget (F4: en vegnormal bærer ingen
kostlinjer, uendret av denne fiksen). Måling: `docs/2026-09-08-n-bundlene-hypoteseform.md` § 10.
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 kravstandard bærer ingen
kostlinjer, uendret av denne fiksen).
- **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`
(`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
`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
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
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`):
@ -2319,7 +2319,7 @@
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,
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
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
@ -2338,8 +2338,7 @@
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
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:
`docs/2026-09-09-p8-forankringstilbudet.md`.
/ 832 filer) er en senere, separat ordre.
- **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),
`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»:
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.
**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.
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,
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.
**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
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
`title` overskriver konseptets egen — `directory_listing` på `krav/N500` returnerer **269
dokumenter, alle med `"title": "N500:2024"`**, og navigasjonsstigens rung 2/3 skiller dem kun med
`title` overskriver konseptets egen — `directory_listing` på ett kravnivå i en av kravbasene returnerte **269
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
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
@ -2375,15 +2374,16 @@
`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
`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
`honesty`-felt hva jeg konstruerte — navn, lengder, ÅDT og alle tolv beløp; `affected_codes` er
syntetiske i tre sett og EKTE `prosessnr` i `kontrakt-sorasen-2027`, som gjør de tre andre bevisst
**Ærlighets-grenser, uttalt:** de fire prosjektene var OPPDIKTET (hvert sett sa i sitt eget
`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 prosesskatalog-settet, som gjorde de tre andre bevisst
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.
**(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_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,
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
@ -2391,12 +2391,12 @@
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
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
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
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
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».
@ -2426,7 +2426,7 @@
helbase-snippet som baerer en `ref` er svakere bevis enn modellens eget `measure` — derfor er de
to halvdelene skilt i utfallet.
- **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
--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
@ -2455,8 +2455,9 @@
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
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
dokumenter, `krav/N100` 69 250 over 445, og R761s egen rot **110 912 over 2 728 UNDERKATALOGER** —
det. P16 målte hva ett nivå koster på et LEVERT korpus (et kravkorpus brukt under utviklingen): det
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.
**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
@ -2464,8 +2465,8 @@
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
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,
R761 479, og n200 på **1 537**, 2,5 % over, fordi taket er et TEGN-budsjett og vinduet er et
STØRSTE er 2 090 tegn med oppføringer — kravbase A lander på 1 493 (ordrens bundne krav), kravbase C 1 453,
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
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
@ -2516,7 +2517,7 @@
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
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`).
**`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,
@ -2526,9 +2527,9 @@
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 % —
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
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
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
@ -2538,9 +2539,10 @@
**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»
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
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
(`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
@ -2560,24 +2562,24 @@
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å
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`
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
«`--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.
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`
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,
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
(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.
- **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
@ -2624,8 +2626,8 @@
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
(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
`bituminøst bærelag` (4 av 1 133 N200). Begge er GRUNNET i P7s forstand (de står ordrett i
vanlige ord fra en kravstandards prosa (her gjengitt som `nødstrømsaggregat`, 4 av 270 dokumenter
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
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`
@ -2634,11 +2636,11 @@
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
(`generate` importerer `validator`, aldri omvendt).
**B1 — to nye former, transkribert fra måling:** R761s kravnumre er bare punktum-tall (`12.1`,
`52.11`), ALLE SEKS `ref`-verdier i `contexts/kontrakt-sorasen-2027/fasit.json` er av den formen,
og INGEN av de to pre-P19-formene matchet én av dem — r761s hele tilbud var **3 identifikatorer
over 6,5 MB**. Målt etter: **2 332** (n100 435→578, n200 982→1 512, n500 272→391). Den fjerde
formen er `65 ASFALTDEKKER`. **Den FØRSTE formen ble UTVIDET i samme slengen, og dét er en
**B1 — to nye former, transkribert fra måling:** prosesskatalogens prosessnumre er bare
punktum-tall (`12.1`, `52.11`), ALLE SEKS `ref`-verdier i prosesskatalog-settets `fasit.json` er
av den formen, og INGEN av de to pre-P19-formene matchet én av dem — prosesskatalogens hele
tilbud var **3 identifikatorer over 6,5 MB**. Målt etter: **2 332** (kravbase A 435→578,
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
`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
@ -2649,9 +2651,9 @@
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
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
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 —
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
@ -2752,25 +2754,26 @@
sekvensiell).
- **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
ett multi-base-pass bar `validated` forslag hvis kostkode var et kapittelnummer i en vegnormal.
To overlever i utboksene og er kjent-positivene: **`10.4`** (tunnel-hauglia runde 3, n500) og
**`1.10.4`** (lindaas P17b, r761). Begge er GRUNNET i P7s forstand og ingen er INERT i P18/B1s
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`** (et kravbase-sett, runde 3, kravbase C) og
**`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
vegnormal-base 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
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: 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
et seksjons-PREFIKS; og r761 erklærer 2 727 `prosessnr` + 2 753 `seksjon`, hvorav **ingen** er
`1.10.4`, som står ÉN gang, som prosa: «iht. vegnormal N200 Vegbygging kap. 1.10.4». **Den
et seksjons-PREFIKS; og prosesskatalogen erklærer 2 727 `prosessnr` + 2 753 `seksjon`, hvorav **ingen**
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,
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
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
SPARER det ene kontekstsettet 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; dét er R761-risikoen ordren
selv navngir, ankommet gjennom døra den ble pekt bort fra. **MÅLT over ALLE 24 koder i runde 3 +
SPARER det ene kontekstsettet bygget på ekte prosessnumre:** alle fem kodene i
prosesskatalog-settet (i dag `contexts/driftsavtale-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 prosesskatalog-kjøring, og settets positive armer blitt umålbare; dét er
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
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
@ -2816,7 +2819,7 @@
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
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å å
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.
@ -2832,11 +2835,11 @@
- **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,
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
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å
sorasen, `1.1.1` på lindaas) som validerte med beløp ingen hadde noe sted.
`validated` kunne ikke bety det det sier: P20 G1/G2 målte EKTE prosessnumre (`12.11` ×3 på
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:
(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
@ -2872,9 +2875,9 @@
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
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`,
`52.11`, `51.1` er ekte R761-`prosessnr`). Det er ikke et uhell i settet; det er hva R761
Prosesskoden ER — en norsk vegkontrakts mengdebeskrivelse prises BY prosesskode — så ordrens
settene bærer **0**, og prosesskatalog-settet bærer **5 av 5** (`12.1`, `12.12`, `22.1`,
`52.11`, `51.1` er ekte `prosessnr` i katalogen). Det er ikke et uhell i settet; det er hva en
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 å
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
@ -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.
**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
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
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
@ -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
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
som vandrer `R761/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`,
`R761/42`. Nekten navnga alt den nærmeste LISTBARE forfaren, som er riktig rung; det den ikke
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 `P900/4`, `P900/41`,
`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` +
`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
@ -2933,7 +2936,7 @@
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
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
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
@ -2944,7 +2947,7 @@
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).
**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
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
@ -2957,7 +2960,7 @@
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 —
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
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.
@ -2974,10 +2977,10 @@
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
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
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
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).
@ -3009,14 +3012,14 @@
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
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
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
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).
**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
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:
@ -3042,8 +3045,8 @@
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
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
gjetninger i samme kjoering, `R761/1/1-1.md` og `R761/1/R761-1-1_id-...md`, begge strakte seg
slik forfar** (`krav/D100` med 445 dokumenter; `P900/1` med NOEYAKTIG ETT — som to separate
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
`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
@ -3058,7 +3061,7 @@
**`_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
(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
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
@ -3090,7 +3093,7 @@
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:**
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
rettes (gatet av en egen arm).
- **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
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
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
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
@ -3149,14 +3152,14 @@
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).
`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
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
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
(`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
`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
@ -3174,7 +3177,7 @@
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
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
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
@ -3219,7 +3222,7 @@
`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
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
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
@ -3227,13 +3230,16 @@
(`tests/test_row6_declaration_rule_loadbearing.py` + rad 6-probene), ti mutasjoner alle røde.
- **Målingene leser en FROSSET kopi av korpuset, pinnet med sha256 — aldri et annet repos levende
build-mappe (18.09, ordre `20260917T223645Z-1296211942`):** gaten, stressdommeren og fire
korpus-tester leste `~/repos/vegnormal-okf/build/ferdig` DIREKTE. **MÅLT 17.09 17:43:** vegnormal
bygget om `r761-2025`, rad 6–7 sa «IKKE MÅLT» og fem tester falt, for en endring ingen her gjorde.
korpus-tester leste et annet repos levende `build/ferdig`-mappe DIREKTE. **MÅLT 17.09 17:43:**
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
USTABILITET: to prosjekter delte en mappe ingen av dem eier, så hva dette repoet MÅLER kunne
flytte seg uten en commit her. **En kopi alene ville bare skjøvet den mappa ett hakk unna**, så
kopien kommer med en PIN: `frozen_bundles.json` (tracked) bærer sti + sha256 + filantall per base,
mens BUNDLENE ALDRI committes her (vegnormal-korpus skal ikke til en offentlig flate).
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
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
@ -3245,19 +3251,19 @@
bytene** — uten det ville et korpus stokket om under samme bytes pinnet rent — og katalognavnet
BÆRER de 12 første tegnene av digesten, så en foreldet kopi er synlig i `ls`, ikke bare for
verifisereren. **Fornyelse er en BESLUTNING, aldri rydding:** ny kopi + ny pin i SAMME commit
(README § Frozen knowledge bases). `--bundle-root` / `PORTFOLIO_VEGNORMAL_ROOT` består som
(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
bestemmer seg for å fryse på nytt; uten den kan ikke gaten brukes til å ta den beslutningen.
**Grep-gaten dekker BEGGE stavemåtene, hver med sin egen kjent-positiv:** `vegnormal-okf/build`
(3 treff før) og det siterte sti-segmentet `"vegnormal-okf"` (4 treff før) — en fil-bred gate med
**Grep-gaten dekker BEGGE stavemåtene av det andre repoets byggesti, hver med sin egen
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
(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
røde mot HELE suiten** + grønn kontroll **1984/5 + 5 xfailed** og node-ID-supersett (1977 → 1994,
**0 fjernet**): M1 pinnen verifiseres aldri (7 røde) · M2 avvik kollapset inn i «mangler» (5) ·
M3 navnet hashes ikke (40) · M4 gate-sømmen reverteres til `root/name` (1) · M5 korpus-testene
skipper på avvik også (4 — én per fil) · M6a `vegnormal-okf/build` tilbake i `src` (1) ·
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
(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
@ -3269,7 +3275,7 @@
kjørt med `--bundle-root` måler ikke det pinnede korpuset og sier det ikke — pinnen er default,
ikke et påbud; digesten dekker hver fil, så en `.DS_Store` som dukker opp i kopien er et AVVIK
(MÅLT: null `.DS_Store` og null symlenker i alle fire basene da kopien ble tatt); og kopien ble
tatt 18.09 fra vegnormals mappe KUN ved lesing — kildens mtimer er uendret.
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
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
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
[`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
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 |
|---|---|---|
| **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 |
| **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
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)
går gjennom en veglysportefølje langs fylkesveg, fra oppdragsfila til en base som består
kjøreklar-sjekken. Basen det ender i finnes og kjører (VERIFISERT: `shared/examples/veglys-fv-soer/`
er den demoen bruker).
**3. Hvordan ser det ut for et konkret prosjekt?** [§5](#5-klientparken-hos-eksempelvirksomheten-fra-bestilling-til-kjøreklar-base)
går gjennom klientparken til den oppdiktede Eksempelvirksomheten, fra oppdragsfila til en
kjøreklar base. Basen det ender i er sjekket inn (VERIFISERT:
`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
@ -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
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:
`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.
*Logisk* er det samme fagstoff: alle tre eksempelbasene (kontorbygg, veglys, tunnel) bærer en
`metode-ipmvp-a.md` og en `kilder-*.md`, og alle tre bygger på samme M&V-rammeverk (IPMVP Option
A). *Fysisk* er det tre ulike filer: 40, 81 og 98 linjer, med hver sin tittel — «for veglys — og
hvorfor de andre opsjonene er stengt», «for tunnelstyring — anlegget måler inngangssignalet, ikke
energien» (VERIFISERT: `wc -l` + `diff` over de tre). Begge veiprosjekt-basene sier det selv i
`index.md`: «metode- og kildelaget er **materialisert inn her**, ikke lenket på tvers av bundler».
*Logisk* er det samme fagstoff: alle tre eksempelbasene (kontorbygg, klientpark, kjøling i
driftssenteret) bærer en `metode-ipmvp-a.md`, og alle tre bygger på samme M&V-rammeverk (IPMVP
Option A). *Fysisk* er det tre ulike filer — målt 2026-08-21 til 40, 81 og 98 linjer — med hver
sin tittel: «for klientparken — og hvorfor de andre opsjonene er stengt», «for kjølestyring —
anlegget måler inngangssignalet, ikke energien» (VERIFISERT: `wc -l` + `diff` over de tre). Begge
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
([§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
anlegget mangler måler; i tunnel er ex-ante stengt fordi anlegget ble 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
faglig riktig: metoden *for klientparken* er ikke metoden *for kjøleanlegget*. I klientparken er
ex-post stengt fordi anlegget mangler måler; i kjøleanlegget er ex-ante stengt fordi anlegget ble
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.
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
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
og ekte litteraturlag, og sier selv hvordan de er ment brukt: «en produksjons-deployer erstatter
prosjektlaget med en ekte kunnskapsbase og beholder litteraturlaget» (VERIFISERT: begge
`index.md`). Det er nøyaktig kategoriskillet over, skrevet fra eksemplenes side.
**Prosjektlaget er det som byttes ut per prosjekt.** Begge de leverte eksempelbasene er fiktive i
begge lag — prosjektet og kildene tilhører den oppdiktede Eksempelvirksomheten — og sier selv
hvordan de er ment brukt: «En produksjons-deployer erstatter begge lagene med en ekte
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
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,
`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
æøå; 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
porteføljemodus.
@ -196,33 +198,33 @@ fortsatt den ene projiserte kandidaten — se
**4. Hvilke harde rammer gjelder?**
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:
lystekniske minstekrav, vedlikeholdsfaktoren, at nattslukking ikke kan antas, og at tiltak
vurderes inne i porteføljen (VERIFISERT: `shared/examples/veglys-fv-soer/veglys-fv-soer.md`,
«Rammer»). Agentene leser dem som tekst; koden håndhever dem ikke (ANTATT: det følger av at
og anskaffelsesrammer. Svaret skrives inn i `type: project`-fila. Klientpark-eksempelet har fire
som begrenser tiltakene: ytelseskravene, ytelsesreserven, at nattlig avstenging ikke kan antas,
og at tiltak vurderes inne i porteføljen (VERIFISERT:
`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).
**5. Hvilke tilnærminger vil du ha vurdert, og hvorfor?**
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
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
én hypothesis-fil per kandidat-tiltak; koden krever det ikke).
parametere, modellert besparelse og kjent usikkerhet (KONVENSJON: begge de leverte eksempelbasene
har én hypothesis-fil per kandidat-tiltak; koden krever det ikke).
**6. Hvordan måles og verifiseres en besparelse i dette faget?**
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
«IPMVP Option A, ved eliminasjon» fordi anlegget mangler måler (VERIFISERT: veglys
besparelse kan sprike, og hvilken måleopsjon som er åpen for dette anlegget. For klientparken er
svaret «IPMVP Option A, ved eliminasjon» fordi anlegget mangler måler (VERIFISERT: klientparkens
`metode-ipmvp-a.md`).
*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.
**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
og det som er lånt fra andre program — veglys-eksempelet deler fila i «Del A — norsk materiale»
og «Del B — lånt materiale», og sier hvorfor: «Å blande de to ville gjort et lånt tall til en
norsk måling» (VERIFISERT: `kilder-veglys-realisering.md`).
*Hvis svaret er «det finnes ingen norsk måling»:* skriv det. Et navngitt evidenshull er innhold;
Svaret blir `type: reference`-fila. Skill skarpt mellom det som er målt på eget anlegg og det
som er lånt fra andre program — klientpark-eksempelet deler fila i «Del A — materiale om
klientparken» og «Del B — lånt materiale», og sier hvorfor: «Å blande de to ville gjort et lånt
tall til en måling av klientparken» (VERIFISERT: `kilder-klientpark-realisering.md`).
*Hvis svaret er «det finnes ingen måling på eget anlegg»:* skriv det. Et navngitt evidenshull er innhold;
et oppdiktet tall er forurensning.
**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`).
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 |
|---|---|---|---|
@ -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
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
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
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
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
`shared/examples/veglys-fv-soer/`, som er sjekket inn, kjører i demoen og er målt med
kjøreklar-sjekken under. Prosjektlaget i den basen er **fiktivt** — porteføljen finnes ikke —
mens litteraturlaget er ekte og kildebelagt (VERIFISERT: basens `index.md`). Det gjør den til et
godt eksempel på nøyaktig det skillet [§2](#2-kategoriene-hva-følger-hva) handler om: et ekte
prosjekt bytter ut prosjektlaget og beholder resten.
Eksempelet følger klientparken — 9 500 stasjonære arbeidsstasjoner — i den oppdiktede
Eksempelvirksomheten. Basen det ender i er `src/portfolio_optimiser/data/bundles/klientpark-energi/`, som er
sjekket inn og er den leverte basen demoen leser. Både prosjektlaget og litteraturlaget er
**fiktive** — porteføljen og kildene finnes ikke (VERIFISERT: basens `index.md`). Skillet
[§2](#2-kategoriene-hva-følger-hva) handler om er likevel synlig i den: prosjektlaget er skrevet
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
En driftsleder i fylkeskommunen vil vite hva LED-utskifting gir på de eldste strekningene, og om
styring oppå det er verdt noe. Oppdragsfila (VERIFISERT: akseptert av kjøringen, kunngjøringen
under er dens faktiske utskrift):
En driftsleder i IT-driftsavdelingen vil vite hva utskifting gir på de eldste maskinene, og om
strømstyring oppå det er verdt noe. Oppdragsfila (VERIFISERT 2026-08-21: en oppdragsfil med samme
form ble akseptert av kjøringen, og kunngjøringen under er dens utskrift, med eksempelets navn):
```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": [
{
"id": "led-trinn-1",
"label": "LED-utskifting av de 2 500 eldste HPS-punktene",
"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."
"id": "pc-trinn-1",
"label": "Utskifting av de 2 500 eldste stasjonære PC-ene",
"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",
"label": "Adaptiv styring på de LED-utskiftede punktene",
"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."
"id": "adaptiv-stromstyring",
"label": "Adaptiv strømstyring på de utskiftede maskinene",
"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,
"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
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
| # | 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 |
| 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` |
| 3 | Den ene projiserte kandidaten | LED-utskifting trinn 1 (2 500 punkter, 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` |
| 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-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 | PC-utskifting trinn 1 (2 500 maskiner, 114 → 70 W), modellert 445 500 NOK/år | `validator-input.json` |
| 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 |
| 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` |
| 8 | Tidligere erfaring | én frø-dom: godkjent med realiseringskorreksjon, rate 0,81, **merket som lån** | `verdict-veglys-fro.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-klientpark-fro.md` |
| 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 | — |
| 11 | Hvem dømmer | fylkets egen energirådgiver, etter kjøringen, via innboksen | `--verdict-dir` |
| 10 | Kilder som data | nei — utstyrsregisteret er levert som tall i et notat; alt er håndkuratert | — |
| 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.)
### 5.3 Hva fagpersonene leverer
| 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` |
| Brenntimer og energipris | drift / økonomi | tabellverdi (Håndbok V124) + 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` |
| Kandidat-tiltak med parametere | drift + fagmiljø | notat: før/etter-effekt, antall, hva som er utledet | to `type: hypothesis`-filer |
| Utstyrsregister: antall arbeidsstasjoner, modell, installert effekt | IT-drift | uttrekk fra utstyrsdatabasen, regneark | `type: project` (energibaseline) + raden i `cost-baseline.json` |
| Driftstimer og energipris | IT-drift / økonomi | tabellverdi (IT-driftshåndboken) + fakturagrunnlag | samme; prisbåndet i `validator-input.json` |
| Kravgrunnlag: ytelseskrav, ytelsesreserve | fagmiljø arbeidsflate | henvisning til innkjøpsspesifikasjonen og IT-driftshåndboken | «Rammer» i `type: project` |
| 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` |
| Litteratur om realiseringsgap, med kilde | fagmiljø | kildeliste med URL og årstall, merket norsk/lånt | `type: reference` |
| Tidligere vurdering av LED på småveg | energirådgiver | kort notat: «forvent ~80 % av modellert, fordi …» | `type: verdict` (frø) |
| Litteratur om realiseringsgap, med kilde | fagmiljø | kildeliste med URL og årstall, merket eget anlegg/lånt | `type: reference` |
| 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
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
```
veglys-fv-soer/
klientpark-energi/
├── index.md type: index inngangen; lenker til alt under
├── veglys-fv-soer.md type: project porteføljen, energibaselinen, rammene
├── tiltak-led-utskifting.md type: hypothesis kandidat 1 — den som er projisert
├── tiltak-adaptiv-styring.md type: hypothesis kandidat 2 — svakere kildebelagt, og merket slik
├── klientpark-energi.md type: project porteføljen, energibaselinen, rammene
├── tiltak-pc-utskifting.md type: hypothesis kandidat 1 — den som er projisert
├── 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
├── kilder-veglys-realisering.md type: reference Del A norsk / Del B lånt
├── verdict-veglys-fro.md type: verdict frø-dommen — holdes ute av lesekonteksten
├── kilder-klientpark-realisering.md type: reference Del A eget anlegg / Del B lånt
├── verdict-klientpark-fro.md type: verdict frø-dommen — holdes ute av lesekonteksten
├── validator-input.json den projiserte kandidaten
└── 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
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.
### 5.5 De to tallfilene — skrevet fra samme linje
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
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
@ -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å.
**É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
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.
### 5.6 Frø-dommen
@ -449,14 +458,15 @@ decision: approved_with_adjustment
realization_rate: 0.81
modelled_saving_nok: 445500
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
81 % av modellert, lånt fra belysnings-programlitteratur og merket som lån. …"
provenance: "frø — AI-forfattet. Realiseringsgraden er LÅNT … Det finnes INGEN norsk ex-post-måling
for veglys. Erstattes av ekte HITL i produksjon."
81 % av modellert, lånt fra virksomhetens etterevalueringer av belysningsprogrammer og merket
som lån. …"
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`
`_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
@ -469,25 +479,26 @@ Det finnes ingen egen «valider basen»-kommando
bestillingen på plass:
```bash
uv run python -m portfolio_optimiser.run VEGLYS-FV-SOER \
--docs-dir shared/examples/veglys-fv-soer \
--bundle-dir shared/examples/veglys-fv-soer \
uv run python -m portfolio_optimiser.run KLIENTPARK-ENERGI \
--docs-dir src/portfolio_optimiser/data/bundles/klientpark-energi \
--bundle-dir src/portfolio_optimiser/data/bundles/klientpark-energi \
--mandate oppdrag.json \
--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
Objective: Redusere energikostnaden i veglysporteføljen Fylkesveg Sør uten å gå under lystekniske minstekrav, med tiltak som kan bestilles i 2027.
Run mandate for KLIENTPARK-ENERGI
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
1. led-trinn-1 — LED-utskifting av de 2 500 eldste HPS-punktene
2. adaptiv-styring — Adaptiv styring på de LED-utskiftede punktene
1. pc-trinn-1 — Utskifting av de 2 500 eldste stasjonære PC-ene
2. adaptiv-stromstyring — Adaptiv strømstyring på de utskiftede maskinene
Stops at: 3 rounds / 100000 tokens
Contacts: no external services
Success: Minst ett tiltak som passerer validatoren og som 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)
Success: Minst ett tiltak som passerer validatoren og som IT-driftsavdelingen kan stå inne for.
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
@ -519,8 +530,8 @@ unåbar, og da finnes det ingen lenke å rapportere), eller at innholdet er godt
mappe.
**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
«KUNNSKAPSBASE: veglys-fv-soer — kostbaseline erklært (ENERGI-VEGLYS-EL 4386150 x 1)», navigerer
`uv run python -m portfolio_optimiser.simulation`. Den kjører nøyaktig `klientpark-energi`, skriver
«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:
`tests/golden/demo-transcript.stdout`, linjene 7, 13 og 50). Agent-svarene i demoen er
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
([§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.
- **Eksempelbasene er ikke ekte prosjekter.** Prosjektlaget er fiktivt; realiseringsgraden i alle
tre frø-dommene er lånt fra utenlandsk programlitteratur fordi ingen norsk ex-post-måling
finnes (VERIFISERT: `provenance`-feltet i de tre dom-filene).
- **Eksempelbasene er ikke ekte prosjekter.** Prosjektlaget er fiktivt; realiseringsgraden i
frø-dommene er lånt fra andre program fordi ingen ex-post-måling av anlegget selv finnes
(VERIFISERT: `provenance`-feltet i dom-filene).
- **De fire innholdstypene er konvensjon**, ikke spesifikasjon
([§4](#4-innholdstypene)).
- **`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
fått fila siden (VERIFISERT: `ls`, begge datert 2026-08-09). Rettelsen er ikke gjort her, for
`cost-baseline.json`. Det var sant da den ble skrevet (2026-08-05); begge de leverte
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.
- **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.
@ -606,18 +617,18 @@ ikke fyrer der.
| 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 |
| 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 |
| 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 |
| `--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 |
| 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 |
| Frø-dommen: rate 0,81, forventet 360 855, lånt | `verdict-veglys-fro.md` frontmatter | 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-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 |
| 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 |
| `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 |

View file

@ -8,7 +8,7 @@
| 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. |
| 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. |
| 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
deterministiske gaten kan ikke lenger mates med hallusinerte kostlinjer.
- **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
toleranse → Rejection; toleranse konfig); metode-registry: metode-caps keyes via
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)
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
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
åpent punkt under).
**(b) Retningen er snudd.** `_CANDIDATES` har nå en `VEGLYS-FV-SOER`-oppføring hvis kostlinjer er
skrevet FRA `shared/examples/veglys-fv-soer/cost-baseline.json`; `baseline_from_scripted_candidate`
**(b) Retningen er snudd.** `_CANDIDATES` har nå en `KLIENTPARK-ENERGI`-oppføring hvis kostlinjer er
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
korrigerte svaret ER den leverte IR-projeksjonen ordrett — inkludert `assumptions`-bandet, så
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.
*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
Kjøring B **tre**: den frøsatte (`verdict-veglys-fro.md`), Steg-8-promoteringen og Steg-7-innboksen —
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-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
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
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å
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) |
| 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). |
| 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.)* |
| 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 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. |
| 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) |
@ -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
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
fortsatt målt: den lever i suiten som `test_anchored_reserve_loadbearing.py`, ikke som demoens
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)
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
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
(«printes fortsatt ordrett») var et premiss, ikke en måling. Det trengs altså **ingen** setning
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**.
3. **`docs/ekspert-svar.md`** er en operatør-guide og leses ikke av demoen.

View file

@ -53,14 +53,15 @@
<!-- 1 -->
<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>
<p class="lead">Du er fagpersonen. Løsningen finner ingen besparelser uten det du vet —
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,
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>
<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>
<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>
<td>LED i veglys · nye rekkverkstyper med lengre levetid</td>
<td>LED-armaturer · frekvensstyrte vifter</td></tr>
<td>energigjerrige PC-er erstatter eldre modeller · skjermer med lengre levetid</td>
<td>effektive kjøleaggregater · frekvensstyrte vifter</td></tr>
<tr><td><b>Behovsstyring framfor fast drift</b></td>
<td>vinterdrift utløst av målestasjon/prognose framfor fast rode-utkalling</td>
<td>ventilasjon styrt på målt CO/NO₂ framfor fast drift · finere dimmetrinn på dagsonen</td></tr>
<td>brukerstøtte utløst av overvåkingsvarsel framfor fast besøksrunde</td>
<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>
<td>dekkefornyelse etter målt spor og jevnhet framfor fast syklus · grøfterens etter tilstand</td>
<td>vask og renhold etter målt tilsmussing framfor fast frekvens</td></tr>
<td>utskifting av PC-er etter målt feilrate framfor fast syklus · lisensopprydding etter faktisk bruk</td>
<td>filterbytte og rengjøring etter målt trykkfall framfor fast frekvens</td></tr>
<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>
</table>
@ -197,11 +198,11 @@
<table>
<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>Lengre intervall mellom tunnelvask</td><td>antall vask per år × kr per vask</td></tr>
<tr><td>✔</td><td>Finere dimming av tunnelbelysningen</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>«Bedre samhandling med entreprenøren»</td><td>ingen mengde, ingen enhetspris</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 filterbytte i kjøleanlegget</td><td>antall bytter per år × kr per bytte</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>Minneoppgradering framfor ny PC</td><td>antall maskiner × kr per maskin</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>
</table>
@ -226,15 +227,15 @@
<table>
<tr><th>Prosjekttype</th><th>Typiske linjer</th><th>Formen</th></tr>
<tr><td rowspan="4"><b>Utbedring veg</b></td>
<td>dekkefornyelse</td><td>m² × kr/m²</td></tr>
<tr><td>vinterdrift</td><td>utkallinger/år × kr per utkalling, eller km × kr/km</td></tr>
<tr><td>veglys, 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 rowspan="4"><b>Tunnel</b></td>
<td>belysning, energi</td><td>kWh/år × kr/kWh</td></tr>
<tr><td>ventilasjon, 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 rowspan="4"><b>Kontor-IT</b></td>
<td>utskifting av PC-er</td><td>antall × kr/stk</td></tr>
<tr><td>brukerstøtte</td><td>utrykninger/år × kr per utrykning, eller brukere × kr/bruker</td></tr>
<tr><td>klientpark, energi</td><td>kWh/år × kr/kWh</td></tr>
<tr><td>kabling, nettverksuttak</td><td>løpemeter × kr/lm</td></tr>
<tr><td rowspan="4"><b>Driftssenter</b></td>
<td>kjøling, 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>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>
</table>
@ -260,25 +261,25 @@
<p>Ikke spør om integrasjoner. Spør per tall. Fem kategorier dekker det meste:</p>
<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>
<td>enhetspriser fra driftskontrakt, sluttkostnad fra tilsvarende prosjekt</td>
<td>energifaktura, priser fra siste elektroanbud</td>
<td>enhetspriser fra rammeavtale, sluttkostnad fra tilsvarende prosjekt</td>
<td>energifaktura, priser fra siste kjøleanbud</td>
<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>
<td>km veg, m² dekke, antall stikkrenner, meter rekkverk, alder og tilstand</td>
<td>antall armaturer og effekt, antall vifter og pumper, lengde, antall løp</td>
<td>antall PC-er og skjermer, antall lisenser, meter kabling, alder og tilstand</td>
<td>antall kjøleaggregater og effekt, antall vifter og pumper, rackplasser, antall rom</td>
<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>
<td>ÅDT, antall vinterutkallinger, saltmengde, klippefrekvens</td>
<td>brenntimer, driftstimer vifter, vaskefrekvens, trafikkfordeling</td>
<td>antall brukere, antall utrykninger, driftstimer per maskin, lisensbruk</td>
<td>driftstimer kjøling, driftstimer vifter, filterbyttefrekvens, lastfordeling</td>
<td>årsforbruket kan ikke regnes</td></tr>
<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>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>
<td>driftskontraktens omfang og løpetid, opsjoner</td>
<td>rammeavtalens omfang og løpetid, opsjoner</td>
<td>serviceavtaler, garantiperioder</td>
<td>tiltak foreslås som ikke kan bestilles</td></tr>
</table>
@ -304,8 +305,8 @@
<table>
<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>
<td>håndbøker og vegnormaler som gjelder tiltaket — for tunnelbelysning
f.eks. Håndbok V124 og N500, med paragraf</td>
<td>standarder og interne driftskrav som gjelder tiltaket — for kjøling i et
serverrom f.eks. virksomhetens egne temperaturkrav, med paragraf</td>
<td>setter gulvet ingen besparelse kan gå under; hindrer forslag som bryter krav</td></tr>
<tr><td><b>G. Priser og indekser</b></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>
<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>
<td>krav til friksjon, jevnhet, sporddybde, siktforhold</td>
<td>lystekniske minstekrav i sonene, luftkvalitetskrav, hysteresetid ved nivåendring</td></tr>
<td>krav til ytelse, skjermstørrelse, oppetid, universell utforming</td>
<td>temperaturkrav i rommet, fuktighetskrav, hysteresetid ved nivåendring</td></tr>
<tr><td><b>Sikkerhetskrav</b></td>
<td>rekkverksklasser, arbeidsvarsling</td>
<td>krav til nødbelysning, ventilasjon ved brann, redundans</td></tr>
<td>krav til kryptering, tilgangsstyring</td>
<td>krav til nødstrøm, brannslukking, redundant kjøling</td></tr>
<tr><td><b>Antakelser som ikke holder</b></td>
<td>«vi kan ikke forutsette at strekningen kan stenges»</td>
<td>«vi kan ikke forutsette nattstenging for arbeid»</td></tr>
<td>«vi kan ikke forutsette at brukerne kan være uten maskin en hel dag»</td>
<td>«vi kan ikke forutsette planlagt nedetid for arbeid»</td></tr>
<tr><td><b>Kontraktsbundet</b></td>
<td>driftskontraktens omfang ut avtaleperioden</td>
<td>rammeavtalens omfang ut avtaleperioden</td>
<td>serviceavtaler, garantibetingelser</td></tr>
<tr><td><b>Budsjett og anskaffelse</b></td>
<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>
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>
<table>
@ -462,12 +463,12 @@
<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>
<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>
<p>De samme spørsmålene på vegsiden: <i>Ble den nye driftsrutinen faktisk fulgt hele vinteren?
Ble tilstandsmålingene brukt til å styre, eller bare rapportert? Hvor mye av tynndekket måtte
gjøres om igjen innen tre år?</i></p>
<p>De samme spørsmålene på kontorsiden: <i>Ble den nye strømstyringen faktisk fulgt hele året?
Ble feilratene brukt til å styre utskiftingen, eller bare rapportert? Hvor mange av de oppgraderte
maskinene måtte likevel byttes innen tre år?</i></p>
<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>.»
@ -563,7 +564,7 @@
<h3>Gjør svaret vesentlig bedre</h3>
<ul>
<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><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>

View file

@ -253,16 +253,16 @@
<rect class="box dashed" x="40" y="40" width="430" height="360"/>
<rect class="box" x="70" y="64" width="370" height="58"/>
<text class="lbl" x="88" y="90">FV42-GSV-E1</text>
<text class="sub" x="88" y="110">Fv. 42 gang- og sykkelveg, etappe 1</text>
<text class="lbl" x="88" y="90">KONTOR-IT-E1</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"/>
<text class="lbl" x="88" y="176">RV13-RAS-TP</text>
<text class="sub" x="88" y="196">Rv. 13 rassikring tunnelportal</text>
<text class="lbl" x="88" y="176">NETT-SIKR-TP</text>
<text class="sub" x="88" y="196">Nettverkssikring, tilkoblingspunkter</text>
<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="sub" x="88" y="282">Bru over Lakselva, rehabilitering</text>
<text class="lbl" x="88" y="262">ARKIV-LAGR-MIGR</text>
<text class="sub" x="88" y="282">Arkivlagring — migrering</text>
<rect class="box soft" x="70" y="322" width="370" height="58"/>
<text class="lbl" x="88" y="348">SKOLE-VVS-OPPGR</text>
@ -275,7 +275,7 @@
<path class="edge" d="M472 93 H556"/>
<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"/>
<text class="tiny" x="580" y="106">kode</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="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="1140" y="134" text-anchor="end">850 000</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="940" y="164">4 200 m3</text>
<text class="mono" x="650" y="164">Migrering av brukerdata</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="580" y="194">03.1</text>
<text class="mono" x="650" y="194">Forsterkningslag knust grus</text>
<text class="mono" x="940" y="194">1 800 m3</text>
<text class="mono" x="650" y="194">Nettverksutstyr kontor</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="580" y="224">05.2</text>
<text class="mono" x="650" y="224">Asfalt Ab11</text>
<text class="mono" x="940" y="224">4 300 m2</text>
<text class="mono" x="650" y="224">Lisens kontorpakke</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="580" y="254">07.4</text>
<text class="mono" x="650" y="254">Kantstein granitt</text>
<text class="mono" x="940" y="254">2 400 m</text>
<text class="mono" x="650" y="254">Skjerm 27 tommer</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="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="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>
<table class="compact">
<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>RV13-RAS-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>KONTOR-IT-E1</td><td>850 000</td><td>6 721 500</td><td class="ok">validated</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>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>
</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>
@ -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"/>
<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>
<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>
<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>
<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="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>
<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>
<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>
@ -1409,7 +1409,7 @@ Produce a REVISED SavingsProposal that resolves this.</code></pre>
</table>
<figure class="fig">
<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="128" 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>
<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>
<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>
</div>
</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>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>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>
</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>
@ -2961,7 +2961,7 @@ Produce a REVISED SavingsProposal that resolves this.</code></pre>
<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">
<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="420" y="42" text-anchor="end">før</text>
<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"/>
<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="420" y="160" text-anchor="end">før</text>
<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>
</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="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>
</section>
@ -3137,8 +3137,8 @@ Produce a REVISED SavingsProposal that resolves this.</code></pre>
</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>
</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="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="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/invarianter.md</code> · <code>STATE.md</code></p>
</div>
</section>
@ -3160,7 +3160,7 @@ Produce a REVISED SavingsProposal that resolves this.</code></pre>
<div class="cols">
<div class="col">
<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 class="col">
<span class="step">Hva målingen faktisk viste</span>
@ -3264,7 +3264,7 @@ Produce a REVISED SavingsProposal that resolves this.</code></pre>
</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"><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>
</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="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="lbl" x="632" y="150">Nye korpus: N400 og R762</text>
<text class="sub" x="632" y="170">basene publiseres ikke, ingen etatskontakt</text>
<text class="lbl" x="632" y="150">Nye kunnskapskorpus</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="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>

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
- **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,
`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 →
@ -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
(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).
- `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.
- 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)