@haus-tech/haus-workflow 5.0.0 → 5.1.0

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.
@@ -65,7 +65,7 @@ respektive skill. Här bor bara routing, kontextbärande, arkivet och dispatchen
65
65
  - **Experimentdata når aldrig Asana.** `utvärdera` skriver aldrig via
66
66
  request-sync; dess enda Asana-kontakt är läsande hämtning av önskemålet i
67
67
  huvudkonversationen. `experiments/`-trädet är inte postkataloger: sync,
68
- 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
69
69
  och rör aldrig `experiments/`.
70
70
  - **Maskinen kör om enbart vid scriptbevisat kontraktsbrott.** Omdispatch
71
71
  (budget EN per agent) svarar på ett fel som precheck, resolve-anchors eller
@@ -74,7 +74,7 @@ respektive skill. Här bor bara routing, kontextbärande, arkivet och dispatchen
74
74
  maskinkörningar producerar fler röster, aldrig mer sanning. Ingen ny
75
75
  omkörningsmekanism får läggas till som bryter mot detta.
76
76
 
77
- ## Arkivet: `.haus/requests/`
77
+ ## Arkivet: `.haus-workflow/requests/`
78
78
 
79
79
  Orkestreringens kuvert. De haus-hanterade request-skillsen känner inte till det;
80
80
  den här skillen (och agenterna via sina prompter) underhåller det.
@@ -84,10 +84,10 @@ samma önskemål) blir flera kataloger — poängen sätts per post, så en kata
84
84
  med flera specar går inte att bedöma.
85
85
 
86
86
  ```
87
- <repo-rot>/.haus/requests/<gid-eller-slug>/
87
+ <repo-rot>/.haus-workflow/requests/<gid-eller-slug>/
88
88
  ├── spec.md EN spec, bytetrogen — det granskaren godkänner
89
- ├── granskarblock.md "## Till granskaren" (bunt: samma block
90
- │ kopieras till varje delkatalog)
89
+ ├── granskarblock.md "## Till granskaren" (bunt: delens eget
90
+ │ block, aldrig ett kopierat, ADR-0118)
91
91
  ├── impact.md påverkansanalysen, ordagrann inkl. sitt
92
92
  │ Till granskaren-block
93
93
  ├── score-pricing-vN.json nytt varv = ny fil, skriv aldrig över
@@ -109,10 +109,15 @@ omdispatch-regeln i dispatch-kontraktet.
109
109
  - Katalognamn: Asana-gid när posten finns på brädan, annars en kebab-slug av
110
110
  spec-rubriken. Bunt utan gid: `<slug>-1`, `<slug>-2`, … När gid myntas senare:
111
111
  döp om katalogen till gid och notera sluggen i `state.json.former_slug`.
112
- - **Första gången arkivet skapas i ett repo:** säkerställ att `.haus/requests/`
113
- står i repots `.gitignore` (lägg till raden om den saknas, säg det).
114
- Prisunderlag ska inte in i kundens historik. `.haus/request.json` (syncens
115
- 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.
116
121
 
117
122
  ### `state.json`
118
123
 
@@ -128,13 +133,37 @@ omdispatch-regeln i dispatch-kontraktet.
128
133
  "approval": null,
129
134
  "granskarutslag": [],
130
135
  "note": null,
136
+ "stopp": null,
137
+ "respec_varv": 0,
138
+ "coverage_post": null,
131
139
  "runs": []
132
140
  }
133
141
  ```
134
142
 
135
143
  - `bundle` = specar ur samma önskemål (sätts av speca vid bunt).
136
144
  `split` = delposter ur ett DELA_UPP-utfall (sätts av bedöm). Båda formen
