@haus-tech/haus-workflow 4.9.0 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/cli.js +1381 -871
  3. package/library/catalog/manifest.json +97 -34
  4. package/library/global/agents/request-assessor.md +2 -2
  5. package/library/global/agents/request-scorer.md +13 -8
  6. package/library/global/agents/request-specifier-eskalerad.md +1 -1
  7. package/library/global/agents/request-specifier.md +1 -1
  8. package/library/global/skills/request/SKILL.md +691 -374
  9. package/library/global/skills/request/scripts/archive.mjs +1376 -0
  10. package/library/global/skills/request/scripts/check-lasta.mjs +64 -0
  11. package/library/global/skills/request/scripts/compare-scores.mjs +23 -15
  12. package/library/global/skills/request/scripts/contract-check.mjs +108 -36
  13. package/library/global/skills/request/scripts/lib.mjs +66 -0
  14. package/library/global/skills/request/scripts/precheck.mjs +30 -27
  15. package/library/global/skills/request/scripts/report.mjs +571 -0
  16. package/library/global/skills/request/scripts/resolve-anchors.mjs +243 -0
  17. package/library/global/skills/request-assess-impact/SKILL.md +10 -10
  18. package/library/global/skills/request-score/SKILL.md +19 -10
  19. package/library/global/skills/request-sync/SKILL.md +7 -7
  20. package/library/global/skills/request-write-spec/SKILL.md +6 -6
  21. package/library/global/skills/request-write-spec/references/specformat.md +2 -2
  22. package/package.json +1 -1
  23. package/library/global/agents/request-classifier.md +0 -55
  24. package/library/global/skills/request/scripts/compare-classifications.mjs +0 -94
  25. package/library/global/skills/request/scripts/quote-repair.mjs +0 -166
  26. package/library/global/skills/request-classify/SKILL.md +0 -79
  27. package/library/global/skills/request-classify/references/klassningsinstruktion.md +0 -152
@@ -1,28 +1,29 @@
1
1
  ---
2
2
  name: request
3
- description: Enda ingången till Haus requestflöde. Använd när användaren säger "/request", "kör requestflödet", "speca och bedöm", "prisa den här posten", "ta önskemålet hela vägen", "vad ligger i kön", "detaljer <post>", eller när flera request-skills ska kedjas i en följd. Enskilda request-skills (request-write-spec, request-classify m.fl.) fungerar fortfarande fristående; den här skillen äger kedjan, arkivet och dispatchen till subagenter.
3
+ description: Enda ingången till Haus requestflöde. Använd när användaren säger "/request", "kör requestflödet", "speca och bedöm", "prisa den här posten", "ta önskemålet hela vägen", "vad ligger i kön", "detaljer <post>", eller när flera request-skills ska kedjas i en följd. Enskilda request-skills (request-write-spec, request-score m.fl.) fungerar fortfarande fristående; den här skillen äger kedjan, arkivet och dispatchen till subagenter.
4
4
  ---
5
5
 
6
6
  # /request — requestflödets ingång
7
7
 
8
- Tunn orkestrering ovanpå de fem request-skillsen. Den här skillen innehåller
9
- **noll domänregler**: allt om specformat, klassning, poäng och Asana-fält bor i
8
+ Tunn orkestrering ovanpå de fyra request-skillsen. Den här skillen innehåller
9
+ **noll domänregler**: allt om specformat, poäng och Asana-fält bor i
10
10
  respektive skill. Här bor bara routing, kontextbärande, arkivet och dispatchen.
11
11
 
12
12
  ## Uppgifter
13
13
 
14
14
  `/request [uppgift]` — utan argument: visa menyn nedan.
15
15
 
16
- | Uppgift | Gör |
17
- | -------------------------------------- | ---------------------------------------------------------------------------------------- |
18
- | `speca <önskemål eller gid>` | Källmanifest → dispatcha `request-specifier` → arkivera spec + granskarblock |
19
- | `bedöm <gid, slug eller nr>` | Grind → säkra analys → dispatcha `request-classifier` ∥ `request-scorer` → arkivera JSON |
20
- | `status` | Kö-läge via request-sync, BLUF-tabell med radnummer |
21
- | `godkänn <nr eller gid>` | Flytta Offer status framåt via request-sync — se grindavsnittet |
22
- | `underkänn <nr eller gid> <skäl>` | Skäl som Asana-kommentar + iterationsräknare via request-sync |
23
- | `detaljer <gid, slug eller nr> [steg]` | Skriv ut arkiverad JSON/dokument för posten |
24
- | `utvärdera <post…> [modell]` | A/B-kör hela flödet per modellvariant till experimentytan; `sammanställ` ger svepvyn |
25
- | `hjälp` | Förklara uppgifterna, rör inga filer |
16
+ | Uppgift | Gör |
17
+ | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
18
+ | `speca <önskemål eller gid>` | Källmanifest → dispatcha `request-specifier` → arkivera spec + granskarblock |
19
+ | `bedöm <gid, slug eller nr>` | Grind → säkra analys → dispatcha `request-scorer` → arkivera JSON |
20
+ | `status` | Kö-läge via request-sync, BLUF-tabell med radnummer |
21
+ | `godkänn <nr eller gid>` | Flytta Offer status framåt via request-sync — se grindavsnittet |
22
+ | `underkänn <nr eller gid> <skäl>` | Skäl som Asana-kommentar + iterationsräknare via request-sync |
23
+ | `döm <post>` | Människodom vid OLIKA: välj körning, beställ omkörning eller underkänn underlaget |
24
+ | `detaljer <gid, slug eller nr> [steg]` | Skriv ut arkiverad JSON/dokument för posten |
25
+ | `utvärdera <post…> <modell…>` | Kör flödet en gång per namngiven modell mot experimentytan, minst en modell; `sammanställ` ger svepvyn, `rapport` en läsbar jämförelse |
26
+ | `hjälp` | Förklara uppgifterna, rör inga filer |
26
27
 
27
28
  ## Hårda regler
28
29
 
@@ -32,20 +33,30 @@ respektive skill. Här bor bara routing, kontextbärande, arkivet och dispatchen
32
33
  Skill-verktyget (`request-sync`).
33
34
  - **En skrivare i taget mot Asana.** Batchresultat skrivs tillbaka sekventiellt,
34
35
  en post i taget, aldrig parallellt.
35
- - **Dokument klistras bytetroget** in i subagent-prompter. Klassningens och
36
- poängens belägg är ordagranna citat; en trunkerad eller omformaterad inklistring
37
- ger tyst fel svar. Klistra aldrig in en sammanfattning.
38
- - **Prislistan och repot hålls borta från classifier/scorer.** Deras
39
- agentdefinitioner saknar sök- och shellverktyg (Grep/Glob/Bash), men Read
40
- finns kvar för skillens referensfil — läs-scopet är alltså en promptregel,
36
+ - **Dokument klistras bytetroget** in i specifier- och assessor-prompter
37
+ (önskemål, spec, granskarblock). En trunkerad eller omformaterad
38
+ inklistring flyttar källtexten under agentens fötter och ger tyst fel
39
+ svar. Klistra aldrig in en sammanfattning.
40
+ - **Prissättningens underlag pekas ut per sökväg (ADR-0099).** Scorerns
41
+ prompt bär två namngivna absoluta sökvägar (`spec.md`, `impact.md`) i
42
+ stället för inklistrad text — agenten läser filerna själv via Read, så
43
+ ingen avskrift kan trunkeras, och beläggens ankare (källa + delsträng)
44
+ löses av scripten mot exakt samma filer på disk. Läs-scopet är EXAKT
45
+ dessa två filer plus skillens egen katalog, aldrig
46
+ körningskatalogen som helhet: `granskarblock.md` är assessorns underlag
47
+ och äldre pricing-JSON vore ren kontaminering av dubbelkörningens
48
+ oberoende.
49
+ - **Prislistan och repot hålls borta från scorern.** Dess
50
+ agentdefinition saknar sök- och shellverktyg (Grep/Glob/Bash), och Read
51
+ är promptbegränsad enligt regeln ovan — läs-scopet är en promptregel,
41
52
  inte en fysisk spärr. Därför: lägg aldrig prislistans innehåll i prompten,
42
53
  och lägg inga egna sökvägar i dispatcherns rader utöver etiketterna i mallen.
43
- Sökvägar som redan står i det bytetrogna underlaget (analysen namnger filer,
54
+ Sökvägar som redan står i underlaget (analysen namnger filer,
44
55
  det är dess jobb) är inte ett brott — regeln gäller det dispatchern
45
56
  lägger till. Granska `LÄSTA:`-raden i svaret (se dispatch-kontraktet).
46
57
  - **Granskarblocken når aldrig kunden.** Specens granskarblock arkiveras separat
47
58
  (`granskarblock.md`). Analysens "## Till granskaren"-block ligger kvar i
48
- `impact.md` (det är del av underlaget för klassningen) — men inget
59
+ `impact.md` (det är del av underlaget för poängsättningen) — men inget
49
60
  granskarblock får någonsin skickas med i material som går till request-sync
50
61
  för kundsynliga fält.
51
62
  - **Arkivet är cache, Asana är sanningen.** Vid konflikt vinner brädan.
@@ -54,39 +65,59 @@ respektive skill. Här bor bara routing, kontextbärande, arkivet och dispatchen
54
65
  - **Experimentdata når aldrig Asana.** `utvärdera` skriver aldrig via
55
66
  request-sync; dess enda Asana-kontakt är läsande hämtning av önskemålet i
56
67
  huvudkonversationen. `experiments/`-trädet är inte postkataloger: sync,
57
- batch och guard läser bara `.haus/requests/<gid-eller-slug>/` på toppnivån
68
+ batch och guard läser bara `.haus-workflow/requests/<gid-eller-slug>/` på toppnivån
58
69
  och rör aldrig `experiments/`.
70
+ - **Maskinen kör om enbart vid scriptbevisat kontraktsbrott.** Omdispatch
71
+ (budget EN per agent) svarar på ett fel som precheck, resolve-anchors eller
72
+ contract-check har bevisat. En oenighet mellan två GILTIGA körningar är en
73
+ bedömningsfråga och går alltid till människan via uppgiften `döm`: fler
74
+ maskinkörningar producerar fler röster, aldrig mer sanning. Ingen ny
75
+ omkörningsmekanism får läggas till som bryter mot detta.
59
76
 
60
- ## Arkivet: `.haus/requests/`
77
+ ## Arkivet: `.haus-workflow/requests/`
61
78
 
62
79
  Orkestreringens kuvert. De haus-hanterade request-skillsen känner inte till det;
63
80
  den här skillen (och agenterna via sina prompter) underhåller det.
64
81
 
65
82
  **En katalog = en spec = en prissättningsbar post.** En bunt (flera specar ur
66
- samma önskemål) blir flera kataloger — klassningen räknar omfattning per spec
67
- och poängen sätts per post, så en katalog med flera specar går inte att bedöma.
83
+ samma önskemål) blir flera kataloger — poängen sätts per post, så en katalog
84
+ med flera specar går inte att bedöma.
68
85
 
69
86
  ```
70
- <repo-rot>/.haus/requests/<gid-eller-slug>/
87
+ <repo-rot>/.haus-workflow/requests/<gid-eller-slug>/
71
88
  ├── spec.md EN spec, bytetrogen — det granskaren godkänner
72
89
  ├── granskarblock.md "## Till granskaren" (bunt: samma block
73
90
  │ kopieras till varje delkatalog)
74
91
  ├── impact.md påverkansanalysen, ordagrann inkl. sitt
75
92
  │ Till granskaren-block
76
- ├── classification-pricing-vN.json nytt varv = ny fil, skriv aldrig över
77
- ├── score-pricing-vN.json N är gemensamt för paret (se bedöm steg 8)
78
- ├── classification-guard-vN.json guard-utfall synkas aldrig, bara arkiveras
79
- ├── score-guard-vN.json
93
+ ├── score-pricing-vN.json nytt varv = ny fil, skriv aldrig över
94
+ ├── score-guard-vN.json guard-utfall synkas aldrig, bara arkiveras
95
+ │ (egen N-sekvens per typ, skild från pricing)
80
96
  └── state.json
81
97
  ```
82
98
 
99
+ Äldre arkiv (före ADR-0098) bär även `classification-pricing-vN.json` /
100
+ `classification-guard-vN.json` från den borttagna klassaxeln. De läses och
101
+ visas som förut (`detaljer`, rapporten, `archive.mjs`) men skrivs aldrig av
102
+ nya körningar.
103
+
104
+ Förkastade försök bär `-avvisad`-suffix och står utanför N-räkningen:
105
+ prissättningens `<typ>-pricing-vN-avvisad.json` och textstegens
106
+ `spec-avvisad-<datum>.md` / `impact-avvisad-<datum>.md` (ADR-0099) — se
107
+ omdispatch-regeln i dispatch-kontraktet.
108
+
83
109
  - Katalognamn: Asana-gid när posten finns på brädan, annars en kebab-slug av
