portfolio-optimiser/docs/plan/2026-08-12-demo-runbook.md
Kjell Tore Guttormsen c50fd2edb4 docs(P4.5): §5s frys-gate ved X kunne aldri feile
Punkt 5 kjørte frys-gaten mot <X> rett etter at punkt 4 leste X som
`git rev-parse HEAD`. På det tidspunktet ER X lik HEAD, og ingenting er
committet imellom — så <X>..HEAD er tom uansett hva treet inneholder.

Målt, ikke resonnert: med en endret src/-fil liggende i arbeidstreet
(` M src/portfolio_optimiser/__init__.py`) sto gaten fortsatt tom. Den
måler ingen tilstand.

Økt 7s begrunnelse for to kjøringer sa «den første måler en tilstand som
ikke lenger finnes når taggen settes». Det er for snill: den finner ingen
tilstand å måle. Dette er repoets egen defektklasse, anvendt på repoets
egen runbook — «en gate som bare kan bli grønn beviser ingenting» er
standarden hver load-bearing test måles mot.

Alvorlighet, uttalt presist: falsk trygghet, ikke falsk grønt. Punkt 3
(rent tre) og punkt 9 (etter Y og Z) dekker den faktiske risikoen, så
ingenting ved onsdagen blir rødt av dette. Men en gate operatøren ser
grønn ved X kan under tidspress gjøre punkt 9 til en gjentakelse man
hopper over — samme argument som ga §6 steg 1 sin `--untracked-files=no`.

Punktet er BEHOLDT, ikke fjernet: fjerning renummererer 6→5 … 11→10 og
bryter fem kryssreferanser (§0s «punkt 10» og «punkt 6», punkt 9s
tilbakereferanse, STATEs «elleve punkter», planens ons-12-rad) — på
frys-eve. I stedet har punkt 5 fått en arm den kan feile på:

    git diff --stat c255662..HEAD -- . ':(exclude)docs/' ':(exclude)CHANGELOG.md'

→ ikke tomt (målt: 6 filer). Den beviser at kommandoen kan diskriminere
FØR punkt 9 hviler på at den er tom. Uten den ville en feilskrevet
':(exclude)…' eller en quoting som ikke overlevde skallet (MULTIOS,
08-09) gitt grønn gate på feil grunnlag, og et utestet tre tagget.
c255662 ligger fast bak både Y og Z, så armen forblir ikke-tom uansett
hvor HEAD står onsdag.

To rettelser i samme pass, begge nødvendige for at teksten ikke skal lyve
på en ny måte:

- punkt 9 sa «Punkt 5 målte en tilstand som ikke lenger finnes» — under
  det nye punkt 5 er det galt på en ny måte, siden punkt 5 ikke målte noe
- §5s ingress sa «Fila skrives ÉN gang onsdag (punkt 6)», mens punkt 4
  instruerer om å skrive X inn i §0. Den bærende egenskapen er at
  ingenting skrives ETTER Y, ikke antallet skrivinger — nå sagt slik.
  Punkt 4 er operasjonelt riktig: å bære en hash i hodet gjennom tre
  gate-kjøringer er verre.

Begge gate-kommandoene er EKSTRAHERT LITERALT fra runbooken og kjørt
(ikke gjenskapt for hånd — repoets MULTIOS-lærdom): arm 1 tom, arm 2
seks filer.

Målt: utfyllings-gaten 2 · §5 elleve punkter · §0/§1/§2/§3/§4/§6/vedlegg
byte-urørt mot 24b5ed2 (kun §5 endret, så økt 9s 67 målinger av §2 står) ·
planens kalender-rader 3 usiterte pipes · frys-gaten mot HEAD TOM ·
810 passed / 4 skipped · ruff + format + mypy rene.

Pre-flighten er re-målt HELT på 24b5ed2, ikke arvet fra 4b9e5d9:
stale tag 0/0 · [Unreleased] 1, null link-refs · frys-gaten d0e8bb0..HEAD
TOM og diskriminerende (c255662..HEAD = 6 filer) · demo exit 0, golden-diff
TOM, 61 linjer, K1 distinkt 8 / linjer 9.