137
- `{parent…, part: N, of: M}`.
145
+ `{parent…, part: N, of: M}`. Aldrig båda på samma katalog.
146
+ - `stopp` = ett stopputfall som scripten bevisat utan scorer:
147
+ `{ "utfall": "KONCEPT" | "DIAGNOS" | "TILLBAKA_TILL_SPEC" | "ESKALERA", "kalla": "spec.md" | "impact.md" | "granskarblock.md", "date": "YYYY-MM-DD", "orsak"?: string }`.
148
+ Skrivs BARA av `archive.mjs stopp` (som själv kontrollerar att källan bär
149
+ det påstådda: kandidatmarkören i `spec.md`, etiketten `motsägelse` i
150
+ `impact.md`, det prisavgörande öppna valet i `granskarblock.md`, ADR-0118)
151
+ och nollas BARA av `archive.mjs respec`. `orsak` sätts när `utfall` är
152
+ `ESKALERA` (kommandot kräver `--orsak` då, så en människa ser varför posten
153
+ inte gick tillbaka till specen en gång till) och saknas annars.
154
+ `archive.mjs set-status priced` vägrar medan fältet är satt. Äldre arkiv
155
+ saknar fältet och läses som `null`.
156
+ - `respec_varv` är en monoton räknare, höjd med 1 av varje `archive.mjs
157
+ respec`-anrop (#505), aldrig nollad. Grinden i bedöm läser den för att
158
+ avgöra om ett kvarstående prisavgörande öppet val ska stoppa med
159
+ `TILLBAKA_TILL_SPEC` (första varvet) eller `ESKALERA` (andra varvet och
160
+ senare): en post går aldrig en tredje specrunda på samma val. Äldre arkiv
161
+ saknar fältet och läses som `0`.
162
+ - `coverage_post` speglar score-kuvertets valfria `coverage_post: { yta,
163
+ omfattning }` (ADR-0119). `archive.mjs add-run` kopierar fältet från den
164
+ senaste pricing-filen som bär det, skelettet (`ny-korning`/`ny-del`) sätter
165
+ det till `null`. `bluf` visar `täckningspost: <yta>` i `anm`-kolumnen när
166
+ fältet är satt.
138
167
  - `approval` skrivs av grinden i bedöm:
139
168
  `{ "source": "asana" | "manual", "by": "<namn, krävs vid manual>",
140
169
  "status_read": "<värde>", "date": "YYYY-MM-DD" }`.
@@ -215,23 +244,29 @@ omdispatch-regeln i dispatch-kontraktet.
215
244
  Modellkörningar från `utvärdera` bor i ett eget träd och är aldrig prisposter:
216
245
 
217
246
  ```
218
- <repo-rot>/.haus/requests/experiments/<slug>/
247
+ <repo-rot>/.haus-workflow/requests/experiments/<slug>/
219
248
  ├── experiment.json ticketreferens, datum, modellista, manifest-hash,
220
249
  │ status per körning
221
250
  ├── <modell>/ `default` som modellvärde ger katalognamnet
222
251
  │ `baseline/`
223
252
  │ ├── k1/
224
- │ ├── k1/korning.json headless-svaret RÅTT: USD, tokens, dispatcher
253
+ │ ├── k1/steg/ råsvaret per agentsteg (claude -p-JSON), inklusive
254
+ │ │ omdispatcher och kundtextfixar
255
+ │ ├── k1/korning.json summan över stegen: USD, tokens, modelUsage, antal
256
+ │ │ steg (report.mjs läser samma nycklar som förut)
225
257
  │ ├── k1/matning.json startarens egen mätning: startad, avslutad, wall_ms
226
258
  │ ├── k2/ senare körning av SAMMA modell
227
- │ └── k2/del-1/ … delkataloger vid DELA_UPP
259
+ │ └── k2/del-1/ … delkataloger: bunt ur speca (`bundle`) eller
260
+ │ delning ur DELA_UPP (`split`), aldrig båda
228
261
  └── <annan modell>/k1/ …
229
262
  ```
230
263
 
231
- - **Två filer, två ägare.** `korning.json` är headless-svaret ordagrant och
232
- ägs av CLI:n; `matning.json` är startarens klocka och ägs av oss. En
233
- haus-nyckel inne i `korning.json` skulle göra den till ett blandat format
234
- som inte längre går att ställa mot CLI:ns egen utdata.
264
+ - **Två filer, två ägare.** `korning.json` skrivs av `korning.mjs` som summan
265
+ över stegens råsvar, som ligger orörda i `steg/`; `matning.json` är
266
+ startarens klocka. En haus-nyckel i ett råsvar under `steg/` skulle göra det
267
+ till ett blandat format; summan bär därför sina egna nycklar
268
+ (`korningsmotor`, `steg`, `modellverifiering`) och råsvaren rörs aldrig
269
+ (ADR-0113 amends ADR-0094).
235
270
  - **Ingen körning skrivs någonsin över.** Värdet i en gammal körning är att
236
271
  kunna jämföra samma modell före och efter en ändring i skillen eller
237
272
  agentprompterna; skriver en ny körning över den är den jämförelsen borta för
@@ -240,13 +275,28 @@ Modellkörningar från `utvärdera` bor i ett eget träd och är aldrig prispost
240
275
  bytetroget (spec.md, granskarblock.md, impact.md, pricing-JSON, state.json,
241
276
  `runs[]`-reglerna), så `precheck.mjs` och `contract-check.mjs` fungerar
242
277
  oförändrade, de pekas bara på `<modell>/k<N>/`. Proveniensen följer med gratis.
278
+ - **Buntregeln: `del-N/` är antingen bunt eller delning, aldrig båda.** En
279
+ bunt ur speca (flera specar ur samma önskemål) läggs som `del-1/ … del-N/`
280
+ under körningskatalogen med `bundle` satt i varje dels `state.json`; en
281
+ delning ur DELA_UPP läggs likadant men med `split` satt. Del 1 ligger ALDRIG
282
+ i körningsroten: roten bär bara `state.json` (skelettet `ny-korning` skrev),
283
+ `korning.json` och `matning.json`, så en rot utan `spec.md` men med delar
284
+ läses som `bunt (N delar)` av `bluf` och rapporten. DELA_UPP inuti en
285
+ buntdel ger `del-N/del-M`. `sparr`, `precheck` och `stopp` körs per del;
286
+ `proveniens` och `bluf` pekas på `k<N>` och går rekursivt genom delarna.
287
+ **Produktionslayouten är oförändrad** (`speca` steg 5: del 1 behåller
288
+ gid-katalogen, syskonen blir `<slug>-N`); det är `korning.mjs` som väljer
289
+ experimentlayouten, alltid mot katalogen `--katalog` pekar ut.
243
290
  - **Numret allokeras aldrig av skillen själv**, alltid med
244
- `archive.mjs ny-korning <modellkatalog>`, som skapar katalogen
245
- atomiskt och skriver dess sökväg. Två samtidiga processer, till exempel två
246
- Claude-konton mot samma checkout, får då `k2` och `k3`, aldrig samma katalog.
247
- Räknar skillen själv förlorar den ena processens körning tyst.
248
- - `state.json` i en körningskatalog bär markören `"experiment": true`.
249
- `approval` förblir `null` och `status` blir ALDRIG `priced`: ett
291
+ `archive.mjs ny-korning <modellkatalog> [--request-id <gid eller slug>]`,
292
+ som skapar katalogen atomiskt, skriver skelettet till dess `state.json`
293
+ (`"experiment": true`, `"stopp": null`, `approval: null`, tomma `runs[]`)
294
+ och skriver katalogens sökväg på stdout. Två samtidiga processer, till
295
+ exempel två Claude-konton mot samma checkout, får då `k2` och `k3`,
296
+ aldrig samma katalog. Räknar skillen själv förlorar den ena processens
297
+ körning tyst.
298
+ - `state.json` i en körningskatalog bär markören `"experiment": true` ur
299
+ skelettet. `approval` förblir `null` och `status` blir ALDRIG `priced`: ett
250
300
  experimentresultat kan inte förväxlas med en prispost.
251
301
  - Katalognamnet `experiments` är reserverat: en spec vars rubrik skulle sluggas
252
302
  till `experiments` får suffixet `-post`.
@@ -254,8 +304,9 @@ Modellkörningar från `utvärdera` bor i ett eget träd och är aldrig prispost
254
304
  körning: `"klar"`, `"ofärdig"`, `"stopp"`, `"väntar på dom"`. Literalerna
255
305
  skrivs exakt så, mellanslag och diakritiska tecken inkluderade. Äldre filer
256
306
  med `baselinemodell` och `kandidatmodell` läses utan fel; nya skrivningar
257
- använder bara `varianter`.
258
- - `experiments/` ärver gitignore-skyddet från `.haus/requests/`.
307
+ använder bara `varianter`. Filen skrivs BARA av `archive.mjs experiment`
308
+ (se `utvärdera` steg 1), aldrig för hand.
309
+ - `experiments/` ärver gitignore-skyddet från `.haus-workflow/requests/`.
259
310
 
260
311
  ## Uppgift: `speca`
261
312
 
@@ -267,6 +318,17 @@ Modellkörningar från `utvärdera` bor i ett eget träd och är aldrig prispost
267
318
  (vyer/termer önskemålet nämner), tidigare specar för samma kund (arkivet +
268
319
  brädan), ev. brief. **Alla sökvägar i manifestet absoluta.** Finns inget repo
269
320
  i sessionen: skriv `KÄLLOR: inga — allt blir antaganden` så specifiern vet.
321
+ Starten avgör samtidigt två rader som specifiern aldrig får gissa:
322
+ `SPRÅK:` (`svenska` eller `engelska`, avläst ur önskemålets brödtext;
323
+ rubrikskelettet i `RUBRIKER:` följer det språket, se promptmallen) och
324
+ `DEPS:` (`installerade` när `node_modules/` eller motsvarande finns under
325
+ REPO-ROT, annars `saknas`; utan repo alltid `saknas`).
326
+ Skriv manifestet till `<katalog>/kallmanifest.md` i varje katalog specen
327
+ arkiveras i (steg 5), så att `bedöm` kan ge assessorn samma manifest utan
328
+ att bygga ett nytt.
329
+ Manifestet har sist sektionen `## Avgjorda val`: varje val som en människa
330
+ avgjort efter ett tidigare stopp, en rad var, ordagrant som svaret gavs;
331
+ `inga` om inga. Specifiern får aldrig flagga ett val som står där.
270
332
  3. **Eskaleringsbedömning.** Misstänkt bunt (flera beslut), behörighet/betalning/
271
333
  oåterkallelig data/personuppgifter, eller offertunderlag → utfallet är
272
334
  prisbärande och eskaleringen är ett MODELLBYTE, inte en flagga: dispatcha
@@ -274,10 +336,15 @@ Modellkörningar från `utvärdera` bor i ett eget träd och är aldrig prispost
274
336
  `request-specifier`, med `ESKALERAD: ja` plus orsaken i prompten.
275
337
  4. **Dispatcha `request-specifier`** (eller den eskalerade varianten enligt
276
338
  steg 3) enligt promptmallen i dispatch-kontraktet.
277
- 5. **Arkivera.** Dela svaret vid `## Till granskaren`. En spec → en katalog:
278
- allt före blocket är specarna; VARJE spec får sin egen katalog med egen
279
- `spec.md` och eget `state.json` (bunt: `bundle`-fältet ifyllt, blocket
280
- kopieras till varje katalog som `granskarblock.md`). Bunt ur en gid-post:
339
+ 5. **Arkivera.** Specifierns svar delas per spec (ADR-0118): varje titelrad
340
+ startar en ny spec, och specens EGET `## Till granskaren`-block avslutar
341
+ den. Ett svar med bara ETT block efter flera specar (gamla formatet) är en
342
+ brist: `korSpecifier` dispatchar om HELA specifierprompten en gång med
343
+ `RÄTTNINGAR:` innan någon katalog skapas, och kvarstår bristen (delat block
344
+ eller ett saknat block) blir steget `ofärdig` direkt, utan katalog. En
345
+ spec → en katalog: VARJE spec får sin egen katalog med egen `spec.md`,
346
+ eget `granskarblock.md` (delens eget block, aldrig ett kopierat) och eget
347
+ `state.json` (bunt: `bundle`-fältet ifyllt). Bunt ur en gid-post:
281
348
  del 1 behåller gid-katalogen, resterande delar får slug-kataloger med
282
349
  `bundle.parent_slug` satt till gid:t; syskontasks på brädan mintas först
283
350
  vid sync. Stämpla varje berörd `state.json` med
@@ -289,11 +356,14 @@ på request-write-spec/references/specformat.md>`. Scriptet sätter
289
356
  referens-hashen måste sättas explicit precis som för poängen, annars
290
357
  tappas specformatets proveniens (se runs-kontraktet).
291
358
  **Respec över befintlig katalog invaliderar analysen:** när `spec.md`
292
- ersätts döps ev. `impact.md` om till `impact-superseded-<datum>.md`,
293
- status sätts till `spec_draft`, `approval` nollas och `granskarutslag`
294
- töms — en ny spec är ogranskad per definition, ett utslag mot en ersatt
295
- spec är inte längre giltigt, och en gammal analys mot ny spec är exakt
296
- den skevhet proveniens-kontrollen finns för att stoppa.
359
+ ersätts i en katalog som redan har en, kör
360
+ `node <skillkatalog>/scripts/archive.mjs respec <katalog>` INNAN den nya
361
+ specen skrivs. Kommandot äger hela invalideringen (analysen läggs undan
362
+ som superseded, status, `approval`, `granskarutslag` och `stopp` återställs,
363
+ `runs[]` rörs aldrig); skriv aldrig de fälten för hand. Skälet står i
364
+ runs-kontraktet: en ny spec är ogranskad per definition, ett utslag mot en
365
+ ersatt spec är inte längre giltigt, och en gammal analys mot ny spec är
366
+ exakt den skevhet proveniens-kontrollen finns för att stoppa.
297
367
  6. **Formkontroll.** Kör `node <skillkatalog>/scripts/precheck.mjs <katalog>`
298
368
  per katalog. BRISTER i spec/granskarblock → dispatcha om specifiern EN gång
299
369
  enligt omdispatch-regeln. **Det förkastade utkastet får kvitto
@@ -304,9 +374,35 @@ på request-write-spec/references/specformat.md>`. Scriptet sätter
304
374
  dispatch får sin egen runs-rad — även den förkastade — så arkivet bär
305
375
  både att ett omtag skedde och varför; en spec godkänd i andra försöket
306
376
  får aldrig se ut som en som blev rätt direkt.
377
+ **Omdispatch gäller endast den spec som föll.** I en bunt rörs de specar
378
+ som passerade inte: omdispatchprompten bär hela ursprungsprompten plus
379
+ `RÄTTNINGAR:` med felraderna för just den specen och raden
380
+ `Returnera endast spec <N>`; svaret ersätter bara den katalogens utkast.
381
+ **Kvarstår brist efter omdispatch blir utkastet aldrig `spec.md`:** även
382
+ det andra utkastet arkiveras som `spec-avvisad-<datum>.md` med egen
383
+ runs-rad, katalogen står utan `spec.md` och behåller status `spec_draft`,
384
+ och i `utvärdera` blir körningen `"ofärdig"`. Visa bristerna och stanna.
385
+ **Kandidat stoppas här, aldrig i bedöm.** Skriver precheck raden
386
+ `KANDIDAT: KONCEPT` eller `KANDIDAT: DIAGNOS` (specen bär markörraden ur
387
+ specformatets kandidatform), kör direkt
388
+ `node <skillkatalog>/scripts/archive.mjs stopp <katalog> --utfall <utfallet ur raden> --kalla spec.md`
389
+ på den katalogen: en kandidat har inget analys- eller poängsteg att vänta
390
+ på, och `stopp`-fältet är det som gör utfallet synligt för `bluf`,
391
+ rapporten och `set-status`. Kandidatens katalog kan fortfarande läggas på
392
+ brädan i steg 7 (kunddialogen är nästa steg för den), men `bedöm` hoppar
393
+ över den.
394
+ **Prisavgörande öppet val stoppas här (ADR-0118).** Skriver precheck raden
395
+ `PRISAVGORANDE: <valet>` (granskarblockets rad `Prisavgörande öppet val:
396
+ ja: …`), kör `archive.mjs stopp <katalog> --utfall TILLBAKA_TILL_SPEC
397
+ --kalla granskarblock.md` och visa valet i kundens språk; ingen assessor.
398
+ Är `state.json.respec_varv` redan ≥ 1 kör i stället `--utfall ESKALERA
399
+ --kalla granskarblock.md --orsak "prisavgörande val kvarstår efter respec:
400
+ <valet>"`: en människa dömer, posten går aldrig en tredje specrunda. När
401
+ valet avgjorts skrivs svaret under `## Avgjorda val` i manifestet, sedan
402
+ `respec` och ny specifier-dispatch.
307
403
  BRISTER i `state.json` är wrapperns egna filer och
308
- rättas av wrappern själv, aldrig via omdispatch. Kvarstår brister, visa dem
309
- och stanna. VARNING:ar visas men stoppar inget.
404
+ rättas av wrappern själv, aldrig via omdispatch. VARNING:ar visas men
405
+ stoppar inget.
310
406
  7. **Svara BLUF:** en rad per spec (rubrik + ev. flaggor), sedan granskarblocket
311
407
  i sin helhet. Erbjud att lägga specarna på brädan via request-sync (🔍) — gör
312
408
  det bara på uttrycklig begäran. När en post läggs på brädan: sätt status
@@ -325,7 +421,7 @@ driftvakt.
325
421
  Endast `✅ Spec ok` (normaliserad på alfabetiska kärnan) släpper igenom.
326
422
  Annan status: visa den och stanna. En inklistrad spec för en post som har
327
423
  gid räknas inte — det är brädstatusen som gäller, läs den ändå.
328
- - **Posten saknar gid MEN repot har `.haus/request.json`:** vägra — repot är
424
+ - **Posten saknar gid MEN repot har `.haus-workflow/request.json`:** vägra — repot är
329
425
  kopplat till en bräda, så en gid-lös post ska in via intaget och granskas
330
426
  där, inte prissättas vid sidan av. Grinden är värd något bara om den inte
331
427
  går att kliva runt.
@@ -343,53 +439,93 @@ driftvakt.
343
439
  specblock är en BRIST — dela posten enligt arkivregeln innan något dispatchas.
344
440
  Övriga BRISTER → åtgärda ledet som brister (om spec: tillbaka till
345
441
  speca-steget) innan något dyrt dispatchas.
442
+ **`KANDIDAT:`-raden avgör före allt annat.** Skriver precheck
443
+ `KANDIDAT: KONCEPT` eller `KANDIDAT: DIAGNOS` (raden kommer alltid, även
444
+ vid exit 1) är posten en kandidat: hoppa över steg 3 till 7, kör
445
+ `archive.mjs stopp <katalog> --utfall <utfallet ur raden> --kalla spec.md`
446
+ om `stopp` inte redan är satt (speca steg 6 brukar ha gjort det;
447
+ ett identiskt omanrop är ofarligt), och routa posten som stopputfallet
448
+ nedan. Ingen analys och ingen poäng får någonsin dispatchas på en
449
+ kandidat.
346
450
  3. **Analys.** Saknas `impact.md`, eller fäller precheck den på
347
451
  underlagsproveniens (analysen gjordes mot en annan spec): kräver repo i
348
452
  sessionen (annars stopp — en gissad analys är förbjuden av skillen själv).
349
453
  Dispatcha `request-assessor` enligt promptmallen — specens granskarblock
350
454
  skickas med som internt underlag (samma förtroendedomän; flaggorna där är
351
- ofta analysens viktigaste ingång). Arkivera svaret som `impact.md`
455
+ ofta analysens viktigaste ingång). Promptens `KÄLLOR:`-rad bär
456
+ `kallmanifest.md` ur katalogen ordagrant, eller `inga` när filen saknas;
457
+ assessorn verifierar manifestet själv och ska aldrig ta det för sant
458
+ (ADR-0113: assessorn utforskade repot från noll trots att manifestet redan
459
+ fanns). Arkivera svaret som `impact.md`
352
460
  (inklusive dess "## Till granskaren"-block — visa blocket för användaren,
353
461
  aldrig till kundfält), stämpla med