84
110
  spec-rubriken. Bunt utan gid: `<slug>-1`, `<slug>-2`, … När gid myntas senare:
85
111
  döp om katalogen till gid och notera sluggen i `state.json.former_slug`.
86
- - **Första gången arkivet skapas i ett repo:** säkerställ att `.haus/requests/`
87
- står i repots `.gitignore` (lägg till raden om den saknas, säg det).
88
- Prisunderlag ska inte in i kundens historik. `.haus/request.json` (syncens
89
- config) förblir spårad.
112
+ - **Gitignore ägs av haus, inte av dig.** `.haus-workflow/requests/` står i den
113
+ haus-hanterade `.gitignore`-blocken och skrivs av `haus apply` — lägg aldrig till
114
+ raden själv, och hand-editera aldrig blocket. Står arkivet ändå spårat i git:
115
+ kör `haus apply --write`, som både skriver regeln och avspårar filerna.
116
+ Prisunderlag ska inte in i kundens historik. `.haus-workflow/request.json` (syncens
117
+ config) förblir spårad — den är konfiguration, inte arbetsmaterial.
118
+ - **Gammalt arkiv under `.haus/`:** flyttas av `haus apply` till
119
+ `.haus-workflow/` (ADR-0103). Skriv aldrig till `.haus/` igen, och flytta inte
120
+ själv — kör `haus apply --write` och låt kommandot göra det.
90
121
 
91
122
  ### `state.json`
92
123
 
@@ -113,36 +144,44 @@ och poängen sätts per post, så en katalog med flera specar går inte att bed
113
144
  `{ "source": "asana" | "manual", "by": "<namn, krävs vid manual>",
114
145
  "status_read": "<värde>", "date": "YYYY-MM-DD" }`.
115
146
  Anteckningen är spårbarhet, aldrig själva godkännandet.
116
- - `granskarutslag` är en lista, en rad per viktningsutslag (granskaren
117
- konstaterar att en körning hade rätt, utan att någon sakuppgift ändras).
118
- `bedöm` steg 6 APPENDAR en rad, skriver ALDRIG över tidigare rader:
119
- `{ "varv": N, "date": "YYYY-MM-DD", "beslut": "<granskarens beslut,
120
- ordagrant>" }`. Tom lista (`[]`) när inget viktningsutslag ännu skett.
121
- Listan är ett revisionsspår och läses medvetet ALDRIG tillbaka in i någon
122
- prompt: utslaget är granskarens dom över körningarna, och når den agenterna
123
- styr den nästa bedömning i stället för att dokumentera den föregående.
124
- Nollas (töms) vid respec, av samma skäl som `approval` nollas (se `speca`
125
- steg 5): ett utslag mot en ersatt spec är inte längre giltigt. Bor ENDAST
126
- i `state.json`. Ett sakändringsutslag går i stället in i `impact.md` som
127
- Granskarutslag-blocket (se `bedöm` steg 6), aldrig hit.
147
+ - `granskarutslag` är en lista, en rad per dom, skriven via `archive.mjs utslag`, aldrig för hand.
148
+ Radform: `{ "typ": "viktning" | "kalibrering" | "sakandring", "date": "YYYY-MM-DD", "beslutad_av": "<namn>", ... }`.
149
+ Viktning bär dessutom `beslut` (ordagrant), `axel` (`score`; `classification`
150
+ förekommer i äldre arkiv), `n` och `galler_fil`: raden ÄR spärrlyftet,
151
+ `archive.mjs sparr` läser den.
152
+ Kalibrering bär `fraga`, `lasning1`, `lasning2`: en notis om att en
153
+ protokollfråga lät två giltiga läsningar divergera, underlag för nästa
154
+ instruktionsversion. Sakändring bär `beslut`: kvittot i `state.json` för
155
+ samma händelse som citatblocket i `impact.md` (se `döm`, vägen "Underlaget
156
+ är fel"). Det ordagranna beslutet bor i citatblocket, raden här är bara
157
+ revisionsspåret, aldrig underlaget självt.
158
+ Listan läses medvetet ALDRIG tillbaka in i någon prompt, oavsett typ: ett
159
+ utslag som når agenterna styr nästa bedömning i stället för att dokumentera
160
+ den föregående. Nollas (töms) vid respec, av samma skäl som `approval`
161
+ nollas (se `speca` steg 5): ett utslag mot en ersatt spec är inte längre
162
+ giltigt. Bor ENDAST i `state.json`. Rader skrivs aldrig om.
128
163
  - `runs[]` får en rad per dispatch, även spec- och analyskörningar:
129
164
  `{ "file": "spec.md" | "impact.md" | "<run>.json", "skill": "...",
130
- "agent": "...", "model": "<ur MODELL-raden>", "effort": "<ur agentdefinitionen>",
165
+ "agent": "...",
166
+ "model": { "uppgiven": "<ur MODELL-svansraden>", "fakturerad": "<känd fakturerad modell, annars null>" },
167
+ "effort": "<ur agentdefinitionen>",
131
168
  "date": "YYYY-MM-DD",
132
169
  "reference_sha256": { "<referensfil>": "<sha256>" },
133
170
  "underlag_sha256": { "spec.md": "<sha256>", "impact.md": "<sha256>" },
134
171
  "double_run": "bekräftad" | "olika" | "tolerans" | null,
135
- "superseded_by": null, "partial_rerun": false, "pairs_with": null,
136
- "quote_repairs": <antal> }`.
137
- `quote_repairs` är frivilligt (utelämnas när det är 0): antal RÄTTAT-rader
138
- från `quote-repair.mjs` för den körningen.
139
- `partial_rerun` och `pairs_with` är frivilliga och sätts bara på en
140
- pricing-rad ur ett riktat varv (se `bedöm` steg 6): `partial_rerun: true`
141
- markerar att raden kom ur en enaxlig omkörning, och `pairs_with` namnger
142
- den stående axelns fil den paras med. Pekaren bor på den omkörda axelns
143
- nya rad, aldrig på den stående axelns äldre rad, som aldrig skrivs om.
144
- Saknas (eller står `false`/`null`) på spec- och analysrader och på rader
145
- ur ett fullt varv.
172
+ "superseded_by": null, "partial_rerun": false, "pairs_with": null }`.
173
+ `model` bär två fält (ADR-0099): `uppgiven` är agentens självrapport ur
174
+ MODELL-svansraden, `fakturerad` fylls när dispatchern känner den
175
+ fakturerade modellen (`add-run --fakturerad`), annars `null` — faktureringens
176
+ sanning per körning bor i `proveniens.fakturerade_modeller`. Äldre arkiv bär
177
+ `model` som ren sträng och läses som förut.
178
+ `quote_repairs` förekommer i äldre arkiv (före ADR-0092) och läses men
179
+ skrivs aldrig av nya körningar.
180
+ Stämplingen görs alltid via `archive.mjs add-run`; hur N räknas ut, vilket
181
+ suffix en fil får och hur en riktad omkörnings parning skrivs bor i
182
+ scriptet och testerna. VILKA `--typ` som skickas till `archive.mjs next-n`,
183
+ alltså vilka axlar som faktiskt kördes i varvet, är fortfarande
184
+ orkestratorns beslut, inte scriptets.
146
185
  `underlag_sha256` är underlagsproveniensen: hashen för de dokument körningen
147
186
  faktiskt såg. Precheck vägrar bedöma när analysens spec-hash inte matchar
148
187
  nuvarande `spec.md`, och guard ska vägra diffa mot en körning vars underlag
@@ -154,46 +193,74 @@ ordagrant>" }`. Tom lista (`[]`) när inget viktningsutslag ännu skett.
154
193
  alla arkiverade körningar. Samma verktyg används för `underlag_sha256`.
155
194
  Referensfilerna ligger under Claude-konfigkatalogen (`CLAUDE_CONFIG_DIR`
156
195
  eller `~/.claude`). En senare guard-körning jämför hash och vägrar diffa vid
157
- skevhet ("omklassning krävs, skalan har ändrats").
196
+ skevhet ("ompoängsättning krävs, skalan har ändrats").
158
197
  - `status` blir ALDRIG `priced` när något stopputfall finns i varvets
159
- körningar, eller när det GÄLLANDE paret har `double_run: "olika"` olyft.
160
- Spärren gäller det gällande paret, inte varje enskild rad för sig: har ett
161
- senare riktat varv landat LIKA eller LIKA_TOLERANS, är spärren lyft för
162
- hela paret, även om den oeniga axelns äldre rader fortfarande bär
163
- `double_run: "olika"` (de skrivs aldrig om, se `partial_rerun`/
164
- `pairs_with` ovan). Läs upplåsningen på den omkörda axelns senaste rad,
165
- aldrig på en äldre. `double_run: "tolerans"` räknas som bekräftad och
166
- blockerar inte `priced`.
198
+ körningar, eller när den GÄLLANDE raden har `double_run: "olika"` olyft, eller
199
+ när den gällande raden bär `partial_rerun: true` utan en matchande
200
+ dom (en beställd omkörning väntar på granskaren, se uppgiften `döm`).
201
+ `archive.mjs sparr` avgör bägge skälen identiskt (`sparrSkal`).
202
+ Spärren lyfts av ett viktningsutslag (`archive.mjs sparr` är den enda tolken,
203
+ se uppgiften `döm`) eller av en beställd omkörning vars dom landat i ett
204
+ viktningsutslag; ingen annan väg lyfter den, och den oeniga axelns äldre
205
+ rader skrivs aldrig om även efter lyft.
206
+ `double_run: "tolerans"` räknas som bekräftad och blockerar inte `priced`.
207
+ I äldre arkiv som bär en klassaxel (före ADR-0098) vägrar
208
+ `archive.mjs set-status priced` därutöver ett OFULLSTÄNDIGT par:
209
+ klass- och poängaxelns gällande varvnummer skiljer sig utan att skillnaden
210
+ är en förklarad enaxlig omkörning (`partial_rerun: true` på den högre
211
+ axelns gällande rad), eller poängaxeln saknas helt. Ett arkiv utan
212
+ klassaxel prövas aldrig mot par-regeln — poängen är den enda axeln.
213
+ Det här skälet prövas av `pricedBlockers` (`archive.mjs set-status`),
214
+ inte av `archive.mjs sparr` — reglerna bor i scriptet och dess tester.
167
215
  - `spec_synced` sätts av speca steg 7 (posten lagd på brädan);
168
216
  `guard_run` reserveras för guard-kommandot (senare byggsteg).
169
217
 
170
218
  ### Experimentytan: `experiments/`
171
219
 
172
- A/B-körningar från `utvärdera` bor i ett eget träd och är aldrig prisposter:
220
+ Modellkörningar från `utvärdera` bor i ett eget träd och är aldrig prisposter:
173
221
 
174
222
  ```
175
- <repo-rot>/.haus/requests/experiments/<slug>/
176
- ├── experiment.json ticketreferens, datum, kandidatmodell,
177
- │ modell per agent per varv, manifest-hash, status
178
- ├── jamforelse.md skrivs när båda varven är klara
179
- ├── baseline/ full standardstruktur enligt arkivkontraktet ovan
180
- └── <kandidatmodell>/ samma struktur; delkataloger del-N/ vid DELA_UPP
223
+ <repo-rot>/.haus-workflow/requests/experiments/<slug>/
224
+ ├── experiment.json ticketreferens, datum, modellista, manifest-hash,
225
+ │ status per körning
226
+ ├── <modell>/ `default` som modellvärde ger katalognamnet
227
+ │ `baseline/`
228
+ │ ├── k1/
229
+ │ ├── k1/korning.json headless-svaret RÅTT: USD, tokens, dispatcher
230
+ │ ├── k1/matning.json startarens egen mätning: startad, avslutad, wall_ms
231
+ │ ├── k2/ senare körning av SAMMA modell
232
+ │ └── k2/del-1/ … delkataloger vid DELA_UPP
233
+ └── <annan modell>/k1/ …
181
234
  ```
182
235
 
