portfolio-optimiser/docs/plan/2026-08-12-demo-runbook.md
Kjell Tore Guttormsen 2319f04420 docs(P4.5): taggens FORM var onsdagens siste ufattede beslutning
Punkt 10 sa `git tag v1.0.0` — lettvekts. Repoets eneste andre tag er
ANNOTERT (målt: `git cat-file -t v0.1.0` → `tag`, melding
`v0.1.0 — first tagged release`), så hovedreleasen ville blitt den eneste
taggen uten forfatter, dato eller melding. Ingen gate ville stoppet det:
`describe --tags` og `tag -l` svarer likt for begge former (målt).

Onsdagen skal MÅLE og UTFØRE, ikke avgjøre. Slik den sto, måtte dagen
enten tagge lettvekts uten å se avviket, eller oppdage det og improvisere
en `-m`-melding på en enveis-dag. Meldingen står nå literalt i §5 punkt 10
— og KUN der; planens to rader peker dit, så det finnes ingen andre kopi
å drifte fra. Utfyllings-gaten står derfor fortsatt på 2: en placeholder
ville gjort meldingen til et tredje felt onsdag måtte fylle.

Formen er tørrkjørt i et engangs-repo, ikke resonnert: `-a` med em-dash gir
`tag -l` → v1.0.0, `describe --tags --exact-match HEAD` → v1.0.0, filtrert
`ls-remote` → 1 linje, og em-dashen overlevde skallet.

MIN FØRSTE HYPOTESE VAR FEIL, OG MÅLINGEN FELTE DEN: jeg trodde punkt 11s
«én linje» brakk for annoterte tagger, siden `ls-remote --tags origin` viser
den peelede `^{}`-refen. Målt mot EKTE origin: MED refspec-filter gir den
annoterte v0.1.0 én linje — `^{}` matcher ikke pattern-et. Punkt 11 var
robust hele tiden. Presisert i teksten, fordi neste leser vil ha samme tvil.

EN ANDRE DEFEKT FALT UT AV Å MÅLE MOT EKTE REMOTE: origin rate-limiter SSH
på burst. Målt: de to første ls-remote gikk igjennom, de fire neste ga
`Connection refused`, porten svarte igjen etter en pause, og Forgejo-weben
var oppe hele tiden (HTTP 303) — serveren var aldri nede. Punkt 11 kjører
to SSH-kall rett etter en push, altså nøyaktig et burst.

Alvorligheten ligger i at BEGGE utfall gir null linjer på stdout (målt):
taggen mangler = exit 0 + tom stderr; kom ikke fram = exit 128 + melding.
Et `| wc -l` kan ikke skille dem — så en rate-limitet bekreftelse leses som
«taggen landet ikke» dagen etter at push-en faktisk lyktes, på enveis-dagen.
Diskriminatoren er exit-koden; retteslen er vent-og-kjør-på-nytt, aldri
re-push eller re-tag. Samme klasse som §5 punkt 5s arm: en gate må kunne
feile på riktig grunn, og de to måtene den svikter på må se ulike ut.

Ingen nye punkter, ingen renummerering: begge endringene sitter PÅ punkt 10
og 11. Målt: §5 fortsatt elleve punkter · utfyllings-gaten 2 · CHANGELOG
urørt ([Unreleased] = 1, null link-refs) · kun to diff-hunks, begge i §5, så
§0/§1/§2/§3/§4/§6 er byte-urørt og økt 9s 67 målinger av §2 står · planens
to rader uendret i linjeantall og pipe-struktur (5/0 og 6/3, før = etter) ·
frys-gaten mot HEAD TOM og diskriminerende (c255662 = 6 filer).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nZQkAJytbaUAYxMAU2wk7
2026-08-11 22:17:29 +02:00

24 KiB
Raw Blame History

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 HEADv1.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-inmaterialize_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_attemptsVALIDATED 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. 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 -l8 (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 noteresgit status --short --untracked-files=noTOMT. 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.**
    **De to måtene armen kan svikte på ser IKKE like ut, og det er med vilje:** *stille tomt* =
    pathspec-en er ødelagt (det er dette armen finnes for). *`fatal: bad revision`* = du bommet på
    hashen — den er `c255662`, målt ikke-tom 08-11. En tom utskrift er altså aldri «feil hash»;
    da hadde git ropt.
    
  • 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 taggengit 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 -a v1.0.0 -m "v1.0.0 — first complete eight-step loop" + git push origin v1.0.0 (origin aleneopen/ er P5-vinduet). Annotert (-a), ikke lettvekts. Repoets eneste andre tag er annotert — målt: git cat-file -t v0.1.0tag, med tagger-header og meldingen v0.1.0 — first tagged release. En lettvekts v1.0.0 ville gjort hovedreleasen til den eneste taggen uten forfatter, dato eller melding. Meldingen står literalt her nettopp for at den ikke skal improviseres på en enveis-dag — den er ikke et felt i §0, og utfyllings-gaten forblir derfor på 2. Formen er tørrkjørt 08-11 (eget engangs-repo, ikke dette treet): -a med em-dash gir git tag -lv1.0.0, git describe --tags --exact-match HEADv1.0.0, filtrert ls-remote1 linje, og em-dashen overlevde skallet.

  • Taggen bekreftet der den ble sattgit tag -l v1.0.0v1.0.0 · git ls-remote --tags origin v1.0.0én linje · git describe --tags --exact-match HEADv1.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». «Én linje» gjelder BEGGE tag-former, så punkt 10s -a endrer ikke forventningen her. Målt mot ekte origin 08-11: den annoterte v0.1.0 gir to linjer uten filter (refs/tags/v0.1.0 + den peelede refs/tags/v0.1.0^{}), men én med v0.1.0 som refspec — ^{} matcher ikke pattern-et. Kjør den derfor med taggnavnet, som over. ⚠️ TOM UTSKRIFT ER TVETYDIG — LES EXIT-KODEN. origin rate-limiter SSH på burst. Målt 08-11: de to første ls-remote gikk igjennom, de fire neste ga Connection refused, og porten svarte igjen etter en pause — Forgejo-weben var oppe hele tiden (HTTP 303), så serveren var aldri nede. Punkt 11 kjører to SSH-kall rett etter en push, altså nøyaktig et burst. Begge tilfellene gir null linjer på stdout (målt): taggen mangler = exit 0 og tom stderr · kom ikke fram = exit 128 og ssh: connect to host … Connection refused på stderr. Et | wc -l alene kan altså ikke skille dem, og lest som «taggen landet ikke» er det en abort på feil grunnlag — dagen etter at push-en faktisk lyktes. Exit 128: vent et halvminutt og kjør kommandoen på nytt. Ikke re-push, og ikke re-tag. git tag -l er lokal og svarer uansett; den skiller «tagget lokalt» fra «nådde origin» uten å røre nettverket.


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.lockikke 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.stdout0. 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.stdout0.
  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.