354
462
  `node <skillkatalog>/scripts/archive.mjs add-run <katalog> --file impact.md
355
463
  --skill request-assess-impact --agent request-assessor
356
464
  --model <ur MODELL-svansraden>` (scriptet sätter `underlag_sha256` mot
357
- `spec.md`). Kör precheck igen med `--impact`. Fäller precheck det nya
465
+ `spec.md`). Kör precheck igen med `--impact`. Läs sedan raden
466
+ `AVVIKELSE: <etikett>` ur precheck-utskriften (etiketten på analysens rad
467
+ `**Avvikelse mot specen:**`; inte att förväxla med agenternas svansrad):
468
+ `motsägelse` betyder att specen förutsätter något som inte stämmer i
469
+ systemet, och då kör
470
+ `node <skillkatalog>/scripts/archive.mjs stopp <katalog> --utfall TILLBAKA_TILL_SPEC --kalla impact.md`
471
+ (kommandot sätter status `assessed`), hoppa över steg 4 till 7 och routa
472
+ posten som stopputfallet nedan: ingen scorer dispatchas på ett underlag
473
+ som redan säger att specen måste tillbaka. `ingen` och `precisering` går
474
+ vidare till steg 4. Fäller precheck det nya
358
475
  utkastet → omdispatcha assessorn EN gång enligt omdispatch-regeln, och ge
359
476
  det förkastade utkastet kvitto precis som i speca steg 6: arkivera det som
360
477
  `impact-avvisad-<datum>.md` och stämpla en egen runs-rad med kontrollens
361
478
  felrader i `--note` (ADR-0099).
362
479
  4. **Prissättning.** Dispatcha `request-scorer` enligt promptmallen.
363
480
  Ingen prislista, inget repo, inga andra posters dokument i prompten.