183
- - Katalognamnet `experiments` är reserverat — en spec vars rubrik skulle
184
- sluggas till `experiments` får suffixet `-post`.
185
- - Variantkatalogerna följer postkatalogens kontrakt bytetroget (spec.md,
186
- granskarblock.md, impact.md, pricing-JSON, state.json, `runs[]`-reglerna) —
187
- därför fungerar `precheck.mjs` och `contract-check.mjs` oförändrade, och
188
- proveniensen (modell, effort, hashar) följer med gratis.
189
- - `state.json` i en variantkatalog bär markören `"experiment": true`.
190
- `approval` förblir `null` och `status` blir ALDRIG `priced` — ett
236
+ - **Två filer, två ägare.** `korning.json` är headless-svaret ordagrant och
237
+ ägs av CLI:n; `matning.json` är startarens klocka och ägs av oss. En
238
+ haus-nyckel inne i `korning.json` skulle göra den till ett blandat format
239
+ som inte längre går att ställa mot CLI:ns egen utdata.
240
+ - **Ingen körning skrivs någonsin över.** Värdet i en gammal körning är att
241
+ kunna jämföra samma modell före och efter en ändring i skillen eller
242
+ agentprompterna; skriver en ny körning över den är den jämförelsen borta för
243
+ alltid. Varje körning får därför en egen katalog `k<N>` under sin modell.
244
+ - **Körningskatalogen ÄR postkatalogen.** Den följer arkivkontraktet
245
+ bytetroget (spec.md, granskarblock.md, impact.md, pricing-JSON, state.json,
246
+ `runs[]`-reglerna), så `precheck.mjs` och `contract-check.mjs` fungerar
247
+ oförändrade, de pekas bara på `<modell>/k<N>/`. Proveniensen följer med gratis.
248
+ - **Numret allokeras aldrig av skillen själv**, alltid med
249
+ `archive.mjs ny-korning <modellkatalog>`, som skapar katalogen
250
+ atomiskt och skriver dess sökväg. Två samtidiga processer, till exempel två
251
+ Claude-konton mot samma checkout, får då `k2` och `k3`, aldrig samma katalog.
252
+ Räknar skillen själv förlorar den ena processens körning tyst.
253
+ - `state.json` i en körningskatalog bär markören `"experiment": true`.
254
+ `approval` förblir `null` och `status` blir ALDRIG `priced`: ett
191
255
  experimentresultat kan inte förväxlas med en prispost.
192
- - `experiment.json` bär `status` per variant: `"klar"` (båda stegen
193
- arkiverade och jämförelse-underlag komplett), `"ofärdig"` (avbruten,
194
- subagentdöd, kontraktsbrott eller MODELL-avvikelse), `"stopp"` (arkiverat
195
- stopputfall).
196
- - `experiments/` ärver gitignore-skyddet från `.haus/requests/`.
256
+ - Katalognamnet `experiments` är reserverat: en spec vars rubrik skulle sluggas
257
+ till `experiments` får suffixet `-post`.
258
+ - `experiment.json` bär `varianter: []` i deklarationsordning och `status` per
259
+ körning: `"klar"`, `"ofärdig"`, `"stopp"`, `"väntar på dom"`. Literalerna
260
+ skrivs exakt så, mellanslag och diakritiska tecken inkluderade. Äldre filer
261
+ med `baselinemodell` och `kandidatmodell` läses utan fel; nya skrivningar
262
+ använder bara `varianter`.
263
+ - `experiments/` ärver gitignore-skyddet från `.haus-workflow/requests/`.
197
264
 
198
265
  ## Uppgift: `speca`
199
266
 
@@ -218,8 +285,14 @@ A/B-körningar från `utvärdera` bor i ett eget träd och är aldrig prisposter
218
285
  kopieras till varje katalog som `granskarblock.md`). Bunt ur en gid-post:
219
286
  del 1 behåller gid-katalogen, resterande delar får slug-kataloger med
220
287
  `bundle.parent_slug` satt till gid:t; syskontasks på brädan mintas först
221
- vid sync. Lägg en `runs`-rad (`file: "spec.md"`, med `underlag_sha256`
222
- tomt) i varje berörd state.json.
288
+ vid sync. Stämpla varje berörd `state.json` med
289
+ `node <skillkatalog>/scripts/archive.mjs add-run <katalog> --file spec.md
290
+ --skill request-write-spec --agent <request-specifier|request-specifier-eskalerad>
291
+ --model <ur MODELL-svansraden> --ref references/specformat.md=<hash via ref-hash.mjs
292
+ på request-write-spec/references/specformat.md>`. Scriptet sätter
293
+ `underlag_sha256` tomt, en spec har inget underlag i repot — men
294
+ referens-hashen måste sättas explicit precis som för poängen, annars
295
+ tappas specformatets proveniens (se runs-kontraktet).
223
296
  **Respec över befintlig katalog invaliderar analysen:** när `spec.md`
224
297
  ersätts döps ev. `impact.md` om till `impact-superseded-<datum>.md`,
225
298
  status sätts till `spec_draft`, `approval` nollas och `granskarutslag`
@@ -228,7 +301,15 @@ A/B-körningar från `utvärdera` bor i ett eget träd och är aldrig prisposter
228
301
  den skevhet proveniens-kontrollen finns för att stoppa.
229
302
  6. **Formkontroll.** Kör `node <skillkatalog>/scripts/precheck.mjs <katalog>`
230
303
  per katalog. BRISTER i spec/granskarblock → dispatcha om specifiern EN gång
231
- enligt omdispatch-regeln; BRISTER i `state.json` är wrapperns egna filer och
304
+ enligt omdispatch-regeln. **Det förkastade utkastet får kvitto
305
+ (ADR-0099, samma regel som prissättningens `-avvisad`):** arkivera det
306
+ som `spec-avvisad-<datum>.md` innan omdispatchen, och stämpla det med en
307
+ egen runs-rad via `archive.mjs add-run --file spec-avvisad-<datum>.md
308
+ --note "<kontrollens felrader>"` (övriga flaggor som spec-radens). VARJE
309
+ dispatch får sin egen runs-rad — även den förkastade — så arkivet bär
310
+ både att ett omtag skedde och varför; en spec godkänd i andra försöket
311
+ får aldrig se ut som en som blev rätt direkt.
312
+ BRISTER i `state.json` är wrapperns egna filer och
232
313
  rättas av wrappern själv, aldrig via omdispatch. Kvarstår brister, visa dem
233
314
  och stanna. VARNING:ar visas men stoppar inget.
234
315
  7. **Svara BLUF:** en rad per spec (rubrik + ev. flaggor), sedan granskarblocket
@@ -239,8 +320,8 @@ A/B-körningar från `utvärdera` bor i ett eget träd och är aldrig prisposter
239
320
 
240
321
  ## Uppgift: `bedöm`
241
322
 
242
- Kör prissättningsbenet: påverkansanalys om den saknas, sedan klass ∥ poäng med
243
- oenighetsgrind.
323
+ Kör prissättningsbenet: påverkansanalys om den saknas, sedan poängsättning med
324
+ driftvakt.
244
325
 
245
326
  1. **Grind — obligatorisk, aldrig överhoppbar.** Bedömning kräver en granskad
246
327
  spec, och godkännandet bor i Asana:
@@ -249,7 +330,7 @@ oenighetsgrind.
249
330
  Endast `✅ Spec ok` (normaliserad på alfabetiska kärnan) släpper igenom.
250
331
  Annan status: visa den och stanna. En inklistrad spec för en post som har
251
332
  gid räknas inte — det är brädstatusen som gäller, läs den ändå.
252
- - **Posten saknar gid MEN repot har `.haus/request.json`:** vägra — repot är
333
+ - **Posten saknar gid MEN repot har `.haus-workflow/request.json`:** vägra — repot är
253
334
  kopplat till en bräda, så en gid-lös post ska in via intaget och granskas
254
335
  där, inte prissättas vid sidan av. Grinden är värd något bara om den inte
255
336
  går att kliva runt.
@@ -274,207 +355,110 @@ oenighetsgrind.
274
355
  skickas med som internt underlag (samma förtroendedomän; flaggorna där är
275
356
  ofta analysens viktigaste ingång). Arkivera svaret som `impact.md`
276
357
  (inklusive dess "## Till granskaren"-block — visa blocket för användaren,
277
- aldrig till kundfält), lägg en `runs`-rad (`file: "impact.md"`,
278
- `underlag_sha256` med hash för `spec.md`). Kör precheck igen med `--impact`.
279
- 4. **Prissättning.** Dispatcha `request-classifier` och `request-scorer`
280
- **parallellt i samma meddelande** enligt promptmallarna. Ingen prislista,
281
- inget repo, inga andra posters dokument i prompterna.
282
- 5. **Kontraktskontroll.** Strippa svansraderna (`MODELL:`, `LÄSTA:`, ev.
283
- `AVVIKELSE:`) ur varje svar, skriv den rena JSON:en till en temporär fil och
284
- kör `node <skillkatalog>/scripts/contract-check.mjs <svar.json> <spec.md> <impact.md> --expect-id <katalogens id>`.
285
- BROTT → kör först
286
- `node <skillkatalog>/scripts/quote-repair.mjs <svar.json> <spec.md> <impact.md>`
287
- (deterministisk citaträttning: ett nästan-ordagrant belägg ersätts med sin
288
- entydigt närmaste källmening, allt annat lämnas orört; utan citatbrott är
289
- scriptet en no-op). Exit 0: kör kontraktskontrollen igen och anteckna
290
- `quote_repairs: <antal RÄTTAT-rader>` i runs-raden.
291
- **VARJE RÄTTAT-rad (före → efter) visas dessutom ordagrant för användaren i
292
- BLUF-svaret**, på samma sätt som en `AVVIKELSE:`-rad alltid visas. En
293
- räknare går inte att granska: rättningen byter belägg i en bedömning som
294
- redan är gjord, och den som läser svaret ska kunna se exakt vilket citat som
295
- byttes mot vilken källmening. Aldrig bara antalet. Kvarstår BROTT efter
296
- rättningen, eller gav rättningen exit 1 (orättbara citat): dispatcha om just
297
- den agenten EN gång enligt omdispatch-regeln;
298
- kvarstår brott, visa dem och stanna utan att arkivera det svaret som giltigt
299
- varv — men släng det inte: arkivera det som `<typ>-pricing-vN-avvisad.json`
300
- (utanför N-räkningen) med kontrollens felrader i en runs-rad, så även
301
- misslyckade körningar har kvitto. En `AVVIKELSE:`-rad visas alltid för
302
- användaren.
303
- 6. **Dubbelkörning (driftvakt).** Körs BARA när inget av svaren är ett
304
- stopputfall — stopproutningen i steg 7 går alltid först, en post som ändå
305
- går till människa ska inte kosta fyra extra körningar. Trigger: klassningens
306
- **utfall** är MEDIUM, LARGE eller OFFERT, ELLER poängen är ≥ 4 (poängen
307
- multipliceras direkt till pris — en hög poäng på låg klass förtjänar samma
308
- vakt). Dispatcha då `request-classifier` OCH `request-scorer` EN gång till,
309
- samma prompter, nya isolerade körningar, parallellt i samma meddelande.
310
- Kostnaden (ett extra par körningar) läggs medvetet exakt där pengarna står
311
- på spel — poängen multipliceras direkt till pris och båda skillsens eget
358
+ aldrig till kundfält), stämpla med
359
+ `node <skillkatalog>/scripts/archive.mjs add-run <katalog> --file impact.md
360
+ --skill request-assess-impact --agent request-assessor
361
+ --model <ur MODELL-svansraden>` (scriptet sätter `underlag_sha256` mot
362
+ `spec.md`). Kör precheck igen med `--impact`. Fäller precheck det nya
363
+ utkastet → omdispatcha assessorn EN gång enligt omdispatch-regeln, och ge
364
+ det förkastade utkastet kvitto precis som i speca steg 6: arkivera det som
365
+ `impact-avvisad-<datum>.md` och stämpla en egen runs-rad med kontrollens
366
+ felrader i `--note` (ADR-0099).
367
+ 4. **Prissättning.** Dispatcha `request-scorer` enligt promptmallen.
368
+ Ingen prislista, inget repo, inga andra posters dokument i prompten.
369
+ 5. **Ankarlösning och kontraktskontroll.** Strippa svansraderna (`MODELL:`,
370
+ `LÄSTA:`, ev. `AVVIKELSE:`) ur svaret. Kör
371
+ `node <skillkatalog>/scripts/check-lasta.mjs "<LÄSTA-radens innehåll>" <spec.md> <impact.md>`
372
+ (exit 1 = kontraktsbrott, se dispatch-kontraktet). Skriv sedan den rena
373
+ JSON:en till fil och kör i ordning:
374
+ `node <skillkatalog>/scripts/resolve-anchors.mjs <svar.json> <spec.md> <impact.md>`
375
+ (löser beläggsankare till ordagranna källblock; OLÖST/FLERTYDIG är ett
376
+ kontraktsbrott), sedan
377
+ `node <skillkatalog>/scripts/contract-check.mjs <svar.json> <spec.md> <impact.md> --expect-id <katalogens id>`.
378
+ BROTT från något av scripten → dispatcha om scorern EN gång enligt
379
+ omdispatch-regeln, med felraderna i prompten. Kvarstår brott: visa dem och
380
+ stanna utan att arkivera svaret som giltigt varv, men släng det inte:
381
+ arkivera som `score-pricing-vN-avvisad.json` (utanför N-räkningen) med
382
+ kontrollens felrader i en runs-rad, så även misslyckade körningar har
383
+ kvitto. En `AVVIKELSE:`-rad visas alltid för användaren.
384
+ 6. **Dubbelkörning (driftvakt).** Körs BARA när svaret inte är ett
385
+ stopputfall — stopproutningen nedan går alltid först, en post som ändå
386
+ går till människa ska inte kosta extra körningar. Trigger: poängen är ≥ 1
387
+ (ADR-0098: tröskeln täcker minst det den borttagna klassaxelns
388
+ MEDIUM/LARGE/OFFERT-villkor täckte; poängen multipliceras direkt till
389
+ pris). Dispatcha då `request-scorer` EN gång till,
390
+ samma prompt, ny isolerad körning.
391
+ Kostnaden (en extra körning) läggs medvetet exakt där pengarna står
392
+ på spel — poängen multipliceras direkt till pris och skillens eget
312
393
  acceptanstest är att två oberoende körningar ger samma resultat.