Åttende defekt i denne dokumentfamilien på åtte økter. Ny klasse: ikke
«feil tidspunkt» og ikke «implisitt steg», men en gate hvis utfall var
avgjort av sin egen plassering i sekvensen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0192WLzngWK5aFDYpkXWuLVh
2026-08-11 21:51:23 +02:00

361 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Demo-runbook — torsdag 13. august 2026
> **Én side å ha i hånda på scenen.** Kjøresekvens · hva som sies hvor · abortstien.
>
> **Status:** beslutnings-innholdet er skrevet **mandag 10. august** (økt 7) — alt som er kjennbart
> uten frysen. **Onsdag 12. fyller ut de målte feltene** og krysser av nederst. Runbooken er
> lesestoff, ikke kjøresti: den ligger under `docs/`, som er unntatt frys-gaten, så den kan skrives
> og fylles ut uten å røre det frosne treet.
>
> **Onsdagens utfyllings-sjekk (kjør FØR demoen):**
> ```
> grep -n '<<[A-ZÆØÅ-]*>>' docs/plan/2026-08-12-demo-runbook.md # → TOMT
> ```
> Ikke tomt = runbooken er et utkast, ikke en runbook. Et uutfylt felt er samme drift-klasse som
> plan-radene økt 4, 5 og 6 hver for seg fant: et dokument som instruerer om noe som ikke stemmer.
---
## 0. Målte felt — fylles onsdag
| Felt | Verdi | Hvor det kommer fra |
|---|---|---|
| Frosset commit **X** | `<<X-HASH>>` | `git rev-parse HEAD` rett etter grønn generalprøve ×2 |
| Generalprøve ×2 | `<<PROEVE-DATO-TID>>` | tidspunktet begge kjøringene var grønne |
**Det finnes med vilje ingen rad for taggen, og fraværet er bærende.** Et tag-felt kunne først
fylles ut *etter* §5s punkt 10 (push) — men §5s punkt 6 krever at utfyllings-gaten er **tom**, altså
før. Å fylle det ærlig ville krevd en commit **etter** taggen, og da står HEAD på den commiten mens
taggen står på `Z`: torsdagens §6 steg 2 (`git describe --tags --exact-match HEAD``v1.0.0`)
ville vært **rød på demo-morgenen**, av den ene gaten som måler identitet. Å la feltet stå ufylt
bryter gaten i stedet. Taggen bekreftes derfor **der den settes** (§5 punkt 11, tre kommandoer i
terminalen) og måles på nytt torsdag av §6 steg 2 — den er en handling som verifiseres, aldri et
tall som avskrives hit.
Alt annet i dette dokumentet er målt mandag 10. august mot det pinnede transkriptet
(`tests/golden/demo-transcript.stdout`, 61 linjer) og står uendret.
---
## 1. Kjøresekvensen
**Kommandoen — kjør denne, ingen andre flagg:**
```
uv run python -m portfolio_optimiser.simulation
```
Forventet: **exit 0**, **61 linjer stdout**, **4 linjer stderr**.
Stderr er fire linjer og skal være der: to `ExperimentalWarning` fra MAF (Skills + MemoryStore),
en blanklinje, og `(arbeidskopi: <systemets temp-katalog>/po-sim-…)`.
**Katalogen varierer, prefikset gjør ikke det.** På macOS er temp-katalogen `/var/folders/…/T/`,
**ikke** `/tmp/` — så linja på skjermen blir lang og stygg, og det er riktig. `po-sim-` er den delen
som er konstant, fordi programmet setter den (`mkdtemp(prefix="po-sim-")`); resten tilhører miljøet.
Fasiten maskerer nøyaktig dette skillet. En lang `/var/folders/…`-sti er altså **ikke** et tegn på
at noe er galt, og ingen grunn til å gå til abortstien.
De to advarslene dempes **ikke** med vilje — de
fyrer mens biblioteket importeres, og å dempe dem ville betydd at rammeverket bestemmer hva MAF
får si til enhver konsument. Rund-tak-linjene («forcing completion») er dempet og skal **ikke** vises.
**Hvis du vil vise at outputen er den frosne:**
```
uv run python -m portfolio_optimiser.simulation > /tmp/demo.out 2>/dev/null
diff /tmp/demo.out tests/golden/demo-transcript.stdout # → TOMT
```
*(Konsoll-kommandoen `portfolio-optimiser-demo` gir byte-identisk stdout og virker like godt.
`-m`-formen står her fordi den ikke antar noe om PATH. `PYTHONIOENCODING=utf-8` er pinnet i
testene; i en vanlig UTF-8-terminal trengs den ikke.)*
---
## 2. Hva som sies — i rekkefølge, forankret i skjermlinjene
Linjenumrene under er linjene i det pinnede transkriptet, så du kan finne igjen stedet uten å lete.
### Åpningen (skjermlinje 15, banneret står allerede der)
Banneret sier det selv. Les det, ikke pynt det bort, og legg til de **tre ærlighets-punktene**
(demo-uke-planen §1 — nivå-2-påstanden, D-I):
1. **Agent-svarene er skriptet.** Dette beviser dataflyten, den deterministiske ryggraden og at
læringssløyfa lukkes — ikke at en levende modell ville produsert nettopp dette forslaget.
2. **Innholdet er håndkuratert, ikke fabrikkert.** Et menneske lagde kunnskapsbasen.
3. **Tallene er modellerte, ikke målte.** Ingen pilot har validert dem i drift.
> Dette er ikke en unnskyldning som svekker demoen — det er grunnregelen repoet er bygget på
> (A5: koden får ikke påstå mer enn den gjør). En demo som overselger bryter med det den demonstrerer.
### Ærlighets-avsnittet — leses opp ORDRETT (innholdsgate-planen §5, **JA-varianten**)
Spor B landet 2026-08-09, så det er JA-varianten som gjelder. NEI-varianten brukes **ikke**.
> «To ting om innholdet dere ser. For det første: denne kunnskapsbasen er laget for hånd, ikke
> produsert av systemet. Det finnes en ingest-vei som henter eksterne kilder inn i formatet, men
> eksempelet her gikk ikke gjennom den, og den generiske fabrikken som skulle laget slike baser er
> ikke bygget — den er bevisst utsatt.
> For det andre, om sikkerhet: ingest-veien skanner nå innholdet før det skrives, med en egen
> sikkerhetskomponent, slik at forgiftet kildeinnhold ikke havner i basen.»
*(Forbeholdet hvis noen graver: gaten er **opt-in** — `materialize_gated` er den gatede inngangen,
`materialize` er bevisst ugatet, og en kaller som vil ha gaten ber om den ved navn. Det står slik i
CHANGELOG-ens Security-oppføring. Si det hvis det spørres; ikke som en fotnote i opplesningen.)*
### Kunnskapsbasen (skjermlinje 79)
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
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.
### Kjøring A, steg for steg (skjermlinje 1133)
| Skjerm | Steg | Det ene poenget |
|---|---|---|
| 1215 | 1 — Forstå konteksten | fem konseptfiler ble **navigert**, ikke søkt opp som tekstbiter; **0** tidligere dommer, og markøren er `False` — det er kontrollen som gjør Kjøring B beviselig |
| 1619 | 2 — Hypotese | forslaget med parametere og kostlinjer; påstått **2 100 000 NOK** |
| 2022 | 3 — Debatt | to deltakere; checkeren gater **resonnementet** og sier `VERDICT=APPROVE` |
| 2324 | 4 — Valider | **REJECTED** — 2 100 000 overstiger P90 feasible **1 769 915**. Dette er hele poenget: checkeren sa ja, tallene sa nei, og tallene vinner |
| 2527 | 5 — Forbedre | grunnen mates tilbake i neste forsøk, **bundet** av `max_attempts`**VALIDATED 445 500** |
| 2829 | 6 — Forkast eller foreslå | typet utfall forlater kjøringen (`validator=validated, checker=approve`) |
| 3033 | 7 — Tilbakemelding (kort løkke) | ekspert-personaen godkjenner **med realiseringskorreksjon** (0.79) |
**Setningen som bærer demoen** (skjermlinje 22 mot 24): *«Checkeren godkjente resonnementet. Den
deterministiske validatoren avviste tallet. To uavhengige falsifiserere, og det er den som regner
som blokkerer.»*
### Mellom kjøringene (skjermlinje 3546)
To **uavhengige** tilbakemeldings-veier, én per tidsskala — og de bærer hver sin markør nettopp
for at ingen av dem skal kunne ta æren for den andre:
- **Steg 7, lang løkke** (3741): en ekspert legger en fil i innboksen **etter** kjøringen
(`realiseringsgrad=0.66`). Rollene byttes aldri: systemet **leser** mappa, eksperten **skriver**
den. Dager kan gå.
- **Steg 8, promotering** (4346): den godkjente dommen løftes inn i wikien (`realiseringsgrad=0.79`).
Gaten er **fail-closed** — kun en godkjent dom promoteres, rå agent-output aldri.
### Kjøring B (skjermlinje 4854)
Kun det som **endret seg** vises. Begge markørene er `True`, og linje 51 sier hvor dommene kom fra:
**1 av 3 fulgte med kunnskapsbasen, de øvrige 2 er dem demoen lærte i denne økten**. Den splitten
er **regnet ut** av kjøringen, ikke skrevet ned — derfor stemmer den også når basen en dag shipper
flere dommer.
Utfallet er **det samme tiltaket til samme beløp**. Det er riktig og verdt å si høyt: læringen
endret ikke svaret her, den endret **grunnlaget** svaret ble formet på.
### Avslutningen (skjermlinje 5661)
Les blokken. Poenget er den siste setningen: **ingen av de to veiene gikk gjennom minnet** — begge
gikk gjennom fil.
### Mandat-setningen (MUNTLIG — ingenting av dette vises på skjermen)
Svaret på «kan vi styre hva som analyseres?»:
> «Ja. En kjøring kan bestilles med en oppdragsfil: du skriver hva kjøringen er til for, og hvilke
> tilnærminger du vil ha vurdert — hver av dem får sin egen vurdering og sin egen linje i oppgjøret,
> og systemet kan i tillegg foreslå sitt eget. Men bestillingen styrer hva som **vurderes**, aldri
> hva som **godkjennes**: validatoren gjelder uendret, så ber du om noe tallene ikke bærer, blir det
> avvist — og avvisningen kommer tilbake til deg med begrunnelsen. Det er ikke vist i denne demoen;
> det er et eget flagg (`--mandate`), og det er dokumentert.»
Dokumentet er [`docs/bestille-en-kjoring.md`](../bestille-en-kjoring.md). Søsterdokumentet for den
andre enden — å avgi dommen etterpå — er `docs/ekspert-svar.md`.
---
## 3. Abortstien
**Feiler live-kjøringen på scenen: ikke debug. Vis fila.**
```
cat /Users/ktg/repos/portfolio-optimiser/tests/golden/demo-transcript.stdout
```
**Stien er absolutt med vilje.** Prosaen under peker på feil katalog som en sannsynlig årsak — og en
relativ sti ville feilet av nøyaktig den årsaken (målt: `cat: tests/golden/…: No such file or
directory`). En abortsti som deler failure-mode med det den aborterer fra, er ingen abortsti; den
gjør ett synlig problem til to.
Den fila **er** transkriptet — ordrett, uten normalisering — fra den frosne kjøringen. Si høyt hva
den er:
> «Dette er den frosne kjøringen fra onsdag, sjekket inn som fasit. Det dere ser er ikke en
> gjenfortelling, det er outputen ordrett — og at den er sjekket inn er grunnen til at jeg kan vise
> den nå.»
Ikke prøv å fikse noe under demoen. Miljøet er den sannsynlige årsaken (`uv sync` / feil katalog),
og feilsøking på scenen koster mer enn fila.
---
## 4. Forventede spørsmål
| Spørsmål | Svar |
|---|---|
| «Hvorfor viste Kjøring A **0** tidligere dommer når basen shipper ett dom-frø?» | Med vilje: Kjøring A kjøres mot en **tom** wiki. Uten den kontrollen kunne markøren i Kjøring B like gjerne kommet fra basen som fra læringen — kontrollen er det som gjør sløyfa beviselig, ikke bare påstått. |
| «Er dette en ekte LLM?» | Nei — agent-svarene er skriptet, og det står i banneret. Det som er ekte er dataflyten, den deterministiske validatoren og at læringen faktisk går gjennom fil. |
| «Hva om modellen hallusinerer et tall?» | Stage 0 avstemmer hver kostlinje mot prosjektets **erklærte** kostbaseline før løseren i det hele tatt kjører. Ukjent kostkode, eller en mengde/enhetspris utenfor toleransen, avvises — validering, aldri reparasjon. |
| «Kan den kjøre en hel portefølje?» | **Biblioteket** har porteføljekjøring med bølge-modell og et globalt token-tak. Kommandolinja eksponerer ikke taket, og **porteføljekjøring er ikke det denne demoen viser** — den kjører ett prosjekt. Ikke tilby en live demonstrasjon av porteføljestien; den er ikke prøvekjørt denne uka. |
| «Er den generiske fabrikken for kunnskapsbaser med?» | Nei, bevisst utsatt. Basen her er håndkuratert. Det er punkt 2 i åpningen. |
| «Hva med `open/`-speilet / hvor ligger koden?» | Taggen `v1.0.0` er satt på det private repoet. Det offentlige speilet synkes etter demoen, sammen med READMEen som forklarer commitene. |
---
## 5. Onsdag 12. — elleve punkter i rekkefølge (etter generalprøve ×2)
**Følges i terminalen. Hakene settes ALDRI i denne fila.** Punkt 6 er runbook-commiten `Y`, og alt
fra punkt 7 og ut skjer *etter* den. Et hake satt der havner enten ucommittet i treet — målt:
`git status --short --untracked-files=no` svarer da ` M docs/plan/2026-08-12-demo-runbook.md`, så
torsdagens §6 steg 1 blir rød — eller i en commit **etter** taggen, som gjør §6 steg 2s identitet
rød. Samme motsigelse som §0s manglende tag-rad, samme to gater. **Fila skrives i punkt 4 og punkt 6
— begge FØR runbook-commiten `Y` — og røres ikke etter den.** Det er *etterpå* som er den bærende
egenskapen, ikke antallet skrivinger: en hash notert i punkt 4 og først skrevet ned tre steg senere
er en hash båret i hodet gjennom tre gate-kjøringer. Bruk papir, en annen skjerm eller hukommelsen
til **hakene** — de er det eneste som ellers ville havnet i fila etter `Y`.
- [ ] Generalprøve ×2 grønn — golden-diff tom begge ganger
- [ ] K1: `grep -oE "^ *Steg [1-8]" <stdout> | tr -d ' ' | sort -u | wc -l`**8**
*(linje-tellingen gir **9** — kjent og forventet: Steg 7 har to linjer, én per tidsskala.
Kjør kommandoen, ikke prosaen.)*
- [ ] **Rent tre FØR X noteres**`git status --short --untracked-files=no`**TOMT**.
Prøven du nettopp kjørte leste **arbeidstreet**; **X** er en **commit**. Frys-gaten under er
commit-til-commit og kan ikke se en ucommittet endring i `src/`**begge** kjøringene av den
ville stått tomme mens det taggede treet var noe annet enn det prøvde. Samme kommando som
§6 steg 1, tredje tidspunkt, egen jobb. Utrackede filer holdes utenfor av samme grunn som der:
`presentasjon-*.html` eies av en annen sesjon.
**Ikke tomt: commit eller forkast — og kjør så prøven OM IGJEN.** Ikke fristes til å committe
og gå videre: den commiten flytter HEAD, så **X** ville pekt på et tre prøven aldri så. Ved X
er ingenting redigert ennå — runbooken (Y) og CHANGELOG (Z) skrives *etter* dette punktet.
- [ ] **X** notert (`git rev-parse HEAD`) og skrevet inn i tabellen i §0
- [ ] **Frys-gaten prøvekjørt — BEGGE armer.**
```
git diff --stat <X>..HEAD -- . ':(exclude)docs/' ':(exclude)CHANGELOG.md' # → TOMT
git diff --stat c255662..HEAD -- . ':(exclude)docs/' ':(exclude)CHANGELOG.md' # → IKKE tomt
```
Den første armen er **tom, og kan ikke være annet**: `<X>` **er** HEAD her — punkt 4 leste den
nettopp, og ingenting er committet siden — så `<X>..HEAD` er tom uansett hva treet inneholder.
Målt med en endret `src/`-fil liggende i treet: gaten sto fortsatt tom. Den er altså en
**lime-inn-sjekk** av hashen (`fatal: bad revision` hvis du bommet), ikke en frys-sjekk.
Frysen selv måles av punkt 3 (rent tre) og punkt 9 (etter Y og Z).
**Den andre armen er den som gir punkt 9 mening** — den beviser at kommandoen *kan* bli
ikke-tom, før punkt 9 hviler på at den er tom. Målt: **6 filer**. `c255662` ligger fast bak
både Y og Z, så den forblir ikke-tom uansett hvor HEAD står i morgen.
**Er den andre armen også tom, er det pathspec-en som er ødelagt** — feilskrevet
`':(exclude)…'`, eller quoting som ikke overlevde skallet (repoets MULTIOS-lærdom: en kommando
som ser riktig ut kan lyve). Punkt 9 ville da vært grønn på feil grunnlag, og du ville tagget
et utestet tre. **Ikke gå videre før den andre armen er ikke-tom.**
- [ ] Runbooken fylt ut (§0s to felt) + `grep -n '<<[A-ZÆØÅ-]*>>' docs/plan/2026-08-12-demo-runbook.md`
→ TOMT — **og så `git commit`. Den commiten ER `Y`.** Uten den er runbooken en ucommittet
endring resten av sekvensen ikke kan se: frys-gaten (punkt 5 og 9) ekskluderer `docs/`, så den
passerer **stille**; punkt 11s anker passerer også, fordi HEAD da fortsatt er `X` og `X` er det
taggede. Først torsdagens §6 steg 1 roper — foran demoen.
- [ ] CHANGELOG-overskriften stemplet — **datoen LESES, ikke skrevet på forhånd og ikke tatt fra
veggklokka:** `git log -1 --format=%cs` (HEAD står nå på `Y`) — **og så `git commit`. Den
commiten ER `Z`, og det er `Z` taggen settes på.** Uten den blir `v1.0.0` tagget med
`## [Unreleased]` fortsatt i CHANGELOG — frys-gaten unntar `CHANGELOG.md` og ser det ikke —
og punkt 8 ville sammenlignet datoen mot en fil som ikke er committet.
- [ ] **Datoen RE-LEST etter at stempel-commiten (Z) finnes, FØR taggen** — `git log -1 --format=%cs`
på nytt → må være **IDENTISK** med den stemplede overskriften. Første lesning skjedde med HEAD
på **Y** (runbook-commiten), og **Y blir aldri tagget**. Faller midnatt mellom Y og Z, stemplet
du gårsdagens dato inn i commiten som faktisk tagges. Avvik = `git commit --amend` på
overskriften, les på nytt, ikke tag før de er like. *(Sklir HELE sekvensen til torsdag morgen,
er alt i orden — Y og Z er da samme dag. Det er bare midnatt MELLOM dem som brekker enigheten.)*
- [ ] **Frys-gaten kjøres ÉN GANG TIL, rett før taggen** — samme kommando som punkt 5s første arm.
**Dette er den første kjøringen som kan si noe.** Punkt 5s arm mot `<X>` var tom *per
konstruksjon* (X var HEAD da); her har runbook-commiten (Y) og CHANGELOG-stempelet (Z) flyttet
HEAD forbi X, så en `src/`-endring imellom ville dukket opp. Tom her = **det taggede treet er
beviselig det prøvde treet**. Ikke tomt = IKKE tag.
- [ ] `git tag v1.0.0` + `git push origin v1.0.0` (**`origin` alene** — `open/` er P5-vinduet)
- [ ] **Taggen bekreftet der den ble satt** — `git tag -l v1.0.0` → `v1.0.0` · `git ls-remote --tags
origin v1.0.0` → **én linje** · `git describe --tags --exact-match HEAD` → `v1.0.0`.
Dette **er** tag-bekreftelsen; den skrives ikke inn i §0 (begrunnelsen står der). Den tredje
kommandoen er nøyaktig ankeret torsdagens §6 steg 2 leser — kjørt her koster den sekunder, og
en tag som landet på feil commit oppdages på **frysedagen** i stedet for på demo-morgenen.
Samme grunn som at §6 selv ble flyttet fra «under demoen» til «før rommet fylles».
---
## 6. Torsdag 13., FØR noen er i rommet — tre kommandoer
Ingenting her vises fram. Dette er sjekken som gjør at en feil blir et ikke-problem i stedet for en
abortsti foran publikum, og den koster sekunder: demoen selv kjører på under 3 sekunder (målt).
**Hvorfor den finnes:** golden-transkriptet ble sjekket inn nettopp for å fange regresjon **mellom
onsdag og torsdag** — selv-identitet (kriterium 6) fanger ikke-determinisme, men ikke at noe flyttet
seg over natta (egnethetsreview-planen pkt. 3). Kommandoen som gjør det står allerede i §1, under
«Hvis du vil vise at outputen er den frosne». **Torsdag er den ikke valgfri, og den kjøres FØR du
går på — ikke som et show underveis.** Samme kommando, annet tidspunkt, helt annen jobb: kjørt på
scenen oppdager den regresjonen samtidig med publikum.
**1 — Rent tre**
```
git status --short --untracked-files=no
```
Forventet: **TOMT** (målt). Utrackede filer holdes bevisst utenfor: `docs/presentasjon-portfolio-optimiser.html`
eies av en annen sesjon, og en gate som roper på den lærer deg å ignorere gaten. Kommer det linjer
her, er det **sporet** innhold som har endret seg — les hva før du gjør noe annet.
**2 — Treet du kjører ER det taggede**
```
git describe --tags --exact-match HEAD
```
Forventet: **`v1.0.0`**. Torsdag skriver ingenting, så forventningen er **IDENTITET** — ikke
frys-gatens «tom modulo `docs/` og `CHANGELOG.md`». De to unntakene var begrunnet i at *onsdagen*
skriver nøyaktig dem; arvet hit ville de gjort gaten blind for den ene skriveren vi vet er aktiv i
repoet (den parallelle sesjonens `docs/`-fil).
**Feiler den, er det en BESKJED — ikke nødvendigvis en abort.** Målt: uten tag på HEAD sier git
`fatal: no tag exactly matches '<sha>'` (exit 128); finnes ikke taggen i det hele tatt, sier den
`fatal: bad revision` — da landet ikke onsdagens siste steg. Begge feiler høylytt; ingen av dem kan
forveksles med grønt. Les så hva som faktisk landet:
```
git diff --stat v1.0.0..HEAD
```
**Ingen unntak her, med vilje.** Rører det `src/`, `tests/`, `shared/`, `pyproject.toml` eller
`uv.lock` → **ikke kjør demoen fra dette treet**, gå til §3. Er det bare `docs/`, er demoen
upåvirket — men da *vet* du det, i stedet for å ha hatt en gate som tidde.
**3 — Outputen er fortsatt den frosne**
Kjør blokka i §1 («Hvis du vil vise at outputen er den frosne»). Forventet: `diff` **TOMT**.
*(Skriver `uv` en `Resolved`/`Audited`/`Installed`-linje før programmet starter, er det miljø-sjekken
sin og ikke en feil. Målt på varmt miljø er `uv run` stille — stderr er de fire linjene §1 beskriver
— men en første kjøring for dagen kan si fra. Det er ingen grunn til abort.)*
**Er 3 ikke tom: ikke debug — gå til §3.** Forskjellen er at du vet det på forhånd, så «dette er den
frosne kjøringen, sjekket inn som fasit» blir en planlagt setning i stedet for en redning.
---
## Vedlegg — tre punkter som IKKE trenger en setning på scenen (målt 2026-08-10)
STATE bar tre «scene-kosmetiske» punkter. Alle tre er målt mot det pinnede transkriptet, og
**ingen av dem er synlige i demoen**:
1. **`23700 NOK/aar` når aldri skjermen.** Personaens `rationale` er 389 tegn og beløpet står helt
til slutt (ca. tegn 370); demoen klipper på **300** tegn, så linja ender på `pga. overes…`.
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.
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.
Punktene står fortsatt som post-demo-opprydding i sine egne spor; de er bare ikke noe torsdagen
må bære.