364
- 5. **Ankarlösning och kontraktskontroll.** Strippa svansraderna (`MODELL:`,
365
- `LÄSTA:`, ev. `AVVIKELSE:`) ur svaret. Kör
366
- `node <skillkatalog>/scripts/check-lasta.mjs "<LÄSTA-radens innehåll>" <spec.md> <impact.md>`
367
- (exit 1 = kontraktsbrott, se dispatch-kontraktet). Skriv sedan den rena
368
- JSON:en till fil och kör i ordning:
369
- `node <skillkatalog>/scripts/resolve-anchors.mjs <svar.json> <spec.md> <impact.md>`
370
- (löser beläggsankare till ordagranna källblock; OLÖST/FLERTYDIG är ett
371
- kontraktsbrott), sedan
372
- `node <skillkatalog>/scripts/contract-check.mjs <svar.json> <spec.md> <impact.md> --expect-id <katalogens id>`.
373
- BROTT från något av scripten → dispatcha om scorern EN gång enligt
481
+ 5. **Ankarlösning och kontraktskontroll.** Skriv agentens hela svarstext (JSON
482
+ plus svansrader) till fil och kör
483
+ `node <skillkatalog>/scripts/kontroll.mjs <svar.txt> <spec.md> <impact.md> --expect-id <katalogens id> --ut <svar.json> --fixa-kundtext --state <katalog>/state.json`.
484
+ `--state` ger contract-check split-status: `OVER_SKALAN` på något som inte
485
+ är en split-del fälls, och ett inget-tak-utfall utan steg mot A16 fälls
486
+ (#503). Scriptet strippar svansraderna (`MODELL:`, `LÄSTA:`, ev. `AVVIKELSE:`),
487
+ reparerar det dispatchern redan vet (`request_id`, enumform,
488
+ siffersträngar, LÄSTA-radens sökvägsform) och kör check-lasta,
489
+ `resolve-anchors.mjs` (beläggsankare till ordagranna källblock;
490
+ OLÖST/FLERTYDIG är ett kontraktsbrott) och contract-check i en process; ett
491
+ protokollterm-brott i ett rent kundtextfält skrivs om av en billig modell i
492
+ stället för att kosta en ny scorerkörning. `REPARERAT:`-raderna visas i
493
+ BLUF-svaret och skrivs i runs-radens `--note`.
494
+ `impact.md` skickas ALLTID med till contract-check, aldrig bara specen:
495
+ scriptet läser etiketten på `**Avvikelse mot specen:**` och fäller varje
496
+ annat utfall än TILLBAKA_TILL_SPEC när analysen bär `motsägelse`
497
+ (motsägelsegrinden i scriptet är bältet, steg 3 hängslena; en post som
498
+ ändå nått hit har ett underlag scriptet ska få se). Samma script kräver
499
+ att varje `bracket.steps[i]` är exakt `{anchor, relation, grund}`.
500
+ BROTT som kvarstår efter reparationerna → omdispatch EN gång enligt
374
501
  omdispatch-regeln, med felraderna i prompten. Kvarstår brott: visa dem och
375
502
  stanna utan att arkivera svaret som giltigt varv, men släng det inte:
376
503
  arkivera som `score-pricing-vN-avvisad.json` (utanför N-räkningen) med
377
504
  kontrollens felrader i en runs-rad, så även misslyckade körningar har
378
505
  kvitto. En `AVVIKELSE:`-rad visas alltid för användaren.
379
- 6. **Dubbelkörning (driftvakt).** Körs BARA när svaret inte är ett
380
- stopputfall — stopproutningen nedan går alltid först, en post som ändå
381
- går till människa ska inte kosta extra körningar. Trigger: poängen är ≥ 1
382
- (ADR-0098: tröskeln täcker minst det den borttagna klassaxelns
383
- MEDIUM/LARGE/OFFERT-villkor täckte; poängen multipliceras direkt till
384
- pris). Dispatcha då `request-scorer` EN gång till,
385
- samma prompt, ny isolerad körning.
386
- Kostnaden (en extra körning) läggs medvetet exakt där pengarna står
387
- på spel — poängen multipliceras direkt till pris och skillens eget
388
- acceptanstest är att två oberoende körningar ger samma resultat.
506
+ 6. **Dubbelkörning (driftvakt).** Körning 2 startar ALLTID samtidigt med
507
+ körning 1, med samma prompt och i en ny isolerad körning; i produktion
508
+ dispatchas båda Agent-anropen på en gång (#500, ADR-0113), promptarna är
509
+ identiska och körningarna oberoende. Trigger: poängen är ≥ 1 (ADR-0098),
510
+ eller utfallet är `OVER_SKALAN` (ADR-0116: det utfallet skickar posten
511
+ till manuell offert och är prisbärande; två OVER_SKALAN med olika golv är
512
+ OLIKA och går till dom). Övriga stopputfall dubbelkörs inte: DELA_UPP körs
513
+ om per del, KONCEPT/DIAGNOS/TILLBAKA_TILL_SPEC går till människa ändå.
514
+ Tröskeln avgör bara om paret RÄKNAS som dubbelkörning, aldrig om
515
+ körning 2 startar. Blir körning 1 ett stopputfall utom OVER_SKALAN, eller
516
+ poäng under 1, arkiveras körning 2 som `-run2` med `superseded_by` mot
517
+ huvudfilen och en notering om att den inte krävdes, den räknas aldrig som
518
+ dubbelkörning, och stopproutningen nedan gäller som förut. I övriga fall
519
+ jämförs paret som nedan; skillens eget acceptanstest är att två oberoende
520
+ körningar ger samma resultat.
389
521
  - Kör hela steg 5 (ankarlösning, sedan kontraktskontroll) på det nya
390
522
  svaret först, med egen omdispatch-budget (EN).
391
523
  - Jämför sedan deterministiskt:
392
- `node <skillkatalog>/scripts/compare-scores.mjs <poäng1.json> <poäng2.json>`
524
+ `node <skillkatalog>/scripts/compare-scores.mjs <poäng1.json> <poäng2.json>`.
525
+ Scriptet diffar även de inre stegen: samma ankare i `bracket.steps` läst
526
+ som `storre` i ena körningen och `mindre` i den andra är en hård diffrad
527
+ (OLIKA), aldrig en toleransrad. Golv- och takankaren är undantagna, de
528
+ flippar av konstruktion när banden angränsar och ägs av toleransregeln.
393
529
  - **LIKA** → körning 1 gäller: `double_run: "bekräftad"` på dess
394
530
  runs-rad. Körning 2-filen arkiveras som `score-pricing-vN-run2.json`
395
531
  med SAMMA N och `superseded_by` satt till körning 1-filen.
@@ -417,7 +553,11 @@ driftvakt.
417
553
  skillnaden ligger inom toleransen. Sätt `double_run: "tolerans"` på
418
554
  bägge runs-raderna. Toleransen är en medveten prisregel: högsta
419
555
  värdet skyddar marginalen, och ett medelvärde räknas fortfarande
420
- aldrig fram. Visa toleransraderna i BLUF-svaret.
556
+ aldrig fram. Visa toleransraderna i BLUF-svaret. Ett par av två
557
+ `OVER_SKALAN` med SAMMA golv där bara konfidensen skiljer skriver
558
+ också `LIKA_TOLERANS`, men utan poäng att jämföra (OVER_SKALAN bär
559
+ `score: null`) vinner körning 1 deterministiskt, aldrig "högsta
560
+ poängen" (ADR-0116).
421
561
  - Exit 2 från jämförelsescriptet är ett dispatcherfel (ojämförbara
422
562
  körningar) — visa felet och stanna, rapportera aldrig LIKA.
423
563
  7. **Arkivera** godkända JSON-svar. Hämta varvets N med
@@ -425,14 +565,18 @@ driftvakt.
425
565
  skriv filerna (`score-pricing-vN.json`, körning 2
426
566
  som `-run2`, avvisade som `-avvisad`), och stämpla VARJE körning med
427
567
  `archive.mjs add-run` (flaggor för fil, skill, agent, modell ur
428
- MODELL-svansraden, effort ur agentdefinitionen, referens-hash via `--ref`
429
- för skillens referensfil (se runs-kontraktet), double-run-utfall,
430
- superseded-by, partial-rerun och pairs-with där de gäller). Exempel för
431
- huvudfilen vid ett bekräftat par:
568
+ MODELL-svansraden som förut, referens-hash via `--ref` för skillens
569
+ referensfil (se runs-kontraktet), double-run-utfall, superseded-by,
570
+ partial-rerun och pairs-with där de gäller). Effort läser scriptet självt
571
+ ur agentdefinitionen under Claude-konfigkatalogen när `--effort`
572
+ utelämnas, så utelämna flaggan; `--model` förblir obligatorisk och kommer
573
+ ur svansraden, aldrig ur definitionen (under `utvärdera` körs agenterna
574
+ med override, så definitionens modell är per konstruktion inte den som
575
+ kördes). Exempel för huvudfilen vid ett bekräftat par:
432
576
  `node <skillkatalog>/scripts/archive.mjs add-run <katalog>
433
577
  --file score-pricing-v1.json --skill request-score
434
578
  --agent request-scorer --model <ur MODELL-svansraden>
435
- --effort <ur agentdefinitionen> --ref poangskala.md=<hash via
579
+ --ref poangskala.md=<hash via
436
580
  ref-hash.mjs på request-score/references/poangskala.md>
437
581
  --double-run bekräftad`. `--double-run` tar ENDAST de tre
438
582
  literalerna `bekräftad`, `olika` eller `tolerans` (scriptet vägrar allt
@@ -444,7 +588,12 @@ ref-hash.mjs på request-score/references/poangskala.md>
444
588
  `archive.mjs set-status <katalog> priced` (scriptet vägrar vid spärr,
445
589
  stopputfall och experiment — se runs-kontraktet)
446
590
  eller `assessed`.
447
- 8. **Svara BLUF:** tabell `post · poäng · utfall · konfidens`, en rad
591
+ 8. **Svara BLUF:** tabellen kommer ur
592
+ `node <skillkatalog>/scripts/archive.mjs bluf <katalog>` och klistras in
593
+ som den är (`| post | poäng | utfall | konfidens | anm |`, en rad per
594
+ katalog och del; poängen ur filerna, stopputfall ur `stopp`, OLIKA och
595
+ tolerans i `anm`), ALDRIG handskriven: en handskriven tabell
596
+ felrapporterade i skarp körning. Under tabellen en rad
448
597
  per avvikelse värd att se (ESKALERA, DELA_UPP,
449
598
  INPUT_SAKNAS, AVVIKELSE-rader). JSON:en visas inte i chatten —
450
599
  `detaljer <post>` gör det. Skriv-tillbaka till brädan sker via request-sync
@@ -458,14 +607,23 @@ fil (steg 7), statusen blir `assessed`, aldrig `priced`, och posten routas:
458
607
  - `DELA_UPP` → föreslå ny analysrunda per del; delarna blir egna kataloger med
459
608
  `split` satt (`part`/`of`), och en del utan tak blir `OVER_SKALAN`, aldrig
460
609
  DELA_UPP igen. Delposter ärver enligt skillen postens spec och skriver aldrig
461
- en egen: förälderns `spec.md` och `granskarblock.md` KOPIERAS in i varje
462
- delkatalog (som buntens granskarblock), medan `impact.md` skrivs per del av
463
- den nya analysrundan.
610
+ en egen: förälderns `spec.md` och `granskarblock.md` KOPIERAS oförändrade in
611
+ i varje delkatalog, var för sig (ett granskarblock per delkatalog, ADR-0118,
612
+ ingen delkatalog delar sitt block med en annan), medan `impact.md` skrivs per
613
+ del av den nya analysrundan.
464
614
  - `OVER_SKALAN` → posten offereras separat; delas aldrig vidare (delningen görs
465
615
  ett steg, skillens egen regel).
466
616
  - `TILLBAKA_TILL_SPEC` / `KONCEPT` / `DIAGNOS` → tillbaka till spec-steget
467
- respektive kunddialog; visa skälet.
468
- - `ESKALERA` / `INPUT_SAKNAS` → visa skälet och stanna.
617
+ respektive kunddialog; visa skälet. Kom utfallet ur scripten (kandidat i
618
+ `spec.md`, `motsägelse` i `impact.md`, prisavgörande öppet val i
619
+ `granskarblock.md`, ADR-0118) bär `state.json.stopp` det via
620
+ `archive.mjs stopp` och det finns ingen varvsfil; kom det ur scorern
621
+ arkiveras varvsfilen som förut och `stopp` förblir `null` (kommandot tar
622
+ bara de tre scriptbevisade källorna: `spec.md`, `impact.md`,
623
+ `granskarblock.md`). En respec nollar `stopp`.
624
+ - `ESKALERA` / `INPUT_SAKNAS` → visa skälet och stanna, eller ett prisavgörande
625
+ val som kvarstår efter respec (källa `granskarblock.md`, orsak i
626
+ `stopp.orsak`, ADR-0118).
469
627
 
470
628
  ## Uppgift: `status`
471
629
 
@@ -624,12 +782,11 @@ dubbelkörning väntar på `döm`, precis som i produktion.
624
782
  `utvärdera rapport` (se avsnitten nedan). Argumentreglerna nedan gäller aldrig
625
783
  de orden; de klassificeras varken som modell eller post-id.
626
784
 
627
- **Flaggorna plockas bort först.** `--om`, `--hoppa-över` och `--körning
628
- <katalog>` (flaggan plus dess värde) lyfts ur argumentlistan INNAN skanningen
629
- nedan börjar, och räknas varken som modell eller post-id. Utan det steget
630
- stoppar svepets egen rad, som slutar med `--körning <katalog>`, skanningen
631
- omedelbart, noll modeller hittas, och kommandot faller på "Minst en modell
632
- krävs" trots att modellen står där.
785
+ **Flaggorna plockas bort först.** `--om` och `--hoppa-över` lyfts ur
786
+ argumentlistan INNAN skanningen nedan börjar, och räknas varken som modell
787
+ eller post-id. Utan det steget stoppar en flagga skanningen, noll modeller
788
+ hittas, och kommandot faller på "Minst en modell krävs" trots att modellen
789
+ står där.
633
790
 
634
791
  **Argumenttolkning.** Kvarvarande avslutande argument avgörs höger till vänster
635
792
  för att hitta GRÄNSEN mot post-id-listan: ett argument räknas som modell bara
@@ -668,34 +825,62 @@ Frågan gäller alltså kostnaden, inte risken att förlora data.
668
825
 
669
826
  1. **Underlag.** Som `speca` steg 1: rått önskemål eller gid — vid gid hämtas
670
827
  posten läsande via request-sync i huvudkonversationen, uppgiftens ENDA
671
- Asana-kontakt. Skapa `experiments/<slug>/` och `experiment.json` med
672
- ticketreferens, datum och `varianter` i deklarationsordning.
673
-
674
- Anteckna samtidigt postens `permalink_url` i `experiment.json` när gid-svaret
675
- bär det. Det kommer ur samma läsande hämtning som redan görs, så steget får
676
- ingen ny Asana-kontakt. Saknas fältet i svaret skrivs det inte, och rapporten
677
- visar då gid som ren text: Asanas permalänk bär ett workspace-gid som inte
678
- finns på experimentytan, så en konstruerad URL vore en gissning.
679
-
680
- **`experiment.json` skrivs läs-modifiera-skriv, aldrig från noll.** Saknas
681
- filen skapas den. Finns den redan läses den in FÖRST och uppdateras sedan:
682
- `varianter` slås ihop som UNION med bevarad deklarationsordning, de
683
- befintliga namnen först och invokationens nya namn efter, och en befintlig
684
- körnings `status` skrivs aldrig över av en process som inte äger just den
685
- körningen. En headless-process med en enmodellslista rör alltså bara sin
686
- egen modell. Utan regeln raderar `fable`-körningen en vecka senare `opus`
687
- och `sonnet` ur filen, och eftersom det är den här filen
688
- återupptagningsregeln läser (se Återupptagbart nedan) slutar ett återupptaget
689
- svep känna igen färdiga körningar och betalar om för dem. Skrivningen ska
690
- vara ATOMISK: skriv till en temporärfil i samma katalog och byt namn.
691
- `experiment.json` är den enda filen på experimentytan som delas mellan
692
- samtidiga processer utan den låsning `archive.mjs ny-korning` ger
693
- körningskatalogerna, så samtidighetsdisciplinen måste stå här i stället.
694
-
695
- 2. **Källmanifest EN gång.** Bygg manifestet som i `speca` steg 2 och
696
- återanvänd det byteidentiskt i ALLA varv. Anteckna manifest-hash i
697
- `experiment.json` (`ref-hash.mjs` på manifesttexten). Enda avsedda
828
+ Asana-kontakt. Skapa `experiments/<slug>/` och skriv underlaget till disk
829
+ med fasta namn: önskemålet ordagrant som `experiments/<slug>/onskemal.md`
830
+ och källmanifestet (steg 2) som `experiments/<slug>/kallmanifest.md`.
831
+ Namnen är fasta därför att `korning.mjs` (steg 4) läser dem därifrån och
832
+ bygger varje agentprompt av dem; ett underlag som bara finns i
833
+ konversationen når aldrig en headless-process.
834
+
835
+ Skapa sedan `experiment.json` med ticketreferens, datum och `varianter` i
836
+ deklarationsordning, alltid genom kommandot:
837
+
838
+ ```
839
+ node <skillkatalog>/scripts/archive.mjs experiment experiments/<slug> \
840
+ --satt request_id=<gid> --satt title=<titel> --satt permalink_url=<url> \
841
+ --satt datum=<YYYY-MM-DD> --satt sprak=<svenska|engelska> --satt manifest_sha256=<hash> \
842
+ --satt eskalerad=<true|false> --satt eskaleringsorsak=<text> \
843
+ --variant <m>...
844
+ ```
845
+
846
+ Nyckelnamnen är rapportens: `report.mjs` läser `request_id`, `title`,
847
+ `permalink_url` och `datum`, och `svep.mjs` läser `request_id` (med
848
+ `ticket` som reserv i äldre filer) till varje varvs REQUEST_ID. Äldre
849
+ filer med `ticket`/`titel` läses som förut men skrivs inte längre.
850
+ `manifest_sha256` (steg 2) och `eskalerad`/`eskaleringsorsak` (steg 3)
851
+ sätts när de är kända, i egna anrop av samma kommando; ett fält som redan
852
+ bär samma värde är no-op. `sprak` avläses ur önskemålets brödtext av
853
+ huvudkonversationen, precis som i `speca` steg 2; `korning.mjs` vägrar
854
+ starta utan den (den gissar aldrig språk, ADR-0114).
855
+
856
+ Anteckna samtidigt postens `permalink_url` (`--satt permalink_url=<url>`)
857
+ när gid-svaret bär det. Det kommer ur samma läsande hämtning
858
+ som redan görs, så steget får ingen ny Asana-kontakt. Saknas fältet i
859
+ svaret skrivs det inte, och rapporten visar då gid som ren text: Asanas
860
+ permalänk bär ett workspace-gid som inte finns på experimentytan, så en
861
+ konstruerad URL vore en gissning.
862
+
863
+ **`archive.mjs experiment` är den ENDA skrivaren av `experiment.json`.**
864
+ Ingen process, varken huvudkonversationen, startaren eller den inre
865
+ processen, skriver filen för hand. Kommandot skapar filen om den saknas
866
+ och läser annars in den först: `varianter` slås ihop som UNION med bevarad
867
+ deklarationsordning (befintliga namn först, invokationens nya efter), en
868
+ befintlig körnings `status` skrivs aldrig över av en process som inte äger just den
869
+ körningen, och skrivningen är ATOMISK under ett lås i experimentkatalogen. Skälet till att det är ett kommando: `experiment.json`
870
+ är den enda filen på experimentytan som delas mellan samtidiga processer,
871
+ och låset är det som gör parallella körningar (steg 4) säkra. Utan det
872
+ tappar två samtidiga processer varandras rader, vilket är exakt vad som
873
+ hände sonnet/k4 i IPC 14001 (#493).
874
+
875
+ 2. **Källmanifest EN gång.** Bygg manifestet som i `speca` steg 2, skriv det
876
+ till `experiments/<slug>/kallmanifest.md` (steg 1) och
877
+ återanvänd det byteidentiskt i ALLA varv. Anteckna manifest-hash med
878
+ `archive.mjs experiment experiments/<slug> --satt manifest_sha256=<hash>`
879
+ (`ref-hash.mjs` på manifesttexten). Enda avsedda
698
880
  skillnaden mellan varven är modellen.
881
+ Manifestet har sist sektionen `## Avgjorda val`: varje val som en människa
882
+ avgjort efter ett tidigare stopp, en rad var, ordagrant som svaret gavs;
883
+ `inga` om inga. Specifiern får aldrig flagga ett val som står där.
699
884
  3. **Eskaleringsvalet görs en gång** enligt `speca`:s regler och gäller alla
700
885
  varv, även eskaleringsvalet är en del av det som hålls konstant.
701
886
  Triggar valet: agenten blir `request-specifier-eskalerad` i stället för
@@ -705,157 +890,223 @@ Frågan gäller alltså kostnaden, inte risken att förlora data.
705
890
  över av varvets modell. Eskaleringsmekanismen kollapsar alltså medvetet
706
891
  under override, det är avsett, inte en lucka: `utvärdera` mäter en ren
707
892
  modellskillnad över alla tre rollerna och testar aldrig
708
- eskaleringsbeslutet i sig.
709
- 4. **Ett varv per modell i listan**, sekventiellt, i deklarationsordning.
710
- Varje varv är EN egen headless-process, och det är det som ger mätningen
711
- gratis. Ansvaret delas, eftersom en process aldrig kan känna sin egen
712
- slutkostnad:
713
-
714
- **Startaren** (huvudkonversationen, eller svepets loop) gör per modell:
715
- allokerar körningskatalogen med `archive.mjs ny-korning`, startar
716
- `claude -p --model sonnet --output-format json` med katalogen som explicit
717
- argument, väntar in processen, och skriver JSON-svaret rått till
718
- `<modell>/k<N>/korning.json`. Rapporten läser sedan `total_cost_usd`,
719
- `usage.input_tokens`, `usage.output_tokens`,
720
- `usage.cache_read_input_tokens`, `usage.cache_creation_input_tokens` och
721
- `subagent_stats.spawned` ur den filen.
722
-
723
- **Startaren tar tiden runt processen** och skriver `<modell>/k<N>/matning.json`
724
- med `startad`, `avslutad` (båda ISO 8601) och `wall_ms` (differensen).
725
- Filen skrivs ÄVEN när processen faller: en kraschad körning har en klocktid,
726
- och den är ofta det intressantaste talet i tabellen. Rapporten läser
727
- väggtiden därifrån, aldrig ur `korning.json`. Skälet är detsamma som för
728
- kostnaden: en process kan inte känna sin egen klocktid när arbetet ligger i
729
- bakgrundssubagenter, och svarets `duration_ms` räknar inte in väntan på dem.
730
- `korning.json` förblir headless-svaret rått, därav den egna filen.
731
-
732
- Startaren stämplar samtidigt körningens `state.json` med `proveniens`:
893
+ eskaleringsbeslutet i sig. Valet antecknas med
894
+ `archive.mjs experiment experiments/<slug> --satt eskalerad=<true|false> --satt eskaleringsorsak=<text>`;
895
+ det är därifrån `svep.mjs` läser `ESKALERAD:` till varje varvs prompt.
896
+ 4. **Ett varv per modell i listan**, startat av `svep.mjs`. Huvudkonversationen
897
+ förbereder steg 1 till 3 och kör sedan, även för en enda post och en enda
898
+ modell:
733
899
 
734
900
  ```
735
- node <skillkatalog>/scripts/archive.mjs proveniens <modell>/k<N> \
736
- --modell <varvets modellvärde> --konto <vilket Claude-konto som körde> \
737
- --underlag <sökväg till källmanifestet>
901
+ node <skillkatalog>/scripts/svep.mjs --repo-rot <abs> --konto <namn> \
902
+ --modell <modell> [--modell <modell>]... \
903
+ --exp experiments/<slug> [--exp experiments/<slug>]... \
904
+ [--tickets-parallellt 2] [--om | --hoppa-över]
738
905
  ```
739
906
 
740
- Kommandot räknar själv `skill_sha256` (över `SKILL.md` plus de fyra
741
- agentdefinitionerna) och `underlag_sha256`, läser `korning.json` och
742
- antecknar `fakturerade_modeller` (den kanoniserade nyckelmängden ur
743
- `modelUsage`, suffix som `[1m]` strippade — ADR-0099), och stämplar även varje
744
- `del-N/`. Utan den stämpeln går det inte att avgöra
745
- vad som skilde `k1` från `k2`, och en jämförelse över tid blir en
746
- gissning. Anropet står här som ett
747
- kommando och inte som en beskrivning med flit: ett steg som ålägger
748
- startaren en skrivning utan att peka ut vem som utför den är en skrivning
907
+ Varje varv startas av `svep.mjs` som EN `korning.mjs`-process, och det är
908
+ det som ger mätningen gratis. Parallellregeln: två tickets åt gången, alla
909
+ modeller för en ticket startar samtidigt, och nästa ticket startar först när en tickets samtliga
910
+ modeller är klara. Det är säkert därför att körningskatalogerna redan är
911
+ isolerade (`ny-korning` är atomisk, varje varv har egen katalog, manifest
912
+ och repo läses bara) och det enda delade tillståndet, `experiment.json`,
913
+ skrivs under lås (steg 1). Ansvaret delas, eftersom en process aldrig kan känna sin egen
914
+ slutkostnad:
915
+
916
+ **Startaren** är `svep.mjs`. Dess ansvar per varv, i en mening: allokera
917
+ körningskatalogen med
918
+ `archive.mjs ny-korning <modellkatalog> --request-id <gid eller slug>`
919
+ (kommandot skriver också skelettet till körningens `state.json`, se
920
+ Experimentytan; den inre processen skriver aldrig den filen från noll),
921
+ starta `korning.mjs` för varvet med körningskatalogen, experimentkatalogen,
922
+ repo-roten, request-id:t och modellen som argument (stderr till
923
+ `stderr.log`; `korning.json` skriver `korning.mjs` själv),
924
+ ta tiden runt processen och skriva
925
+ `<modell>/k<N>/matning.json` (`startad`, `avslutad`, `wall_ms`,
926
+ `exit_code`; skrivs ÄVEN när processen faller), stämpla `proveniens` i
927
+ körningens `state.json` med
928
+ `archive.mjs proveniens <modell>/k<N> --modell <varvets modellvärde> --konto <Claude-kontot som körde> --underlag <sökväg till kallmanifest.md>`
929
+ EFTER att `korning.json` skrivits, verifiera modellen mot `modelUsage`,
930
+ och sätta körningens status via `archive.mjs experiment`. Hur vart och ett
931
+ görs bor i scriptet och dess tester, inte här. Att stegen står som
932
+ kommandon är med flit (ADR-0094): ett steg som ålägger startaren en
933
+ skrivning utan att peka ut vem som utför den är en skrivning
749
934
  som inte blir av, och den syns först som tomma celler långt senare.
750
- Kör därför proveniens-stämpeln EFTER att `korning.json` skrivits, aldrig
751
- före — annars saknar stämpeln faktureringsdatan.
752
-
753
- **Modellverifiering görs av startaren mot faktureringen (ADR-0099),
754
- aldrig mot svansraden ensam.** När processen är klar: jämför
755
- `fakturerade_modeller` mot varvets begärda modell plus orkestratorns
756
- (sonnet). En modellnyckel som varken är varvets modell eller
757
- orkestratorns är en faktisk MODELL-avvikelse → körningen blir
758
- `"ofärdig"`. En MODELL-svansrad som säger något annat än vad
759
- `modelUsage` visar är en RAPPORTERINGSAVVIKELSE: den visas i rapporten
760
- och står kvar i `runs[]` (raden bär agentens uppgift i
761
- `model.uppgiven`), men den fäller ALDRIG ensam körningen — modeller är
762
- opålitliga källor om sin egen identitet, och att kasta en korrekt
763
- körning på en felskriven svansrad betalar om ett riktigt resultat.
764
- Modellvärdet `default` kör agenternas egna defaults och får lagligt
765
- producera flera modellnycklar: ingen mängdjämförelse görs där.
766
-
767
- **Den inre processen** känns igen på invokationen, aldrig på gissning: bär
768
- den `--körning`, är du den inre processen. Allokera då ingen katalog, starta
769
- ingen ny process, och kör stegen direkt mot den anvisade katalogen. Utan det
770
- stoppvillkoret läser den inre processen samma uppgift som startaren, startar
771
- ett varv till och dubblar notan.
772
-
773
- Den inre processen kör speca-stegen (dispatch, kontraktskontroll,
935
+
936
+ Fyra regler i den kedjan är kontrakt mot rapporten och arkivet och står
937
+ därför kvar här med sitt skäl:
938
+
939
+ - **Väggtiden bor i `matning.json`, aldrig i `korning.json`.** Rapporten
940
+ läser `total_cost_usd`, `usage.input_tokens`, `usage.output_tokens`,
941
+ `usage.cache_read_input_tokens`, `usage.cache_creation_input_tokens` och
942
+ `subagent_stats.spawned` ur `korning.json`, men väggtiden ur
943
+ `matning.json`: en process kan inte känna sin egen klocktid när arbetet
944
+ ligger i bakgrundsprocesser, och stegsvarens `duration_ms` räknar inte in väntan på dem.
945
+ `korning.json` bär summan över stegen, därav den egna filen. En kraschad körning har en klocktid, ofta det intressantaste
946
+ talet i tabellen, därför skrivs `matning.json` även vid krasch.
947
+ - **Proveniensen stämplas EFTER `korning.json`, aldrig före**, annars
948
+ saknar stämpeln faktureringsdatan. Kommandot räknar själv `skill_sha256`
949
+ (över `SKILL.md` plus de fyra agentdefinitionerna) och `underlag_sha256`,
950
+ läser `korning.json` och antecknar `fakturerade_modeller` (den
951
+ kanoniserade nyckelmängden ur `modelUsage`, suffix som `[1m]` strippade,
952
+ ADR-0099), och stämplar även varje `del-N/`. Utan den stämpeln går det
953
+ inte att avgöra vad som skilde `k1` från `k2`, och en jämförelse över tid
954
+ blir en gissning.
955
+ - **Modellverifiering görs av startaren mot faktureringen (ADR-0099),
956
+ aldrig mot svansraden ensam.** När processen är klar: jämför
957
+ `fakturerade_modeller` mot varvets begärda modell plus de två tolererade
958
+ avvikelserna, kundtextfixens `sonnet` och CLI:ns `haiku`-hjälpanrop. En
959
+ modellnyckel som varken är varvets begärda modell, kundtextfixens
960
+ `sonnet` eller CLI:ns `haiku`-hjälpanrop är en faktisk MODELL-avvikelse →
961
+ körningen blir `"ofärdig"`. Startaren tolererar `haiku`-nyckeln i summan;
962
+ taket på 200 output-tokens, som skiljer hjälpanropet från en riktig
963
+ körning, prövas per steg av `korning.mjs`. Resultatet ligger i
964
+ `korning.json` som `modellverifiering` (`ok` plus `avvikelser`, där varje
965
+ rad namnger sitt steg), så avvikelsen går att peka ut i det steg den
966
+ uppstod. En MODELL-svansrad som säger något annat än vad
967
+ `modelUsage` visar är en RAPPORTERINGSAVVIKELSE: den visas i rapporten
968
+ och står kvar i `runs[]` (raden bär agentens uppgift i
969
+ `model.uppgiven`), men den fäller ALDRIG ensam körningen — modeller är
970
+ opålitliga källor om sin egen identitet, och att kasta en korrekt
971
+ körning på en felskriven svansrad betalar om ett riktigt resultat.
972
+ Modellvärdet `default` kör agenternas egna defaults och får lagligt
973
+ producera flera modellnycklar: ingen mängdjämförelse görs där.
974
+ - **Statusen sätts via `archive.mjs experiment`**, aldrig genom att skriva
975
+ filen: exit ≠ 0 eller en faktisk MODELL-avvikelse ger `"ofärdig"` med
976
+ `--orsak`; har den inre processen redan satt `"stopp"`, `"väntar på dom"`
977
+ eller `"ofärdig"` behålls den; annars `"klar"`.
978
+
979
+ **`korning.mjs` är den inre processen: ett script utan modellturer.** Det
980
+ bygger prompterna ur `onskemal.md`, `kallmanifest.md` och `experiment.json`,
981
+ startar varje agentsteg som en egen process
982
+ `claude -p --agent <agent> --output-format json --max-turns <tak> [--model <modell>]`
983
+ med prompten på stdin, sparar råsvaret under `k<N>/steg/`, kör precheck,
984
+ `kontroll.mjs` och compare-scores, arkiverar via `archive.mjs` och skriver
985
+ `korning.json` som summan. Det gör ingen Asana-hämtning och bygger inget
986
+ nytt manifest. Buntens delar körs parallellt (två åt gången) och de två
987
+ scorerkörningarna samtidigt (#500).
988
+
989
+ `korning.mjs` kör speca-stegen (dispatch, kontraktskontroll,
774
990
  arkivering) och därefter bedöm-stegen (analys, poäng,
775
991
  ankarlösning, dubbelkörning enligt bedöm) mot den anvisade katalogen, utan
776
992
  godkännandegrind, och rör ingenting utanför den. Är modellen `default`
777
993
  används ordinarie promptmallar och agenternas defaults, ingen override. Är
778
- den ett modellnamn får varje Agent-anrop modell-override till den på ALLA
779
- tre agenter; agentdefinitionerna röres aldrig, effort tas ur definitionen
780
- som vanligt. Overriden verifieras av STARTAREN mot `modelUsage` (se
781
- modellverifieringen ovan) — svansraden arkiveras men avgör aldrig ensam.
782
- `approval` förblir `null`, `state.json`
783
- får `"experiment": true`. `REQUEST_ID` och `--expect-id` använder alltid
784
- ticketens gid/slug, aldrig katalogens namn.
785
-
786
- Orkestratorn pinnas till sonnet i ALLA körningar. Dess kostnad blir då en
787
- konstant som går att räkna bort, och `modelUsage` i JSON-svaret skiljer den
788
- från agenternas kostnad utan extra arbete.
789
-
790
- Subagentdöd, kontraktsbrott (kvarstående BROTT efter omdispatch) eller
994
+ den ett modellnamn får varje agentprocess modell-override till den på ALLA
995
+ tre agenter; overriden är `--model` på processen, aldrig en promptrad, och
996
+ agentdefinitionerna röres aldrig, effort tas ur definitionen
997
+ som vanligt. Overriden verifieras per steg mot `modelUsage` i råsvaret
998
+ (ADR-0099, se modellverifieringen ovan), svansraden arkiveras men avgör
999
+ aldrig ensam. `approval` förblir `null`, `state.json`
1000
+ får `"experiment": true` ur skelettet `ny-korning` skrev. `REQUEST_ID`
1001
+ och `--expect-id` använder alltid ticketens gid/slug, aldrig katalogens
1002
+ namn.
1003
+
1004
+ **Bunt i den inre processen:** ger specifiern flera specar läggs de som
1005
+ `del-1/ … del-N/` under körningskatalogen med `bundle` satt (buntregeln i
1006
+ Experimentytan), aldrig del 1 i roten; varje del får sin egen `spec.md`,
1007
+ `granskarblock.md` och `state.json`, och speca steg 6 (precheck,
1008
+ omdispatch per del, `stopp` vid kandidat) körs per del. Faller en del på
1009
+ kvarstående brist blir körningen `"ofärdig"`; övriga delar körs ändå
1010
+ klart så arkivet visar vad som gick att bedöma.
1011
+
1012
+ **DELA_UPP körs inte autonomt av `korning.mjs` (v1).** Utfallet arkiveras
1013
+ och körningen får status `stopp` med orsak; delningsanalysen saknar
1014
+ promptmall i den här skillen, och ett script får inte improvisera en
1015
+ (ADR-0113).
1016
+
1017
+ Ett fallet agentsteg, kontraktsbrott (kvarstående BROTT efter omdispatch) eller
791
1018
  en faktisk MODELL-avvikelse (obegärd nyckel i `modelUsage`, aldrig en
792
1019
  ensam svansradsavvikelse), i vilket steg det än inträffar, sätter
793
- körningens `status` till `"ofärdig"` i `experiment.json`.
794
-
795
- 5. **DELA_UPP körs autonomt.** Ett DELA_UPP-utfall följer bedöms ordinarie
796
- delningsregler men utan paus för granskare: delkataloger
1020
+ körningens `status` till `"ofärdig"` via `archive.mjs experiment`
1021
+ (`experiments/<slug> --korning <modell>/k<N> --status ofärdig --orsak <vad som föll>`),
1022
+ aldrig genom att skriva filen.
1023
+
1024
+ 5. **DELA_UPP i experimentet.** Ett DELA_UPP-utfall följer bedöms ordinarie
1025
+ delningsregler men utan paus för granskare (i v1 kör `korning.mjs` inte
1026
+ delningen själv, se steg 4; reglerna här gäller när den körs):
1027
+ delkataloger
797
1028
  `<modell>/k<N>/del-1/ … del-N/` med `split` satt, förälderns `spec.md` +
798
- `granskarblock.md` kopierade in, ny analys + poäng per del.
1029
+ `granskarblock.md` kopierade in, ny analys + poäng per del. Delas en
1030
+ buntdel blir det `del-N/del-M/` under den delen, aldrig ett nytt
1031
+ syskon i roten (`bundle` och `split` sätts aldrig på samma katalog).
799
1032
  En del utan tak blir `OVER_SKALAN` och delas aldrig vidare. Övriga
800
1033
  stopputfall (`TILLBAKA_TILL_SPEC`, `KONCEPT`, `DIAGNOS`, `ESKALERA`,
801
- `INPUT_SAKNAS`) kräver människa eller kund: arkivera utfallet, avbryt det
1034
+ `INPUT_SAKNAS`) kräver människa eller kund: arkivera utfallet, sätt
1035
+ körningens status till `"stopp"` via `archive.mjs experiment`, avbryt det
802
1036
  varvet, gör det synligt i rapporten när den körs, och övriga körningar i
803
1037
  listan fortsätter oberoende.
804
1038
 
805
1039
  **OLIKA väntar på dom.** Skriver dubbelkörningen OLIKA i ett varv:
806
1040
  arkivera enligt bedöms regler (spärren står), sätt körningens status till
807
- `"väntar på dom"` i `experiment.json`, synligt i rapporten när den körs.
1041
+ `"väntar på dom"` via `archive.mjs experiment`, synligt i rapporten när
1042
+ den körs.
808
1043
  Domen är människans även i experimentet: `döm` körs mot körningskatalogen
809
1044
  när granskaren vill (adresserad via sin sökväg, se `döm`), och ingen dom
810
1045
  simuleras av uppgiften. Domen i sig skriver inte om `experiment.json`,
811
- det gör `utvärdera` självt, nästa gång det körs för samma post (se
1046
+ det gör `svep.mjs` (via `archive.mjs experiment`) nästa gång `utvärdera`
1047
+ körs för samma post (se
812
1048
  Återupptagbart i avsnittet Batch nedan för hur och när statusen går
813
1049
  till `"klar"` och rapporten kan visa utfallet). Övriga körningar i listan
814
1050
  fortsätter oberoende oavsett när, eller om, domen fälls.
815
1051
 
816
1052
  6. **Rapport.** BLUF i chatten: klart eller ofärdigt per körning, stopputfall,
817
- och sökvägen till `experiments/<slug>/`. Nämn att `utvärdera rapport` finns
1053
+ och sökvägen till `experiments/<slug>/`. Tabellen per körning kommer ur
1054
+ `node <skillkatalog>/scripts/archive.mjs bluf <modell>/k<N>` (rekursivt
1055
+ över delarna, rotraden för en bunt lyder `bunt (N delar)`) och klistras in
1056
+ som den är, aldrig handskriven. Nämn att `utvärdera rapport` finns
818
1057
  när underlaget ska läsas sida vid sida. Ingen skrivning mot Asana.
819
1058
 
820
1059
  ### Batch: flera poster
821
1060
 
822
1061
  `utvärdera <post1> <post2> … <modell…>` kör varje ticket och modell i en EGEN
823
- headless-process, sekventiellt:
1062
+ `korning.mjs`-process, parallellt enligt `svep.mjs`: två tickets åt gången, alla
1063
+ modeller för en ticket samtidigt, och nästa ticket startar när en ticket är
1064
+ helt klar (steg 4). Raden `svep.mjs` kör per varv:
824
1065
 
825
1066
  ```
826
- claude -p --model sonnet --output-format json "/request utvärdera <gid> <modell> --hoppa-över --körning <katalog>"
1067
+ node <skillkatalog>/scripts/korning.mjs --katalog <k<N>> --exp <experiments/<slug>> --repo-rot <abs> --request-id <gid> --modell <modell>
827
1068
  ```
828
1069
 
829
- Orkestratorn är huvudloopens modell och pinnas till sonnet i headless:
830
- agenternas modeller styrs av definition plus override och verifieras av
831
- startaren mot `modelUsage` i `korning.json` (ADR-0099) —
832
- aldrig av huvudloopens val, och aldrig av svansraden ensam.
1070
+ Underlaget läses från disk av `korning.mjs`; ingenting går via stdin till
1071
+ orkestreringen, bara till agentprocesserna. Vilka rader varje agentprompt bär
1072
+ (ÖNSKEMÅL, KÄLLOR, REPO-ROT, DEPS, SPRÅK, RUBRIKER, ESKALERAD, REQUEST_ID) bor
1073
+ i scriptet och dess tester.
833
1074
 
834
- **Raden bär ALLTID `--om` eller `--hoppa-över`, och väljer `--hoppa-över`.**
835
- Det är inte en stilregel utan det som gör svepet möjligt: en `claude -p`-process
836
- kan inte svara på en fråga, så en rad utan flagga skulle hänga eller gissa.
837
- `--hoppa-över` är dessutom precis återupptagningens semantik, ett avbrutet svep
838
- som startas om plockar upp där det stannade i stället för att betala om för
839
- allt färdigt.
1075
+ Det finns ingen orkestratormodell: agenternas modeller styrs av definition plus
1076
+ `--model`-override och verifieras per steg mot `modelUsage` i råsvaret, aldrig
1077
+ av svansraden ensam.
840
1078
 
841
- `--körning <katalog>` pekar ut den körningskatalog som `archive.mjs ny-korning`
842
- redan allokerat åt den ticketen och modellen. Den inre processen arkiverar
1079
+ `--om` och `--hoppa-över` är svepets egna flaggor; `korning.mjs` frågar aldrig
1080
+ något och kan därför inte hänga. `--hoppa-över` är dessutom precis
1081
+ återupptagningens semantik, ett avbrutet svep som startas om plockar upp där
1082
+ det stannade i stället för att betala om för allt färdigt.
1083
+
1084
+ `--katalog` pekar ut den körningskatalog som `archive.mjs ny-korning`
1085
+ redan allokerat åt den ticketen och modellen. `korning.mjs` arkiverar
843
1086
  bara dit, aldrig någon annanstans.
844
1087
 
845
- Svepets loop är startaren och har startarens hela ansvar per rad: den tar tiden
846
- runt `claude -p`-anropet och skriver `matning.json`, och den kör
847
- `archive.mjs proveniens` mot körningskatalogen när processen är klar.
1088
+ Svepets loop är startaren, och den heter `svep.mjs`: den bär startarens hela
1089
+ ansvar per rad (`ny-korning`, tidtagningen som skriver `matning.json`,
1090
+ `archive.mjs proveniens` mot körningskatalogen när processen är klar,
1091
+ modellverifieringen och status via `archive.mjs experiment`), se steg 4.
848
1092
 
849
- - **En ticket = ett färskt kontextfönster.** Allt tillstånd bor i arkivet på
850
- disk; huvudsessionen samlar bara exitstatus och en radssammanfattning per
851
- ticket. `/clear` mellan tickets behövs inte — processgränsen är
1093
+ - **En ticket, en modell = en `korning.mjs`-process; varje agentsteg är ett
1094
+ eget kontextfönster.** Allt tillstånd bor i arkivet på
1095
+ disk; `svep.mjs` skriver en rad per ticket och modell när den startar och
1096
+ när den slutar (status, väggtid, USD) och en sluttabell, och huvudsessionen
1097
+ samlar bara det. `/clear` mellan tickets behövs inte — processgränsen är
852
1098
  motsvarigheten. Headless-körningen förutsätter att uppgiften kan köra utan
853
1099
  interaktiva permission-frågor, vilket håller: den läser repot och skriver
854
- enbart under `.haus/requests/` — plus EN läsande Asana-hämtning per ticket
855
- (steg 1), så MCP-behörigheten för läsning måste täcka headless-körning.
856
- - **Kör vidare vid fel.** En ticket vars gid inte går att hämta eller vars
857
- process kraschar noteras i sammanfattningen; resten av listan fortsätter.
858
- - **Återupptagbart:** innan en körning startas kontrolleras arkivet, styrt av
1100
+ enbart under `.haus-workflow/requests/`. Asana-hämtningen gjordes redan i
1101
+ steg 1 av huvudkonversationen, som skrev `onskemal.md`, så `korning.mjs`
1102
+ behöver ingen MCP-kontakt alls.
1103
+ - **Kör vidare vid fel.** En ticket vars underlag saknas (`onskemal.md`,
1104
+ `kallmanifest.md` eller `experiment.json` med `request_id`) märks och hoppas
1105
+ över; en vars process kraschar blir `"ofärdig"` med orsak. Resten av svepet
1106
+ fortsätter, och `svep.mjs` avslutar med exit 1 när någon körning blev
1107
+ `"ofärdig"`, annars 0.
1108
+ - **Återupptagbart:** det gör `svep.mjs`: innan en körning startas
1109
+ kontrolleras arkivet, styrt av
859
1110
  körningens `status` i `experiment.json`. `"klar"` och `"stopp"` hoppas
860
1111
  över med en notis. `"ofärdig"` är avbruten data, inget resultat värt att
861
1112
  skydda: den utlöser aldrig omkörningsfrågan, utan körs om automatiskt som
@@ -864,16 +1115,16 @@ runt `claude -p`-anropet och skriver `matning.json`, och den kör
864
1115
  `"väntar på dom"` OMVÄRDERAS i stället för att hoppas över, det är den
865
1116
  enda platsen som är aktören för den statusen: kör `archive.mjs sparr` på
866
1117
  körningskatalogen. Exit 0 betyder att domen är fälld: sätt körningens
867
- status till `"klar"` och fortsätt där den stannade (rapporten kan köras
868
- separat när alla körningar för posten är klara). Exit 1 betyder att
869
- den fortfarande väntar: notera det och gå vidare till nästa ticket.
870
- `döm` fäller domen men rör aldrig `experiment.json` (se `döm`), det är
871
- bara genom att `utvärdera` körs igen för samma post som statusen
872
- uppdateras. Omkörningsfrågan (se `utvärdera` ovan) gäller alltså bara
1118
+ status till `"klar"` via `archive.mjs experiment` och fortsätt där den
1119
+ stannade (rapporten kan köras separat när alla körningar för posten är
1120
+ klara). Exit 1 betyder att den fortfarande väntar: notera det och gå vidare
1121
+ till nästa varv. `döm` fäller domen men rör aldrig `experiment.json` (se
1122
+ `döm`), det är bara genom att `utvärdera` körs igen för samma post som
1123
+ statusen uppdateras. Omkörningsfrågan (se `utvärdera` ovan) gäller alltså bara
873
1124
  `"klar"`-körningar: det är resultat värda att skydda mot ett fingerfel,
874
- en ofärdig körning har inget att skydda. Körningar är billiga relativt
875
- risken att lappa ihop halva resultat. Ett svep över många tickets kan
876
- därmed fortsätta i en senare session utan onödiga omkörningar.
1125
+ en ofärdig körning har inget att skydda. Körningar är billiga
1126
+ relativt risken att lappa ihop halva resultat. Ett svep över många tickets
1127
+ kan därmed fortsätta i en senare session utan onödiga omkörningar.
877
1128
 
878
1129
  ### Sammanställning
879
1130
 
@@ -923,8 +1174,11 @@ människans, gjord på erfarenhet av systemet. Rapporten visar spridningen.
923
1174
 
924
1175
  ## Mekaniska script: `scripts/`
925
1176
 
926
- Nio deterministiska script bor i katalogen: de fem kontrollerna nedan,
927
- `archive.mjs` som äger arkivets kataloger och spärrar, `report.mjs` som
1177
+ Tolv deterministiska script bor i katalogen: de fem kontrollerna nedan,
1178
+ `kontroll.mjs` som kör scorerns kontrollkedja i en process,
1179
+ `archive.mjs` som äger arkivets kataloger, spärrar och `experiment.json`,
1180
+ `svep.mjs` som är utvärderas startare,
1181
+ `korning.mjs` som är utvärderas inre process, `report.mjs` som
928
1182
  renderar modellrapporten (se `utvärdera rapport`), `lib.mjs` med de delade
929
1183
  byggstenarna och `ref-hash.mjs` för stabil hashning. Kontrollerna ersätter
930
1184
  modellkörningar där kontrollen är ren form,
@@ -935,7 +1189,17 @@ referensfiler vars regler det speglar och VARNAR när en katalogbump ändrat dem
935
1189
  - `scripts/precheck.mjs <arkivkatalog> [--impact]` — formkontroll FÖRE dyra
936
1190
  dispatcher: rubriker, exakt ett specblock, 2–5 punkter, förbjuden vokabulär,
937
1191
  granskarblocksrader, state.json-fält. Skiljer på BRISTER (exit 1, stoppar)
938
- och VARNING (heuristik — visas, stoppar inte).
1192
+ och VARNING (heuristik — visas, stoppar inte). Rubrikerna matchas via
1193
+ aliastabellen i `lib.mjs` på svenska eller engelska; rubriker ur båda
1194
+ språken i samma spec är en BRIST. Kandidatformen känns igen på markörraden
1195
+ (`**Koncept-kandidat**`/`**Diagnos-kandidat**`, eller de engelska
1196
+ motsvarigheterna) och skrivs som `KANDIDAT: KONCEPT` eller
1197
+ `KANDIDAT: DIAGNOS` på stdout, alltid, även vid exit 1; en kandidat med en
1198
+ Lösning är en BRIST. Med `--impact` läses etiketten på analysens rad
1199
+ `**Avvikelse mot specen:**` och skrivs som `AVVIKELSE: ingen`,
1200
+ `AVVIKELSE: precisering` eller `AVVIKELSE: motsägelse`; en rad utan
1201
+ etikett är en BRIST. Orkestratorn läser kandidat och avvikelse ur de
1202
+ raderna, aldrig ur filerna själv.
939
1203
  - `scripts/resolve-anchors.mjs <run.json> <spec.md> [impact.md]` — löser
940
1204
  beläggsankare (`belagg_ankare`/`ankare`) till ordagranna källblock, körs
941
1205
  FÖRE contract-check. Exit 0 = allt löst (eller inga ankare i filen), 1 =
@@ -950,6 +1214,16 @@ referensfiler vars regler det speglar och VARNAR när en katalogbump ändrat dem
950
1214
  INPUT_SAKNAS kräver saknas-lista i stället för frågerader. Ett olöst ankare
951
1215
  (belägg saknas trots `belagg_ankare`, eller `ankare` utan `citat`) är
952
1216
  dispatcherns fel, inte agentens: exit 2 med hänvisning till resolve-anchors.mjs.
1217
+ Stegschemat: varje `bracket.steps[i]` (även per alternativ) är exakt
1218
+ `{anchor, relation, grund}` med `relation` ur `storre`, `mindre`, `lika`;
1219
+ egna nycklar och tomma grunder är BROTT. Motsägelsegrinden: ges `impact.md`
1220
+ och analysens `**Avvikelse mot specen:**` bär `motsägelse` är varje annat
1221
+ utfall än TILLBAKA_TILL_SPEC ett BROTT. Prövar dessutom inringningens
1222
+ konsistens (golv före tak, between angränsande och inuti golv–tak, poäng
1223
+ mellan between-paret, relation mot poäng; poäng PÅ ett ankare trots
1224
+ storre/mindre är VARNING bara vid avrundningskollaps), att `type` är en av
1225
+ skalans fem typer, och undantar svartlistade ord som står i specen
1226
+ (ADR-0115).
953
1227
  - `scripts/check-lasta.mjs "<LÄSTA-innehåll>" <spec.md> <impact.md>` —
954
1228
  kontrollerar scorerns läs-scope: LÄSTA-raden ska bära underlagens två
955
1229
  sökvägar och får därutöver bara bära filer under skillens egen katalog.
@@ -960,8 +1234,75 @@ referensfiler vars regler det speglar och VARNAR när en katalogbump ändrat dem
960
1234
  `LIKA_TOLERANS` (poängskillnad ≤ 0,5 med samma outcome, oavsett confidence,
961
1235
  och ankarband som överlappar eller angränsar; exit 0, verdiktraden pekar ut
962
1236
  körningen med högst poäng) eller `OLIKA` + diffrader; exit 0/1, exit 2 =
963
- ojämförbara körningar (dispatcherfel, aldrig LIKA). Scriptet kräver mode
964
- `pricing`; guard-jämförelser är en medveten utbyggnad i guard-steget.
1237
+ ojämförbara körningar (dispatcherfel, aldrig LIKA). Diffar även de inre
1238
+ stegen: samma ankare i `bracket.steps` med `storre` i ena körningen och
1239
+ `mindre` i den andra ger diffraden
1240
+ `bracket.steps[A<n>].relation: "mindre" ↔ "storre"`, en hård diff som
1241
+ aldrig faller under toleransen; golv- och takankare i någon av körningarna
1242
+ undantas, de flippar av konstruktion när banden angränsar. Scriptet kräver
1243
+ mode `pricing`; guard-jämförelser är en medveten utbyggnad i guard-steget.
1244
+ Angränsning avgörs av skalans ordning (lib.mjs ANKARE, A17 mellan A12 och
1245
+ A13), och utöver poäng och ankare diffas `type` (toleransrad), antalet
1246
+ `conditions` (hård diff) och `alternatives` per label (ADR-0115).
1247
+ - `scripts/archive.mjs <kommando> <katalog> [flaggor]`: arkivets mekanik
1248
+ (fullständig lista i scriptets huvud): `add-run`, `next-n`, `sparr`,
1249
+ `utslag`, `set-status`, `kalibrering`, `ny-korning`, `proveniens`, samt
1250
+ `stopp <katalog> --utfall KONCEPT|DIAGNOS|TILLBAKA_TILL_SPEC --kalla spec.md|impact.md`
1251
+ (skriver `state.stopp` efter att ha kontrollerat att källan bär utfallet;
1252
+ `--kalla impact.md` sätter status `assessed`, `--kalla spec.md` rör inte
1253
+ status), `respec <katalog>` (lägger undan `impact.md` som
1254
+ `impact-superseded-<datum>.md`, återställer status `spec_draft`,
1255
+ `approval`, `granskarutslag` och `stopp`, rör aldrig `runs[]`) och
1256
+ `bluf <katalog>` (markdown-tabellen `| post | poäng | utfall | konfidens | anm |`
1257
+ över katalogen och dess `del-N/` rekursivt, bunt-roten som
1258
+ `bunt (N delar)`; värdena ur filerna, aldrig ur prosa). `add-run` läser
1259
+ `effort:` ur agentdefinitionen när `--effort` utelämnas och kanoniserar
1260
+ `--model`; `set-status priced` vägrar när `stopp` är satt.
1261
+ `experiment <experimentkatalog> [--satt nyckel=värde]... [--variant <modell>]... [--korning <modell>/k<N> --status <klar|ofärdig|stopp|väntar på dom> [--orsak <text>]]`
1262
+ är den ENDA skrivaren av `experiment.json` (se `utvärdera` steg 1): låst
1263
+ läs-modifiera-skriv med atomisk skrivning, katalog och fil skapas om de
1264
+ saknas. `--satt` skriver toppnivåfält append-only (samma värde är no-op,
1265
+ ett annat värde är exit 2; `eskalerad` tolkas som boolean), `--variant` är
1266
+ UNION med bevarad ordning, och `--korning` kräver `--status` ur de fyra
1267
+ statusliteralerna, rör ingen annan körning och kräver att katalogen
1268
+ `<modell>/k<N>` finns. Exit 0 ok, 2 användnings- eller tillståndsfel.
1269
+ - `scripts/svep.mjs --repo-rot <abs> --konto <namn> --modell <m>... --exp experiments/<slug>... [--tickets-parallellt 2] [--om | --hoppa-över]`:
1270
+ utvärderas startare (se `utvärdera` steg 4 och Batch). Kör två tickets åt
1271
+ gången med alla modeller per ticket samtidigt, nästa ticket när en är helt
1272
+ klar, och gör per varv `ny-korning`, starten av `korning.mjs`,
1273
+ `stderr.log`, `matning.json`, `proveniens`,
1274
+ modellverifiering mot `modelUsage` och status via `archive.mjs experiment`.
1275
+ Återupptar ur `experiment.json`: `klar` och `stopp` hoppas utan `--om`,
1276
+ `väntar på dom` prövas med `sparr`, `ofärdig` körs om. En `--exp` utan
1277
+ `onskemal.md`, `kallmanifest.md` eller `experiment.json` med `request_id` märks
1278
+ och hoppas över. Exit 0 när ingen körning blev `ofärdig`, 1 annars, 2 vid
1279
+ användningsfel.
1280
+ - `scripts/korning.mjs --katalog <abs k<N>> --exp <abs experimentkatalog> --repo-rot <abs> --request-id <gid eller slug> --modell <modell|default> [--claude <kommando>] [--delar-parallellt 2]`:
1281
+ utvärderas inre process (se `utvärdera` steg 4), ett script utan
1282
+ modellturer. Bygger prompterna ur `onskemal.md`, `kallmanifest.md` och
1283
+ `experiment.json`, där `sprak` är OBLIGATORISK: saknas den skrivs en
1284
+ instruktion på stderr och scriptet avslutar med exit 2 innan någon prompt
1285
+ byggs (ADR-0114). Varje agentsteg är en egen `claude -p --agent`-process med
1286
+ råsvaret under `k<N>/steg/`, och kontrollerna (precheck, `kontroll.mjs`,
1287
+ compare-scores) plus arkiveringen (`archive.mjs`) är scriptanrop.
1288
+ Buntens delar körs två åt gången och scorerns två körningar samtidigt;
1289
+ `DELA_UPP` körs inte autonomt i v1 utan ger status `stopp` med orsak.
1290
+ `korning.json` är summan över stegen (`korningsmotor`, `steg`,
1291
+ `modellverifiering` plus rapportens vanliga nycklar). Exit 0 när körningen
1292
+ gick igenom, 1 när minst en del blev ofärdig, 2 vid användningsfel.
1293
+ - `scripts/kontroll.mjs <svar.txt> <spec.md> <impact.md> --expect-id <id> --ut <ren.json> [--fixa-kundtext] [--claude <kommando>] [--fix-modell sonnet] [--fix-fil <rå.json>]`:
1294
+ scorerns kontrollkedja i EN process (se `bedöm` steg 5): svansrader,
1295
+ JSON-extraktion, deterministiska reparationer, sedan check-lasta,
1296
+ resolve-anchors och contract-check i EN process. Reparerar det dispatchern
1297
+ redan vet: `request_id`, versaler och gemener på `outcome`, `mode`,
1298
+ `confidence` och `bracket.steps[].relation`, sträng till tal på `score`, och
1299
+ LÄSTA-radens sökvägsform. Reparationerna rör aldrig poäng, utfall eller
1300
+ belägg, och varje reparation skrivs som en `REPARERAT:`-rad som anroparen
1301
+ bokför i runs-radens `note`. `--fixa-kundtext` skriver om ett
1302
+ protokollterm-brott i ett rent kundtextfält med en billig modell i stället
1303
+ för att kosta en hel scorerkörning, högst en runda. stdout bär `MODELL:`,
1304
+ `LÄSTA:`, ev. `AVVIKELSE:`, `REPARERAT:`-rader och sist verdiktet. Exit 0 =
1305
+ kedjan håller, 1 = BROTT (omdispatch enligt ADR-0092), 2 = användningsfel.
965
1306
 
966
1307
  Kontrollerna bedömer aldrig innehåll och returnerar aldrig protokollutfall —
967
1308
  en post som passerar kan fortfarande stoppas av protokollen själva, och det är
@@ -983,13 +1324,35 @@ aldrig vidare som om kontrollen passerat.
983
1324
  <manifest med absoluta sökvägar, eller "inga — allt blir antaganden">
984
1325
 
985
1326
  REPO-ROT: <absolut sökväg eller "saknas">
1327
+ DEPS: <installerade | saknas>
1328
+ SPRÅK: <svenska | engelska>
1329
+ RUBRIKER:
1330
+ **Problem**
1331
+ **Lösning**
1332
+ **När det är klart**
1333
+ **Ingår inte**
1334
+ **Utseende**
1335
+ **Kräver**
1336
+ **Villkor**
986
1337
  ESKALERAD: <ja|nej>
987
1338
  ```
988
1339
 
1340
+ `RUBRIKER:` är skelettet på specens språk, literalerna ordagrant. Det
1341
+ svenska står ovan; vid `SPRÅK: engelska` tas de sju raderna ur
1342
+ rubriktabellen i `request-write-spec/references/specformat.md`
1343
+ (Problem, Solution, Done when, Not included, Appearance, Requires,
1344
+ Conditions). Fotnot: en kandidat ersätter Lösning och När det är klart med
1345
+ markörraden (`**Koncept-kandidat**` eller `**Diagnos-kandidat**`, på
1346
+ engelska `**Concept candidate**` eller `**Diagnosis candidate**`), det
1347
+ avgör specifiern själv enligt skillen.
1348
+
989
1349
  `request-assessor`:
990
1350
 
991
1351
  ```
992
1352
  REPO-ROT: <absolut sökväg>
1353
+ DEPS: <installerade | saknas>
1354
+ KÄLLOR:
1355
+ <kallmanifest.md ordagrant, eller "inga">
993
1356
 
994
1357
  SPEC (godkänd):
995
1358
  <spec.md bytetroget>