313
- - Kör kontraktskontrollen (steg 5) på båda nya svaren först, med egen
314
- omdispatch-budget (EN per agent).
394
+ - Kör hela steg 5 (ankarlösning, sedan kontraktskontroll) på det nya
395
+ svaret först, med egen omdispatch-budget (EN).
315
396
  - Jämför sedan deterministiskt:
316
- `node <skillkatalog>/scripts/compare-classifications.mjs <klass1.json> <klass2.json>`
317
397
  `node <skillkatalog>/scripts/compare-scores.mjs <poäng1.json> <poäng2.json>`
318
- - **Båda LIKA** → körning 1-paret gäller: `double_run: "bekräftad"` på dess
319
- runs-rader. Körning 2-filerna arkiveras som `...-pricing-vN-run2.json`
320
- med SAMMA N som paret och `superseded_by` satt till motsvarande körning
321
- 1-fil. `-run2`-filer räknas ALDRIG när nästa N bestäms, och guard och
398
+ - **LIKA** → körning 1 gäller: `double_run: "bekräftad"` på dess
399
+ runs-rad. Körning 2-filen arkiveras som `score-pricing-vN-run2.json`
400
+ med SAMMA N och `superseded_by` satt till körning 1-filen.
401
+ `-run2`-filer räknas ALDRIG när nästa N bestäms, och guard och
322
402
  `detaljer` läser alltid filen utan suffix.
323
- - **Någon OLIKA** → behandla posten som ESKALERA. Det beskriver routingen,
324
- aldrig artefakten: ingen fil ändras, ingen JSON skrivs om, inget
325
- ESKALERA-värde patchas in. Arkivera båda körningarna för båda axlarna
326
- (den som inte blir axelns huvudfil arkiveras som `-run2`) och stämpla
327
- runs-raderna PER AXEL, aldrig per
328
- varv. Det finns exakt en regel, och den går efter vad axelns eget
329
- jämförelsescript skrev:
330
- - **Den OENIGA axeln** (den vars script skrev OLIKA) får
331
- `double_run: "olika"` och `superseded_by: null` på bägge sina
332
- runs-rader. Körning 1 är huvudfil utan suffix och körning 2 arkiveras
333
- som `-run2`, men ingen av dem är utslagen: oenigheten är
334
- oavgjord tills en människa eller ett riktat varv avgör den, och
335
- `superseded_by` förblir därför `null` på båda.
336
- - **Den ENIGA axeln** behåller sitt vanliga utfall och sitt vanliga
337
- `superseded_by`: LIKA ger `double_run: "bekräftad"` med körning 1 som
338
- suffixfri huvudfil, LIKA_TOLERANS ger `double_run: "tolerans"` med
339
- verdiktradens utpekade körning (högsta poängen) som suffixfri
340
- huvudfil och den andra som `-run2` med `superseded_by` mot den, precis
341
- enligt LIKA_TOLERANS-punkten nedan, oavsett vilken av dem som var
342
- körning 1. Regeln "högsta poängen gäller" är densamma oavsett vilken
343
- punkt som routar posten.
344
-
345
- Arkivet namnger därmed vilken axel som var oenig: `"olika"` står bara på
346
- den axelns rader. Jämförelsescriptens verdikter lever bara i stdout och
347
- försvinner med sessionen, så utan den stämplingen kunde ett senare varv
348
- köra om fel axel och släppa igenom en oavgjord klassflip till pris.
349
- Var BÅDA axlarna OLIKA bär bägge axlarnas rader `"olika"`.
350
- Status blir `assessed`. Visa diffen (rad · körning 1 · körning 2)
351
- för granskaren. Inget `customer`-block ur någon av körningarna får gå
352
- till request-sync medan det GÄLLANDE paret har `double_run: "olika"`
353
- olyft (samma scope som `priced`-spärren, se state.json-reglerna ovan):
354
- har ett senare riktat varv landat LIKA eller LIKA_TOLERANS är spärren
355
- lyft för hela paret, även om den oeniga axelns äldre rader fortfarande
356
- bär `"olika"`. Läs upplåsningen på den omkörda axelns senaste rad,
357
- aldrig på en äldre. Välj ALDRIG en av körningarna själv och räkna
358
- ALDRIG fram ett medelvärde.
359
- **Återställningen är ett riktat nytt varv:** spärren (`double_run:
360
- "olika"`) sitter på den oeniga axelns rader, och ett nytt varv med LIKA
361
- (eller LIKA_TOLERANS) låser upp hela paret. VILKEN axel som körs om är
362
- ingen bedömning: det är den axel vars runs-rader bär `"olika"`.
363
- **Den omkörda axeln dubbelkörs ALLTID i ett riktat varv**, oavsett
364
- triggervillkoren i steg 6: spärren kan bara lyftas av ett
365
- jämförelseutslag, och en ensam körning ger inget att jämföra mot. En
366
- klassomkörning som landar SMALL med stående poäng under 4 skulle annars
367
- aldrig få något utslag och raderna förbli låsta för alltid. Hur MÅNGA
368
- axlar som körs om beror på vilket slags utslag granskaren gav:
369
- - **Viktningsutslag** (granskaren konstaterar att en körning hade rätt,
370
- utan att någon sakuppgift ändras): appendas som en ny rad i
371
- `state.json.granskarutslag` (se ovan), skriver ALDRIG över en
372
- tidigare rad, och går ALDRIG in i `impact.md`. Se
373
- Granskarutslag-punkten nedan för varför en fri text i underlaget vore
374
- fel. Underlaget är därmed oförändrat, så kör ENDAST den axel om
375
- (klass eller poäng) som var OLIKA, och bara den omkörda axelns agent
376
- dispatchas, aldrig båda. Triggerns villkor läses fortfarande mot den
377
- omkörda axelns nya svar plus den stående axelns gällande fil, aldrig
378
- mot ett nytt körningspar, men det är inte triggern som avgör om axeln
379
- dubbelkörs: i ett riktat varv dubbelkörs den alltid. Var BÅDA axlarna
380
- OLIKA finns ingen enig axel att hålla stilla: kör då BÅDA om ändå,
381
- `partial_rerun` förblir `false` på bägge nya rader. Vid enaxlig
382
- omkörning paras den stående axelns gällande fil med det nya resultatet
383
- (se steg 8) utan att skrivas om. Landar det riktade varvet
384
- LIKA/LIKA_TOLERANS lyfts spärren för hela paret, antecknat på den
385
- omkörda axelns nya runs-rad (`partial_rerun: true`, `pairs_with` mot
386
- den stående filens namn). Pekaren bor på den nya raden, den stående
387
- axelns rad skrivs aldrig om, så guard och `detaljer` läser
388
- upplåsningen via `pairs_with` på den nya raden, aldrig tvärtom.
389
- - **Sakändringsutslag**, eller ett beslut som av annat skäl måste stå i
390
- underlaget (se Granskarutslag-punkten nedan): appendas i `impact.md`,
391
- och vid en faktisk sakändring körs assessorn om (ny `impact.md`).
392
- Underlaget har då ändrats, en körning mot gammalt underlag får aldrig
393
- paras med en mot nytt, `underlag_sha256` skulle skilja och
394
- proveniens-kontrollen finns just för den skevheten, så kör BÅDA
395
- axlarna om, `partial_rerun` `false` på båda nya rader.
396
-
397
- - **Granskarutslag skrivs som citat, aldrig som prosa.** När granskarens
398
- beslut ska in i underlaget, alltså när det ändrar eller preciserar en
399
- sakuppgift i analysen, eller av annat skäl måste stå i underlaget för
400
- spårbarhet: appendas det sist i `impact.md` som ett avgränsat block
401
- enligt mallen nedan, beslutet ordagrant citerat, aldrig omformulerat av
402
- dispatchern och aldrig invävt i analysens löptext. En fritt formulerad
403
- utslagsmening läses av nästa körning som sakuppgift och styr om
404
- klassningen, det var precis felet i pilotkörningarna, därför alltid
405
- mallen, aldrig fri prosa. Kräver beslutet en faktisk sakändring i
406
- analysen: kör om assessorn (ny `impact.md`). Underlaget har i båda
407
- fallen ändrats, så `bedöm` kör om BÅDA axlarna (se riktade-varv-stycket
408
- ovan). Ett rent viktningsutslag, där granskaren bara konstaterar att en
409
- körning hade rätt utan att röra någon sakuppgift, hör ALDRIG hemma här:
410
- det appendas i stället som en rad i `state.json.granskarutslag`, se
411
- samma stycke, just för att underlaget ska förbli oförändrat och den
412
- riktade omkörningen giltig. Blocket är internt underlag med samma status som
413
- Till granskaren-blocket och följer aldrig med i kundtext.
414
-
415
- ```
416
- ## Granskarutslag (varv <N>, <YYYY-MM-DD>)
417
-
418
- > <granskarens beslut, ordagrant>
419
-
420
- Utslaget gäller endast viktningen mellan körningarna och ändrar inga
421
- sakuppgifter i analysen ovan.
422
- ```
423
-
424
- - **Klassjämförelsen LIKA men poängjämförelsen skriver LIKA_TOLERANS**
425
- (poängskillnad ≤ 0,5 med samma outcome och konfidens; klassjämförelsen
426
- har ingen tolerans, en klassflip är alltid OLIKA och routar posten som
427
- ESKALERA oavsett poängens tolerans, se föregående punkt) → paret räknas
428
- som bekräftat. Bytet gäller ENDAST poängaxelns filer: klassaxeln följer
429
- LIKA-regeln som vanligt, klass-körning 1-filen blir huvudfil utan suffix
430
- och klass-körning 2 arkiveras som `-run2` med `superseded_by`. För
431
- poängaxeln blir verdiktradens utpekade körning (högsta poängen)
432
- huvudfilen `score-pricing-vN.json`, och den andra arkiveras som `-run2`
433
- med `superseded_by` satt mot huvudfilen, oavsett vilken av dem som var
434
- körning 1. N är fortfarande gemensamt för de axlar som kördes i varvet
435
- (här: klass och poäng, båda).
436
- Huvudfilerna kan alltså komma från olika körningar, till exempel klass ur
437
- körning 1 och poäng ur körning 2. Det är avsiktligt: båda körningarna har
438
- passerat kontraktskontrollen och skillnaden ligger inom toleransen. Sätt
439
- `double_run: "tolerans"` på poängaxelns bägge runs-rader; klassaxelns
440
- runs-rader får `double_run: "bekräftad"` som vanligt. Toleransen är en
441
- medveten prisregel: högsta värdet skyddar marginalen, och ett medelvärde
442
- räknas fortfarande aldrig fram. Visa toleransraderna i BLUF-svaret.
443
- - Exit 2 från ett jämförelsescript är ett dispatcherfel (ojämförbara
403
+ - **OLIKA** → domen är människans, aldrig maskinens. Arkivera båda
404
+ körningarna (körning 1 utan suffix, körning 2 som `-run2`; på båda
405
+ raderna `double_run: "olika"` med `superseded_by: null`).
406
+ Status `assessed`. Ingen kundtext får gå till
407
+ request-sync medan spärren står (`archive.mjs sparr` är tolken).
408
+ Visa i BLUF-svaret beläggen bakom oenigheten, sida vid sida:
409
+ diffraderna (outcome, score, ankare, konfidens) har ingen
410
+ radkoppling till beläggen, `drivers[]` är en egen lista, så visa
411
+ samtliga `drivers[].citat`/`pastaende` ur båda körningarna
412
+ som stöd för hela utfallet. Det var beläggen som avgjorde domen i
413
+ varje uppmätt äkta oenighet. Hänvisa sedan till `döm <post>` och
414
+ STANNA: ingen omkörning, ingen eskalering, inget val av körning.
415
+ - **LIKA_TOLERANS** (poängskillnad ≤ 0,5 med samma outcome, oavsett
416
+ konfidens, en konfidensskillnad ensam fäller aldrig ett par inom
417
+ toleransen) → paret räknas som bekräftat. Verdiktradens utpekade
418
+ körning (högsta poängen) blir huvudfilen `score-pricing-vN.json`,
419
+ och den andra arkiveras som `-run2` med `superseded_by` satt mot
420
+ huvudfilen, oavsett vilken av dem som var körning 1. Det är
421
+ avsiktligt: båda körningarna har passerat kontraktskontrollen och
422
+ skillnaden ligger inom toleransen. Sätt `double_run: "tolerans"` på
423
+ bägge runs-raderna. Toleransen är en medveten prisregel: högsta
424
+ värdet skyddar marginalen, och ett medelvärde räknas fortfarande
425
+ aldrig fram. Visa toleransraderna i BLUF-svaret.
426
+ - Exit 2 från jämförelsescriptet är ett dispatcherfel (ojämförbara
444
427
  körningar) — visa felet och stanna, rapportera aldrig LIKA.
445
- 7. **Oenighetsgrind klass ↔ poäng.** Deterministiska regler, i ordning:
446
- - Endera svaret är ett stopputfall → hela posten routas enligt stopplistan
447
- nedan. Det andra svaret arkiveras men markeras `superseded_by` mot
448
- stopputfallets fil, och statusen blir `assessed`, aldrig `priced`.
449
- - Båda levererar (klass + poäng): flagga oenighet i BLUF-tabellen mot
450
- ankarbanden — klass TINY/SMALL med poäng ≥ 4, klass MEDIUM med poäng
451
- ≤ 0.75 eller ≥ 13, klass LARGE/OFFERT med poäng ≤ 3. Flaggan är
452
- information till granskaren — ingen automatisk åtgärd, ingen omkörning.
453
- 8. **Arkivera** godkända JSON-svar. Paret arkiveras atomärt: båda eller inget
454
- ("båda eller inget" gäller de axlar som kördes i varvet: vid riktad
455
- omkörning arkiveras den omkörda axelns fil ensam, och dess runs-rad får
456
- `partial_rerun: true` samt `pairs_with: "<den stående filens namn>"`.
457
- Pekaren bor på den nya raden, aldrig på den stående axelns äldre rad, som
458
- aldrig skrivs om, så guard och `detaljer` hittar paret genom att läsa den
459
- nya raden),
460
- och `N` i `-pricing-vN.json` är gemensamt för de axlar som kördes i
461
- varvet (nästa N = högsta befintliga för någon av filtyperna + 1, räknat
462
- bland de filtyper som kördes; vid riktad omkörning bumpar alltså bara den
463
- omkörda filtypens N; `-run2`- och `-avvisad`-filer räknas aldrig;
464
- guard-filer har en EGEN N-sekvens, skild från pricing). Uppdatera
465
- `state.json.runs` med modell, effort, referens-hash och `underlag_sha256`
466
- (hash för `spec.md` och `impact.md` som körningen såg) per fil, status →
467
- `priced` (eller `assessed` vid stopputfall).
468
- 9. **Svara BLUF:** tabell `post · klass · poäng · utfall · konfidens`, en rad
469
- per avvikelse värd att se (oenighetsflagga, ESKALERA, DELA_UPP,
470
- INPUT_SAKNAS, AVVIKELSE-rader, samt varje RÄTTAT-rad från citaträttningen
471
- enligt steg 5). JSON:en visas inte i chatten —
428
+ 7. **Arkivera** godkända JSON-svar. Hämta varvets N med
429
+ `node <skillkatalog>/scripts/archive.mjs next-n <katalog> --typ score`,
430
+ skriv filerna (`score-pricing-vN.json`, körning 2
431
+ som `-run2`, avvisade som `-avvisad`), och stämpla VARJE körning med
432
+ `archive.mjs add-run` (flaggor för fil, skill, agent, modell ur
433
+ MODELL-svansraden, effort ur agentdefinitionen, referens-hash via `--ref`
434
+ för skillens referensfil (se runs-kontraktet), double-run-utfall,
435
+ superseded-by, partial-rerun och pairs-with där de gäller). Exempel för
436
+ huvudfilen vid ett bekräftat par:
437
+ `node <skillkatalog>/scripts/archive.mjs add-run <katalog>
438
+ --file score-pricing-v1.json --skill request-score
439
+ --agent request-scorer --model <ur MODELL-svansraden>
440
+ --effort <ur agentdefinitionen> --ref poangskala.md=<hash via
441
+ ref-hash.mjs på request-score/references/poangskala.md>
442
+ --double-run bekräftad`. `--double-run` tar ENDAST de tre
443
+ literalerna `bekräftad`, `olika` eller `tolerans` (scriptet vägrar allt
444
+ annat, flaggnamnet med bindestreck, aldrig `--double_run` med understreck
445
+ som i JSON-fältet). Utelämna flaggan helt när ingen dubbelkörning gjorts.
446
+ `--superseded-by <fil>` och `--partial-rerun` (utan värde) läggs till där
447
+ de gäller, se runs-kontraktet. Stämplingsreglerna
448
+ bor i scriptet och dess tester, inte här. Sätt status via
449
+ `archive.mjs set-status <katalog> priced` (scriptet vägrar vid spärr,
450
+ stopputfall och experiment — se runs-kontraktet)
451
+ eller `assessed`.
452
+ 8. **Svara BLUF:** tabell `post · poäng · utfall · konfidens`, en rad
453
+ per avvikelse värd att se (ESKALERA, DELA_UPP,
454
+ INPUT_SAKNAS, AVVIKELSE-rader). JSON:en visas inte i chatten —
472
455
  `detaljer <post>` gör det. Skriv-tillbaka till brädan sker via request-sync
473
456
  i huvudkonversationen, bara på uttrycklig begäran — och det är den flytten
474
457
  som tar posten ✅ Spec ok → ⚖️ Poängsättning redo för granskning enligt
475
458
  syncens regler.
476
459
 
477
- **Stopputfall routas, aldrig tolkas:**
460
+ **Stopputfall routas, aldrig tolkas.** Ett stopputfall arkiveras som varvets
461
+ fil (steg 7), statusen blir `assessed`, aldrig `priced`, och posten routas:
478
462
 
479
463
  - `DELA_UPP` → föreslå ny analysrunda per del; delarna blir egna kataloger med
480
464
  `split` satt (`part`/`of`), och en del utan tak blir `OVER_SKALAN`, aldrig
@@ -524,104 +508,431 @@ missta:
524
508
  - Verben rör Offer status och kommentarer — aldrig Score, Class eller
525
509
  beskrivningsfält.
526
510
 
511
+ ## Uppgift: `döm`
512
+
513
+ Människans dom över en post vars dubbelkörning skrev OLIKA. Adressering som
514
+ `godkänn` (radnummer eller gid, aldrig fuzzy). Kräver att spärren står
515
+ (`archive.mjs sparr` exit 1), annars: visa status och stanna.
516
+
517
+ **En variantkatalog (från `utvärdera`) adresseras med sin sökväg direkt,
518
+ aldrig med radnummer eller gid.** Experimentets slug är samma sträng som ett
519
+ vanligt gid skulle vara, så gid-adressering skulle resolva till postkatalogen
520
+ på toppnivå, inte till variantkatalogen under `experiments/<slug>/`.
521
+ `archive.mjs` tar ändå en katalog som argument oavsett var den ligger, så
522
+ sökvägen fungerar identiskt.
523
+
524
+ **En dom på en katalog under `experiments/` skriver aldrig om
525
+ `experiment.json` i sig.** `archive.mjs utslag` och `archive.mjs sparr`
526
+ verkar bara på variantkatalogens egen `state.json`. Statusen i
527
+ `experiment.json` uppdateras inte av `döm` — den uppdateras av `utvärdera`,
528
+ körd igen för samma post, vars återupptagbarhetsregel omvärderar en
529
+ `"väntar på dom"`-variant och gör utfallet synligt för rapporten när den körs
530
+ (se `utvärdera`).
531
+
532
+ 1. **Visa underlaget.** Kör jämförelsescriptet på det gällande paret och visa
533
+ varje diffrad med beläggen sida vid sida (som i bedöm steg 6). Ingen
534
+ sammanfattning i stället för beläggen: domen fattas på dem.
535
+ 2. **Fråga granskaren** (AskUserQuestion) med tre vägar.
536
+ - **"Körning N gäller"** (N är 1 eller 2, vilken av de två uppmätta
537
+ körningarna som hade rätt): skriv domen via `archive.mjs utslag <katalog> --typ viktning --beslut "<ordagrant>" --beslutad-av <namn> --axel score --n <n> --galler-fil <fil>`.
538
+ (`--axel classification` finns kvar enbart för domar i äldre arkiv
539
+ med en klassaxel, före ADR-0098.)
540
+ `--n` är INTE körningsnumret i rubriken ovan: det är det AKTUELLA
541
+ varvet, avläst ur den senaste pricing-filen
542
+ `score-pricing-v<n>.json` (aldrig `next-n`s utdata, som pekar på
543
+ NÄSTA, ännu okörda varv). `--galler-fil` är den arkiverade fil
544
+ granskaren dömer till
545
+ förmån för: körning 1 → `score-pricing-v<n>.json` utan suffix,
546
+ körning 2 → `score-pricing-v<n>-run2.json`. Efter en beställd
547
+ omkörning (nästa väg) finns TRE arkiverade filer, och
548
+ `--galler-fil` kan peka på VILKEN som helst av dem: omkörningens egen
549
+ fil (version = gällande `n`), eller endera av de två ursprungliga
550
+ körningarnas filer från ett LÄGRE varv, om granskaren dömer att en av
551
+ dem hade rätt hela tiden. Scriptet vägrar bara ett HÖGRE varv än `--n`
552
+ (en dom kan aldrig gälla ett varv som inte fanns när den skrevs) och en
553
+ fil som inte faktiskt arkiverades via `add-run`. Kör `archive.mjs sparr`
554
+ efteråt: exit 0 betyder att spärren är lyft, exit 1 betyder att
555
+ något fortfarande väntar (utskriften namnger vad).
556
+ Inga filer döps om eller skrivs om.
557
+ - **"Beställ omkörning":** EN ny körning, ingen tvångsdubbling: dispatcha
558
+ scorern (ordinarie promptmall och kontraktskedja), arkivera med
559
+ `partial_rerun` och `pairs_with` mot den stående filen. Den nya
560
+ raden väntar precis som originalraden på en dom (`archive.mjs sparr`
561
+ spärrar den ensam, oavsett vad den råkar bära i `double_run`): visa
562
+ scriptdiffen mot BÅDA de stående körningarna och gå till steg 2 igen,
563
+ det är fortfarande granskaren som dömer. Vill granskaren ha två nya
564
+ mätningar beställs två.
565
+ - **"Underlaget är fel":** granskarens beslut appendas i `impact.md` som
566
+ citatblock enligt mallen nedan, assessorn körs om (ny `impact.md`),
567
+ därefter kör `bedöm` ett nytt fullt varv. En körning mot
568
+ gammalt underlag paras aldrig med en mot nytt.
569
+ 3. **Kalibreringsnotis.** Är oenigheten äkta (en fråga eller ett ankarval som
570
+ två giltiga körningar läste olika, inte en etikett): skriv `archive.mjs utslag <katalog> --typ kalibrering --beslutad-av <namn> --fraga <id> --lasning1 "<körning 1:s läsning, en mening>" --lasning2 "<körning 2:s läsning>"`.
571
+ Notisen är råmaterial till nästa instruktionsversion (mekanismen bakom
572
+ v0.2 till v0.6) och läses aldrig in i någon prompt. `archive.mjs kalibrering <arkivrot>`
573
+ listar alla.
574
+
575
+ **Granskarutslag skrivs som citat, aldrig som prosa.** När granskarens
576
+ beslut ska in i underlaget, alltså när det ändrar eller preciserar en
577
+ sakuppgift i analysen, eller av annat skäl måste stå i underlaget för
578
+ spårbarhet: appendas det sist i `impact.md` som ett avgränsat block enligt
579
+ mallen nedan, beslutet ordagrant citerat, aldrig omformulerat av
580
+ dispatchern och aldrig invävt i analysens löptext. En fritt formulerad
581
+ utslagsmening läses av nästa körning som sakuppgift och styr om
582
+ bedömningen, det var precis felet i pilotkörningarna, därför alltid mallen,
583
+ aldrig fri prosa. Kräver beslutet en faktisk sakändring i analysen: kör om
584
+ assessorn (ny `impact.md`). Underlaget har i båda fallen ändrats, så
585
+ `bedöm` kör ett nytt fullt varv (se steg 2 ovan, vägen "Underlaget är fel").
586
+ Skriv samtidigt kvittot `archive.mjs utslag <katalog> --typ sakandring --beslut "<ordagrant>" --beslutad-av <namn>`
587
+ i `state.json` (se `granskarutslag` ovan): citatet i `impact.md` är
588
+ underlaget, sakandring-raden är bara revisionsspåret. Ett rent
589
+ viktningsutslag, där granskaren bara konstaterar att en körning hade rätt
590
+ utan att röra någon sakuppgift, hör ALDRIG hemma här: det skrivs i stället
591
+ via `archive.mjs utslag --typ viktning` (se steg 2 ovan, vägen "Körning N
592
+ gäller"), just för att underlaget ska förbli oförändrat. Blocket är internt
593
+ underlag med samma status som Till granskaren-blocket och följer aldrig
594
+ med i kundtext.
595
+
596
+ ```
597
+ ## Granskarutslag (varv <N>, <YYYY-MM-DD>)
598
+
599
+ > <granskarens beslut, ordagrant>
600
+
601
+ Utslaget gäller endast viktningen mellan körningarna och ändrar inga
602
+ sakuppgifter i analysen ovan.
603
+ ```
604
+
527
605
  ## Uppgift: `detaljer`
528
606
 
529
607
  `detaljer <gid|slug|nr> [steg]` — skriv ut arkiverade filer för posten: utan
530
608
  steg en lista över vad som finns, med steg (`spec`, `granskarblock`, `analys`,
531
- `klass`, `poäng`, `state`) filens innehåll rått. För `klass`/`poäng`: senaste
609
+ `poäng`, `state`) filens innehåll rått; `klass` läser äldre arkivs
610
+ classification-filer (före ADR-0098). För `klass`/`poäng`: senaste
532
611
  `-pricing-vN.json` utan suffix (aldrig `-run2`/`-avvisad`).
533
612
 
534
613
  ## Uppgift: `utvärdera`
535
614
 
536
- A/B-utvärdering av modellvalet: kör en post genom hela flödet två varv —
537
- `baseline` med agentdefaults och ett varv med alla agenter tvingade till en
538
- kandidatmodell (default `opus`) — och arkivera all output sida vid sida under
539
- experimentytan. Uppgiften samlar underlag; människan analyserar. Ingen dom
540
- fälls över vilken modell som är bäst.
615
+ Modellutvärdering: `utvärdera <post…> <modell…>`, med minst en modell. Kör en
616
+ post genom hela flödet en gång per namngiven modell, varje varv med samtliga
617
+ tre agenter (specifiern, assessorn, scorern) tvingade till varvets
618
+ modell, så varje varv mäter en faktisk modellskillnad i stället för brus mellan
619
+ en fast produktionsmix och en enda experimentmodell.
620
+ `default` som modellvärde ger agentdefaults (dagens produktionsblandning) och
621
+ kör utan override; katalognamnet blir då `baseline/`, för alla andra värden
622
+ `<modell>/`. Arkivera all output sida vid sida under experimentytan.
623
+ Uppgiften samlar underlag; människan analyserar. Ingen dom fälls över vilken
624
+ modell som är bäst, och ingen dom simuleras: en OLIKA i något varvs
625
+ dubbelkörning väntar på `döm`, precis som i produktion.
626
+
627
+ **Underkommandon resolvas först.** Är det enda argumentet `sammanställ` eller
628
+ `rapport`, är det underkommandot `utvärdera sammanställ` respektive
629
+ `utvärdera rapport` (se avsnitten nedan). Argumentreglerna nedan gäller aldrig
630
+ de orden; de klassificeras varken som modell eller post-id.
631
+
632
+ **Flaggorna plockas bort först.** `--om`, `--hoppa-över` och `--körning
633
+ <katalog>` (flaggan plus dess värde) lyfts ur argumentlistan INNAN skanningen
634
+ nedan börjar, och räknas varken som modell eller post-id. Utan det steget
635
+ stoppar svepets egen rad, som slutar med `--körning <katalog>`, skanningen
636
+ omedelbart, noll modeller hittas, och kommandot faller på "Minst en modell
637
+ krävs" trots att modellen står där.
638
+
639
+ **Argumenttolkning.** Kvarvarande avslutande argument avgörs höger till vänster
640
+ för att hitta GRÄNSEN mot post-id-listan: ett argument räknas som modell bara
641
+ om det är ett känt agentmodellnamn (`fable`, `opus`, `sonnet`, `haiku`, …) eller
642
+ literalen `default`, allt annat läses som post-id, och skanningen stannar vid
643
+ det första argumentet som inte är ett modellnamn. Ett argument som varken är
644
+ ett känt modellnamn eller ett giltigt gid/slug är ett FEL, aldrig en gissning:
645
+ visa felet och stanna i stället för att chansa på vilken sida av gränsen det
646
+ hör hemma. De modellargument skanningen hittar bildar sedan modellistan i
647
+ DEKLARATIONSORDNING, vänster till höger.
648
+
649
+ **Minst en modell krävs.** Hittar skanningen ingen modell alls är det ett FEL,
650
+ aldrig en default. Ett kommando som kostar timmar och pengar ska säga vilka
651
+ modeller det kör; en tyst standarduppsättning gör att en körning kan starta med
652
+ en annan modell än den som avsågs, och den kostnaden syns först efteråt.
653
+
654
+ **Dubbletter vägras.** Står samma modellvärde två gånger i listan, vägra
655
+ körningen med ett felmeddelande som förklarar varför: två lika värden pekar på
656
+ samma katalognamn och mäter ingen skillnad mot varandra. Vill du köra samma
657
+ modell en gång till, gör det i en egen invokation, då blir det en ny körning
658
+ under samma modell enligt regeln i Experimentytan.
659
+
660
+ **En namngiven modell rör bara sin egen katalog.** Övriga modellers kataloger
661
+ läses inte och skrivs inte. Det är det som gör att en jämförelse kan byggas i
662
+ etapper: `utvärdera <gid> opus sonnet` idag och `utvärdera <gid> fable` en
663
+ vecka senare ger tre modeller under samma post, utan att de första två körs om.
664
+
665
+ **Omkörning frågas fram, den antas aldrig.** Namnger invokationen en modell som
666
+ redan har körningar under sig, stanna INNAN något dyrt startas och fråga om de
667
+ ska köras om. Ställ EN fråga som täcker alla kolliderande modeller, inte en per
668
+ modell. Svaret kan ges i förväg med `--om` (kör om) eller `--hoppa-över` (rör
669
+ dem inte); bär invokationen någon av dem ställs ingen fråga.
670
+
671
+ En omkörning skriver aldrig över den gamla körningen, den blir `k<N+1>`.
672
+ Frågan gäller alltså kostnaden, inte risken att förlora data.
541
673
 
542
674
  1. **Underlag.** Som `speca` steg 1: rått önskemål eller gid — vid gid hämtas
543
675
  posten läsande via request-sync i huvudkonversationen, uppgiftens ENDA
544
676
  Asana-kontakt. Skapa `experiments/<slug>/` och `experiment.json` med
545
- ticketreferens, datum och kandidatmodell.
677
+ ticketreferens, datum och `varianter` i deklarationsordning.
678
+
679
+ Anteckna samtidigt postens `permalink_url` i `experiment.json` när gid-svaret
680
+ bär det. Det kommer ur samma läsande hämtning som redan görs, så steget får
681
+ ingen ny Asana-kontakt. Saknas fältet i svaret skrivs det inte, och rapporten
682
+ visar då gid som ren text: Asanas permalänk bär ett workspace-gid som inte
683
+ finns på experimentytan, så en konstruerad URL vore en gissning.
684
+
685
+ **`experiment.json` skrivs läs-modifiera-skriv, aldrig från noll.** Saknas
686
+ filen skapas den. Finns den redan läses den in FÖRST och uppdateras sedan:
687
+ `varianter` slås ihop som UNION med bevarad deklarationsordning, de
688
+ befintliga namnen först och invokationens nya namn efter, och en befintlig
689
+ körnings `status` skrivs aldrig över av en process som inte äger just den
690
+ körningen. En headless-process med en enmodellslista rör alltså bara sin
691
+ egen modell. Utan regeln raderar `fable`-körningen en vecka senare `opus`
692
+ och `sonnet` ur filen, och eftersom det är den här filen
693
+ återupptagningsregeln läser (se Återupptagbart nedan) slutar ett återupptaget
694
+ svep känna igen färdiga körningar och betalar om för dem. Skrivningen ska
695
+ vara ATOMISK: skriv till en temporärfil i samma katalog och byt namn.
696
+ `experiment.json` är den enda filen på experimentytan som delas mellan
697
+ samtidiga processer utan den låsning `archive.mjs ny-korning` ger
698
+ körningskatalogerna, så samtidighetsdisciplinen måste stå här i stället.
699
+
546
700
  2. **Källmanifest EN gång.** Bygg manifestet som i `speca` steg 2 och
547
- återanvänd det byteidentiskt i båda varven. Anteckna manifest-hash i
701
+ återanvänd det byteidentiskt i ALLA varv. Anteckna manifest-hash i
548
702
  `experiment.json` (`ref-hash.mjs` på manifesttexten). Enda avsedda
549
703
  skillnaden mellan varven är modellen.
550
- 3. **Eskaleringsvalet görs en gång** enligt `speca`:s regler och gäller båda
551
- varven — även eskaleringsvalet är en del av det som hålls konstant.
552
- 4. **Varv `baseline`.** Kör speca-stegen (dispatch, kontraktskontroll,
553
- arkivering) och därefter bedöm-stegen (analys, klassning ∥ poäng,
554
- dubbelkörning, citaträttning, riktad omkörning) enligt ordinarie
555
- uppgifter, men mot variantkatalogen `baseline/` och utan
556
- godkännandegrind: `approval` förblir `null`, `state.json` får
557
- `"experiment": true`. Dispatcherna använder ordinarie promptmallar och
558
- agenternas defaults — ingen override. `REQUEST_ID` och `--expect-id`
559
- använder alltid ticketens gid/slug, aldrig variantkatalogens namn
560
- (`baseline`/modellnamnet).
561
- 5. **Varv `<kandidatmodell>`.** Exakt samma steg, prompter och underlag, men
562
- varje Agent-anrop får modell-override till kandidatmodellen.
563
- Agentdefinitionerna röres aldrig; effort tas ur definitionen som vanligt.
564
- Verifiera via MODELL-svansraden att overriden slog igenom och redovisa
565
- avvikelsen om den finns. Generellt för hela uppgiften: subagentdöd,
566
- kontraktsbrott (kvarstående BROTT efter citaträttning) eller
567
- MODELL-avvikelse — i vilket steg det än inträffar — sätter variantens
568
- `status` till `"ofärdig"` i `experiment.json`.
569
- 6. **DELA_UPP körs autonomt.** Ett DELA_UPP-utfall följer bedöms ordinarie
704
+ 3. **Eskaleringsvalet görs en gång** enligt `speca`:s regler och gäller alla
705
+ varv, även eskaleringsvalet är en del av det som hålls konstant.
706
+ Triggar valet: agenten blir `request-specifier-eskalerad` i stället för
707
+ `request-specifier`, likadant i alla varv. Modell-overriden i steg 4
708
+ gäller ändå den eskalerade agenten precis som de två andra: dess egen
709
+ pinnade modell, den som enligt agentdefinitionen ÄR eskaleringen, skrivs
710
+ över av varvets modell. Eskaleringsmekanismen kollapsar alltså medvetet
711
+ under override, det är avsett, inte en lucka: `utvärdera` mäter en ren
712
+ modellskillnad över alla tre rollerna och testar aldrig
713
+ eskaleringsbeslutet i sig.
714
+ 4. **Ett varv per modell i listan**, sekventiellt, i deklarationsordning.
715
+ Varje varv är EN egen headless-process, och det är det som ger mätningen
716
+ gratis. Ansvaret delas, eftersom en process aldrig kan känna sin egen
717
+ slutkostnad:
718
+
719
+ **Startaren** (huvudkonversationen, eller svepets loop) gör per modell:
720
+ allokerar körningskatalogen med `archive.mjs ny-korning`, startar
721
+ `claude -p --model sonnet --output-format json` med katalogen som explicit
722
+ argument, väntar in processen, och skriver JSON-svaret rått till
723
+ `<modell>/k<N>/korning.json`. Rapporten läser sedan `total_cost_usd`,
724
+ `usage.input_tokens`, `usage.output_tokens`,
725
+ `usage.cache_read_input_tokens`, `usage.cache_creation_input_tokens` och
726
+ `subagent_stats.spawned` ur den filen.
727
+
728
+ **Startaren tar tiden runt processen** och skriver `<modell>/k<N>/matning.json`
729
+ med `startad`, `avslutad` (båda ISO 8601) och `wall_ms` (differensen).
730
+ Filen skrivs ÄVEN när processen faller: en kraschad körning har en klocktid,
731
+ och den är ofta det intressantaste talet i tabellen. Rapporten läser
732
+ väggtiden därifrån, aldrig ur `korning.json`. Skälet är detsamma som för
733
+ kostnaden: en process kan inte känna sin egen klocktid när arbetet ligger i
734
+ bakgrundssubagenter, och svarets `duration_ms` räknar inte in väntan på dem.
735
+ `korning.json` förblir headless-svaret rått, därav den egna filen.
736
+
737
+ Startaren stämplar samtidigt körningens `state.json` med `proveniens`:
738
+
739
+ ```
740
+ node <skillkatalog>/scripts/archive.mjs proveniens <modell>/k<N> \
741
+ --modell <varvets modellvärde> --konto <vilket Claude-konto som körde> \
742
+ --underlag <sökväg till källmanifestet>
743
+ ```
744
+
745
+ Kommandot räknar själv `skill_sha256` (över `SKILL.md` plus de fyra
746
+ agentdefinitionerna) och `underlag_sha256`, läser `korning.json` och
747
+ antecknar `fakturerade_modeller` (den kanoniserade nyckelmängden ur
748
+ `modelUsage`, suffix som `[1m]` strippade — ADR-0099), och stämplar även varje
749
+ `del-N/`. Utan den stämpeln går det inte att avgöra
750
+ vad som skilde `k1` från `k2`, och en jämförelse över tid blir en
751
+ gissning. Anropet står här som ett
752
+ kommando och inte som en beskrivning med flit: ett steg som ålägger
753
+ startaren en skrivning utan att peka ut vem som utför den är en skrivning
754
+ som inte blir av, och den syns först som tomma celler långt senare.
755
+ Kör därför proveniens-stämpeln EFTER att `korning.json` skrivits, aldrig
756
+ före — annars saknar stämpeln faktureringsdatan.
757
+
758
+ **Modellverifiering görs av startaren mot faktureringen (ADR-0099),
759
+ aldrig mot svansraden ensam.** När processen är klar: jämför
760
+ `fakturerade_modeller` mot varvets begärda modell plus orkestratorns
761
+ (sonnet). En modellnyckel som varken är varvets modell eller
762
+ orkestratorns är en faktisk MODELL-avvikelse → körningen blir
763
+ `"ofärdig"`. En MODELL-svansrad som säger något annat än vad
764
+ `modelUsage` visar är en RAPPORTERINGSAVVIKELSE: den visas i rapporten
765
+ och står kvar i `runs[]` (raden bär agentens uppgift i
766
+ `model.uppgiven`), men den fäller ALDRIG ensam körningen — modeller är
767
+ opålitliga källor om sin egen identitet, och att kasta en korrekt
768
+ körning på en felskriven svansrad betalar om ett riktigt resultat.
769
+ Modellvärdet `default` kör agenternas egna defaults och får lagligt
770
+ producera flera modellnycklar: ingen mängdjämförelse görs där.
771
+
772
+ **Den inre processen** känns igen på invokationen, aldrig på gissning: bär
773
+ den `--körning`, är du den inre processen. Allokera då ingen katalog, starta
774
+ ingen ny process, och kör stegen direkt mot den anvisade katalogen. Utan det
775
+ stoppvillkoret läser den inre processen samma uppgift som startaren, startar
776
+ ett varv till och dubblar notan.
777
+
778
+ Den inre processen kör speca-stegen (dispatch, kontraktskontroll,
779
+ arkivering) och därefter bedöm-stegen (analys, poäng,
780
+ ankarlösning, dubbelkörning enligt bedöm) mot den anvisade katalogen, utan
781
+ godkännandegrind, och rör ingenting utanför den. Är modellen `default`
782
+ används ordinarie promptmallar och agenternas defaults, ingen override. Är
783
+ den ett modellnamn får varje Agent-anrop modell-override till den på ALLA
784
+ tre agenter; agentdefinitionerna röres aldrig, effort tas ur definitionen
785
+ som vanligt. Overriden verifieras av STARTAREN mot `modelUsage` (se
786
+ modellverifieringen ovan) — svansraden arkiveras men avgör aldrig ensam.
787
+ `approval` förblir `null`, `state.json`
788
+ får `"experiment": true`. `REQUEST_ID` och `--expect-id` använder alltid
789
+ ticketens gid/slug, aldrig katalogens namn.
790
+
791
+ Orkestratorn pinnas till sonnet i ALLA körningar. Dess kostnad blir då en
792
+ konstant som går att räkna bort, och `modelUsage` i JSON-svaret skiljer den
793
+ från agenternas kostnad utan extra arbete.
794
+
795
+ Subagentdöd, kontraktsbrott (kvarstående BROTT efter omdispatch) eller
796
+ en faktisk MODELL-avvikelse (obegärd nyckel i `modelUsage`, aldrig en
797
+ ensam svansradsavvikelse), i vilket steg det än inträffar, sätter
798
+ körningens `status` till `"ofärdig"` i `experiment.json`.
799
+
800
+ 5. **DELA_UPP körs autonomt.** Ett DELA_UPP-utfall följer bedöms ordinarie
570
801
  delningsregler men utan paus för granskare: delkataloger
571
- `<variant>/del-1/ … del-N/` med `split` satt, förälderns `spec.md` +
572
- `granskarblock.md` kopierade in, ny analys + klassning + poäng per del.
802
+ `<modell>/k<N>/del-1/ … del-N/` med `split` satt, förälderns `spec.md` +
803
+ `granskarblock.md` kopierade in, ny analys + poäng per del.
573
804
  En del utan tak blir `OVER_SKALAN` och delas aldrig vidare. Övriga
574
805
  stopputfall (`TILLBAKA_TILL_SPEC`, `KONCEPT`, `DIAGNOS`, `ESKALERA`,
575
806
  `INPUT_SAKNAS`) kräver människa eller kund: arkivera utfallet, avbryt det
576
- varvet och redovisa i jämförelsen — det andra varvet fortsätter oberoende.
577
- 7. **Jämförelse.** När båda varven är klara, skriv
578
- `experiments/<slug>/jamforelse.md`: delningsbeslutet per variant (delade
579
- sig / delade sig inte), tabell per post/delpost med klass, poäng,
580
- inringning, dubbelkörningsutfall, stopputfall och `quote_repairs` per
581
- variant, samt kort notis om specskillnader (rubrik, omfång, Ingår
582
- inte-avvikelser). Ingen värdering av vilken variant som är bättre —
583
- rådatan per katalog är analysunderlaget.
584
- 8. **Rapport.** BLUF i chatten: klart/ofärdigt per varv, stopputfall, och
585
- sökvägen till `experiments/<slug>/`. Ingen skrivning mot Asana.
807
+ varvet, gör det synligt i rapporten när den körs, och övriga körningar i
808
+ listan fortsätter oberoende.
809
+
810
+ **OLIKA väntar på dom.** Skriver dubbelkörningen OLIKA i ett varv:
811
+ arkivera enligt bedöms regler (spärren står), sätt körningens status till
812
+ `"väntar på dom"` i `experiment.json`, synligt i rapporten när den körs.
813
+ Domen är människans även i experimentet: `döm` körs mot körningskatalogen
814
+ när granskaren vill (adresserad via sin sökväg, se `döm`), och ingen dom
815
+ simuleras av uppgiften. Domen i sig skriver inte om `experiment.json`,
816
+ det gör `utvärdera` självt, nästa gång det körs för samma post (se
817
+ Återupptagbart i avsnittet Batch nedan för hur och när statusen går
818
+ till `"klar"` och rapporten kan visa utfallet). Övriga körningar i listan
819
+ fortsätter oberoende oavsett när, eller om, domen fälls.
820
+
821
+ 6. **Rapport.** BLUF i chatten: klart eller ofärdigt per körning, stopputfall,
822
+ och sökvägen till `experiments/<slug>/`. Nämn att `utvärdera rapport` finns
823
+ när underlaget ska läsas sida vid sida. Ingen skrivning mot Asana.
586
824
 
587
825
  ### Batch: flera poster
588
826
 
589
- `utvärdera <post1> <post2> … [kandidatmodell]` kör varje ticket i en EGEN
827
+ `utvärdera <post1> <post2> … <modell…>` kör varje ticket och modell i en EGEN
590
828
  headless-process, sekventiellt:
591
829
 
592
830
  ```
593
- claude -p "/request utvärdera <gid> <kandidatmodell>"
831
+ claude -p --model sonnet --output-format json "/request utvärdera <gid> <modell> --hoppa-över --körning <katalog>"
594
832
  ```
595
833
 
834
+ Orkestratorn är huvudloopens modell och pinnas till sonnet i headless:
835
+ agenternas modeller styrs av definition plus override och verifieras av
836
+ startaren mot `modelUsage` i `korning.json` (ADR-0099) —
837
+ aldrig av huvudloopens val, och aldrig av svansraden ensam.
838
+
839
+ **Raden bär ALLTID `--om` eller `--hoppa-över`, och väljer `--hoppa-över`.**
840
+ Det är inte en stilregel utan det som gör svepet möjligt: en `claude -p`-process
841
+ kan inte svara på en fråga, så en rad utan flagga skulle hänga eller gissa.
842
+ `--hoppa-över` är dessutom precis återupptagningens semantik, ett avbrutet svep
843
+ som startas om plockar upp där det stannade i stället för att betala om för
844
+ allt färdigt.
845
+
846
+ `--körning <katalog>` pekar ut den körningskatalog som `archive.mjs ny-korning`
847
+ redan allokerat åt den ticketen och modellen. Den inre processen arkiverar
848
+ bara dit, aldrig någon annanstans.
849
+
850
+ Svepets loop är startaren och har startarens hela ansvar per rad: den tar tiden
851
+ runt `claude -p`-anropet och skriver `matning.json`, och den kör
852
+ `archive.mjs proveniens` mot körningskatalogen när processen är klar.
853
+
596
854
  - **En ticket = ett färskt kontextfönster.** Allt tillstånd bor i arkivet på
597
855
  disk; huvudsessionen samlar bara exitstatus och en radssammanfattning per
598
856
  ticket. `/clear` mellan tickets behövs inte — processgränsen är
599
857
  motsvarigheten. Headless-körningen förutsätter att uppgiften kan köra utan
600
858
  interaktiva permission-frågor, vilket håller: den läser repot och skriver
601
- enbart under `.haus/requests/` — plus EN läsande Asana-hämtning per ticket
859
+ enbart under `.haus-workflow/requests/` — plus EN läsande Asana-hämtning per ticket
602
860
  (steg 1), så MCP-behörigheten för läsning måste täcka headless-körning.
603
861
  - **Kör vidare vid fel.** En ticket vars gid inte går att hämta eller vars
604
862
  process kraschar noteras i sammanfattningen; resten av listan fortsätter.
605
- - **Återupptagbart:** innan ett varv startas kontrolleras arkivet —
606
- skippet styrs av variantens `status` i `experiment.json`: `"klar"` eller
607
- `"stopp"` hoppas över med en notis, `"ofärdig"` körs om från varvets
608
- början. Varven är billiga relativt risken att lappa ihop halva körningar.
609
- Ett svep över många tickets kan därmed fortsätta i en senare session utan
610
- omkörningar.
863
+ - **Återupptagbart:** innan en körning startas kontrolleras arkivet, styrt av
864
+ körningens `status` i `experiment.json`. `"klar"` och `"stopp"` hoppas
865
+ över med en notis. `"ofärdig"` är avbruten data, inget resultat värt att
866
+ skydda: den utlöser aldrig omkörningsfrågan, utan körs om automatiskt som
867
+ en ny `k<N+1>` under samma modell. Den gamla ofärdiga körningen skrivs
868
+ ändå aldrig över, den står kvar som historik över vad som gick fel.
869
+ `"väntar på dom"` OMVÄRDERAS i stället för att hoppas över, det är den
870
+ enda platsen som är aktören för den statusen: kör `archive.mjs sparr` på
871
+ körningskatalogen. Exit 0 betyder att domen är fälld: sätt körningens
872
+ status till `"klar"` och fortsätt där den stannade (rapporten kan köras
873
+ separat när alla körningar för posten är klara). Exit 1 betyder att
874
+ den fortfarande väntar: notera det och gå vidare till nästa ticket.
875
+ `döm` fäller domen men rör aldrig `experiment.json` (se `döm`), det är
876
+ bara genom att `utvärdera` körs igen för samma post som statusen
877
+ uppdateras. Omkörningsfrågan (se `utvärdera` ovan) gäller alltså bara
878
+ `"klar"`-körningar: det är resultat värda att skydda mot ett fingerfel,
879
+ en ofärdig körning har inget att skydda. Körningar är billiga relativt
880
+ risken att lappa ihop halva resultat. Ett svep över många tickets kan
881
+ därmed fortsätta i en senare session utan onödiga omkörningar.
611
882
 
612
883
  ### Sammanställning
613
884
 
614
885
  `utvärdera sammanställ` läser alla `experiments/*/` och skriver
615
- `experiments/sammanstallning.md`: tabell per ticket med klass/poäng per
616
- variant, delnings- och stopputfall, och avvikelsemarkering där varianterna
617
- skiljer sig. Halvfärdiga kataloger märks `ofullständig`, aldrig tyst
886
+ `experiments/sammanstallning.md`: tabell per ticket med en rad per modell som
887
+ bär poäng, delnings- och stopputfall, plus avvikelsemarkering där
888
+ modellerna skiljer sig. Modellens rad tas ur dess GÄLLANDE körning, den högsta
889
+ `k<N>` vars status i `experiment.json` är `"klar"`. En modell utan klar körning
890
+ representeras av sin senaste `k<N>` och märks `ofullständig` tillsammans med
891
+ den körningens status. Halvfärdiga kataloger märks likadant, aldrig tyst
618
892
  utelämnade. Bara läsning — inga agentdispatcher, inga Asana-anrop.
619
893
 
620
- ## Mekaniska kontroller: `scripts/`
621
-
622
- Sju deterministiska script (fem kontroller + `lib.mjs` med de delade
623
- byggstenarna + `ref-hash.mjs` för stabil hashning) ersätter modellkörningar
624
- där kontrollen är ren form —
894
+ ### Rapport
895
+
896
+ `utvärdera rapport [--ut <fil>]` renderar en HTML-rapport över experimentytan
897
+ med `scripts/report.mjs`. Rapporten täcker ALLTID hela ytan; något postfilter
898
+ finns inte, menyn i rapporten är där läsaren väljer post. Utan `--ut` skrivs
899
+ HTML på stdout, annars till filen. Publicera den som artefakt när den ska
900
+ läsas.
901
+
902
+ **Rapporten skrivs ALDRIG automatiskt.** Ingen körning genererar den när den
903
+ blir klar. Den kostar nästan ingenting att regenerera, så det finns ingen
904
+ anledning att skriva den på spekulation, och en fil som ingen bett om är en fil
905
+ ingen litar på.
906
+
907
+ Rapporten är ren läsning: inga agentdispatcher, inga Asana-anrop, inga
908
+ skrivningar i arkivet utöver utdatafilen.
909
+
910
+ Vad den visar: en meny över poster med ticketens titel, dess katalogslug och
911
+ vilka modeller posten har, och per vald post en kolumn per körning. Varje delpost ur
912
+ ett DELA_UPP-utfall är en egen kolumn efter sin körning (`opus / k1 / del-1`),
913
+ eftersom det är där utfallen bor när en post delat sig. Sektionerna är fältvisa,
914
+ samma fält på samma rad tvärs kolumnerna: utfall, poängens inringning med
915
+ golv, tak, steg och drivers,
916
+ specens fyra fält, samt `impact.md` och `granskarblock.md` hopfällda per
917
+ körning. Äldre körningar med klassningsfiler (före ADR-0098) får dessutom
918
+ sin Klassning-sektion renderad som förut — kalibreringshistoriken kastas inte.
919
+ Sist står kostnaden (USD, tokens, väggtid, dispatcher) och proveniensen.
920
+ En delkolumn saknar egen mätning och säger `ingår i k<N>`, aldrig omätt.
921
+ Matrisen är ojämn med flit: en post kan ha två modeller och en annan tre,
922
+ eftersom en dyrare modell läggs till bara där de billigare inte räckte.
923
+
924
+ Vad den ALDRIG gör: rangordnar modeller, rekommenderar en nivå, räknar aggregat
925
+ tvärs poster med olika modelluppsättning, eller uttalar sig om vilket utfall
926
+ som var korrekt. Bedömningen av vad som är ett acceptabelt utfall är
927
+ människans, gjord på erfarenhet av systemet. Rapporten visar spridningen.
928
+
929
+ ## Mekaniska script: `scripts/`
930
+
931
+ Nio deterministiska script bor i katalogen: de fem kontrollerna nedan,
932
+ `archive.mjs` som äger arkivets kataloger och spärrar, `report.mjs` som
933
+ renderar modellrapporten (se `utvärdera rapport`), `lib.mjs` med de delade
934
+ byggstenarna och `ref-hash.mjs` för stabil hashning. Kontrollerna ersätter
935
+ modellkörningar där kontrollen är ren form,
625
936
  billigare, snabbare och utan omdömesglidning. Varje script bär sha256 för de
626
937
  referensfiler vars regler det speglar och VARNAR när en katalogbump ändrat dem
627
938
  (kontraktsdrift ska upptäckas av en check, inte av en kund):
@@ -630,39 +941,39 @@ referensfiler vars regler det speglar och VARNAR när en katalogbump ändrat dem
630
941
  dispatcher: rubriker, exakt ett specblock, 2–5 punkter, förbjuden vokabulär,
631
942
  granskarblocksrader, state.json-fält. Skiljer på BRISTER (exit 1, stoppar)
632
943
  och VARNING (heuristik — visas, stoppar inte).
944
+ - `scripts/resolve-anchors.mjs <run.json> <spec.md> [impact.md]` — löser
945
+ beläggsankare (`belagg_ankare`/`ankare`) till ordagranna källblock, körs
946
+ FÖRE contract-check. Exit 0 = allt löst (eller inga ankare i filen), 1 =
947
+ minst ett OLÖST/FLERTYDIG ankare (kontraktsbrott, mekanisk omdispatch),
948
+ 2 = användningsfel.
633
949
  - `scripts/contract-check.mjs <run.json> <spec.md> [impact.md] [--expect-id <id>]`
634
- — kontraktskontroll av classify/score-JSON EFTER körning: enums,
950
+ — kontraktskontroll av score-JSON EFTER körning: enums,
635
951
  konfidensintervall, poängupplösning, inringningens närvaro (även per
636
952
  alternativ; under-A1-fallet med floor null och tak A1 är giltigt), belägg/
637
953
  driver-citat som ordagranna substrängar av underlaget, protokollterm-
638
954
  svartlista i kundtextfält, sluten kuvertnyckellista, request_id mot katalogen.
639
- INPUT_SAKNAS kräver saknas-lista i stället för frågerader.
640
- - `scripts/compare-classifications.mjs <a.json> <b.json>` — deterministisk
641
- dubbelkörningsdiff: utfall, basklass, varje frågas svar och konfidensens sida
642
- om 0.8-gränsen. Skriver `LIKA` eller `OLIKA` + diffrader, exit 0/1; exit 2 =
643
- ojämförbara körningar (dispatcherfel, aldrig LIKA).
644
- - `scripts/compare-scores.mjs <a.json> <b.json>` — samma för poäng: outcome,
955
+ INPUT_SAKNAS kräver saknas-lista i stället för frågerader. Ett olöst ankare
956
+ (belägg saknas trots `belagg_ankare`, eller `ankare` utan `citat`) är
957
+ dispatcherns fel, inte agentens: exit 2 med hänvisning till resolve-anchors.mjs.
958
+ - `scripts/check-lasta.mjs "<LÄSTA-innehåll>" <spec.md> <impact.md>` —
959
+ kontrollerar scorerns läs-scope: LÄSTA-raden ska bära underlagens två
960
+ sökvägar och får därutöver bara bära filer under skillens egen katalog.
961
+ Exit 0 = ok, 1 = kontraktsbrott (BROTT-rader), 2 = användningsfel.
962
+ - `scripts/compare-scores.mjs <a.json> <b.json>` — deterministisk
963
+ dubbelkörningsdiff: outcome,
645
964
  score, confidence, golv- och takankare. Aldrig prosa. Skriver `LIKA`,
646
- `LIKA_TOLERANS` (poängskillnad ≤ 0,5 med samma outcome och confidence, och
647
- ankarband som överlappar eller angränsar; exit 0, verdiktraden pekar ut
648
- körningen med högst poäng) eller `OLIKA` + diffrader; i övrigt samma
649
- exit-semantik. Båda compare-scripten kräver mode `pricing`; guard-jämförelser
650
- är en medveten utbyggnad i guard-steget.
651
- - `scripts/quote-repair.mjs <run.json> <spec.md> [impact.md]`: deterministisk
652
- citaträttning som körs FÖRE omdispatch när contract-check fällt ett citat: ett
653
- nästan-ordagrant belägg (eller citattecken-omgärdat driver-segment) ersätts med
654
- sin entydigt närmaste källmening, allt annat lämnas orört. Rättar aldrig över
655
- en polaritetsskillnad (negationsord) och skriver allt eller inget, så en
656
- halvrättad fil aldrig arkiveras. Exit 0 = inga orättbara citat kvar (rättade
657
- eller inga), 1 = orättbara kvar och filen orörd (omdispatch), 2 =
658
- användningsfel eller oläsbar input (dispatcherfel).
965
+ `LIKA_TOLERANS` (poängskillnad ≤ 0,5 med samma outcome, oavsett confidence,
966
+ och ankarband som överlappar eller angränsar; exit 0, verdiktraden pekar ut
967
+ körningen med högst poäng) eller `OLIKA` + diffrader; exit 0/1, exit 2 =
968
+ ojämförbara körningar (dispatcherfel, aldrig LIKA). Scriptet kräver mode
969
+ `pricing`; guard-jämförelser är en medveten utbyggnad i guard-steget.
659
970
 
660
971
  Kontrollerna bedömer aldrig innehåll och returnerar aldrig protokollutfall —
661
972
  en post som passerar kan fortfarande stoppas av protokollen själva, och det är
662
973
  deras jobb. Vid script-fel (exit 2 eller krasch): visa felet och stanna, kör
663
974
  aldrig vidare som om kontrollen passerat.
664
975
 
665
- ## Dispatch-kontrakt (gäller alla fyra agenterna)
976
+ ## Dispatch-kontrakt (gäller alla tre agenterna)
666
977
 
667
978
  - Prompten är självbärande: agenten ser inte den här konversationen.
668
979
  - **Promptmallar, etiketterna ordagrant:**
@@ -692,31 +1003,32 @@ aldrig vidare som om kontrollen passerat.
692
1003
  <granskarblock.md bytetroget>
693
1004
  ```
694
1005
 
695
- `request-classifier` / `request-scorer`:
1006
+ `request-scorer` (underlag per sökväg, ADR-0099 — aldrig inklistrat):
696
1007
 
697
1008
  ```
698
1009
  REQUEST_ID: <gid eller slug>
699
1010
  MODE: <pricing|guard>
700
1011
  DELPOST: <nej | ja, del N av M>
701
1012
 
702
- SPEC:
703
- <spec.md bytetroget>
704
-
705
- PÅVERKANSANALYS:
706
- <impact.md bytetroget>
1013
+ SPEC: <absolut sökväg till spec.md>
1014
+ PÅVERKANSANALYS: <absolut sökväg till impact.md>
707
1015
  ```
708
1016
 
709
- (guard-läge lägger till `FULLPLAN:` och `TIDIGARE_KLASSNING:` /
710
- `TIDIGARE_POANG:` — guard-kommandot kommer i ett senare steg.)
1017
+ (guard-läge lägger till `FULLPLAN:` och `TIDIGARE_POANG:` —
1018
+ guard-kommandot kommer i ett senare steg.)
711
1019
 
712
1020
  - **Svansrader efter huvudinnehållet**, i denna ordning: `MODELL: <modell-id>`
713
1021
  (alla agenter), `LÄSTA: <kommaseparerade absoluta sökvägar eller "inga">`
714
- (endast classifier/scorer — deras Read är promptbegränsad, raden gör regeln
1022
+ (endast scorern — dess Read är promptbegränsad, raden gör regeln
715
1023
  granskbar), `AVVIKELSE: <text>` (endast om något i prompten bröt mot agentens
716
1024
  regler, t.ex. en prislista i underlaget). Svansraderna strippas före varje
717
1025
  maskinell kontroll och före arkivering; MODELL antecknas i runs-raden,
718
- LÄSTA verifieras ligga under agentens tillåtna katalog, AVVIKELSE visas
719
- alltid för användaren.
1026
+ AVVIKELSE visas alltid för användaren. LÄSTA kontrolleras MEKANISKT av
1027
+ `scripts/check-lasta.mjs` (ADR-0099): raden ska bära underlagens två
1028
+ utpekade sökvägar (båda obligatoriska) och får därutöver bara bära filer
1029
+ under skillens egen katalog (referensfilen) — allt annat är ett
1030
+ kontraktsbrott och behandlas som BROTT i bedöm steg 5 (omdispatch EN
1031
+ gång, sedan stopp), aldrig bara en notis.
720
1032
  - Agentens svar i övrigt är rådata enligt skillens kontrakt (prosa-dokument
721
1033
  eller JSON), ingen kommentar runt om.
722
1034
  - **Omdispatch (max EN per agent och varv):** skicka HELA ursprungsprompten
@@ -726,6 +1038,11 @@ aldrig vidare som om kontrollen passerat.
726
1038
  <felraderna från kontrollen, citerade ordagrant>
727
1039
  ```
728
1040
  En ny subagent minns inget — enbart felrader utan underlag är obrukbart.
1041
+ **Omtaget bokförs alltid (ADR-0099):** det förkastade försöket arkiveras
1042
+ med `-avvisad`-suffix (prissättning: `<typ>-pricing-vN-avvisad.json`;
1043
+ textsteg: `spec-avvisad-<datum>.md` / `impact-avvisad-<datum>.md`) och får
1044
+ en egen runs-rad med kontrollens felrader — två körningar av samma steg
1045
+ utan spår av varför är precis den lucka kvittot stänger.
729
1046
 
730
1047
  ## Använd inte när
731
1048
 
@@ -734,5 +1051,5 @@ aldrig vidare som om kontrollen passerat.
734
1051
  - Frågan gäller Asana-uppsättning ("koppla repot") — det är request-sync:s
735
1052
  uppsättningsflöde, kör den direkt.
736
1053
  - Guard-läget ("kör guarden", före första kodrad) — körs tills vidare via
737
- request-classify/request-score direkt; ett `guard`-kommando kommer i ett
1054
+ request-score direkt; ett `guard`-kommando kommer i ett
738
1055
  senare steg.