@haus-tech/haus-workflow 5.0.1 → 5.2.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.
@@ -86,8 +86,8 @@ med flera specar går inte att bedöma.
86
86
  ```
87
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
@@ -133,13 +133,37 @@ omdispatch-regeln i dispatch-kontraktet.
133
133
  "approval": null,
134
134
  "granskarutslag": [],
135
135
  "note": null,
136
+ "stopp": null,
137
+ "respec_varv": 0,
138
+ "coverage_post": null,
136
139
  "runs": []
137
140
  }
138
141
  ```
139
142
 
140
143
  - `bundle` = specar ur samma önskemål (sätts av speca vid bunt).
141
144
  `split` = delposter ur ett DELA_UPP-utfall (sätts av bedöm). Båda formen
142
- `{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.
143
167
  - `approval` skrivs av grinden i bedöm:
144
168
  `{ "source": "asana" | "manual", "by": "<namn, krävs vid manual>",
145
169
  "status_read": "<värde>", "date": "YYYY-MM-DD" }`.
@@ -226,17 +250,23 @@ Modellkörningar från `utvärdera` bor i ett eget träd och är aldrig prispost
226
250
  ├── <modell>/ `default` som modellvärde ger katalognamnet
227
251
  │ `baseline/`
228
252
  │ ├── k1/
229
- │ ├── 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)
230
257
  │ ├── k1/matning.json startarens egen mätning: startad, avslutad, wall_ms
231
258
  │ ├── k2/ senare körning av SAMMA modell
232
- │ └── 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
233
261
  └── <annan modell>/k1/ …
234
262
  ```
235
263
 
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.
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).
240
270
  - **Ingen körning skrivs någonsin över.** Värdet i en gammal körning är att
241
271
  kunna jämföra samma modell före och efter en ändring i skillen eller
242
272
  agentprompterna; skriver en ny körning över den är den jämförelsen borta för
@@ -245,21 +275,40 @@ Modellkörningar från `utvärdera` bor i ett eget träd och är aldrig prispost
245
275
  bytetroget (spec.md, granskarblock.md, impact.md, pricing-JSON, state.json,
246
276
  `runs[]`-reglerna), så `precheck.mjs` och `contract-check.mjs` fungerar
247
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.
248
290
  - **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
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
255
300
  experimentresultat kan inte förväxlas med en prispost.
256
301
  - Katalognamnet `experiments` är reserverat: en spec vars rubrik skulle sluggas
257
302
  till `experiments` får suffixet `-post`.
258
303
  - `experiment.json` bär `varianter: []` i deklarationsordning och `status` per
259
304
  körning: `"klar"`, `"ofärdig"`, `"stopp"`, `"väntar på dom"`. Literalerna
260
- skrivs exakt så, mellanslag och diakritiska tecken inkluderade. Äldre filer
305
+ skrivs exakt så, mellanslag och diakritiska tecken inkluderade. Startraden
306
+ (`"ofärdig"`, "startad, inte avslutad") bär dessutom startarens `pid`
307
+ medan körningen pågår; slutstatusen ersätter raden och tar bort den
308
+ (#516). Äldre filer
261
309
  med `baselinemodell` och `kandidatmodell` läses utan fel; nya skrivningar
262
- använder bara `varianter`.
310
+ använder bara `varianter`. Filen skrivs BARA av `archive.mjs experiment`
311
+ (se `utvärdera` steg 1), aldrig för hand.
263
312
  - `experiments/` ärver gitignore-skyddet från `.haus-workflow/requests/`.
264
313
 
265
314
  ## Uppgift: `speca`
@@ -272,6 +321,20 @@ Modellkörningar från `utvärdera` bor i ett eget träd och är aldrig prispost
272
321
  (vyer/termer önskemålet nämner), tidigare specar för samma kund (arkivet +
273
322
  brädan), ev. brief. **Alla sökvägar i manifestet absoluta.** Finns inget repo
274
323
  i sessionen: skriv `KÄLLOR: inga — allt blir antaganden` så specifiern vet.
324
+ Starten avgör samtidigt två rader som specifiern aldrig får gissa:
325
+ `SPRÅK:` (`svenska` eller `engelska`, avläst ur önskemålets brödtext;
326
+ rubrikskelettet i `RUBRIKER:` följer det språket, se promptmallen) och
327
+ `DEPS:` (`installerade` när `node_modules/` eller motsvarande finns under
328
+ REPO-ROT, annars `saknas`; utan repo alltid `saknas`).
329
+ Skriv manifestet till `<katalog>/kallmanifest.md` i varje katalog specen
330
+ arkiveras i (steg 5), så att `bedöm` kan ge assessorn samma manifest utan
331
+ att bygga ett nytt. Manifestets första rad är `SPRÅK: <svenska|engelska>`,
332
+ samma värde som prompten fick: `bedöm` läser språket därifrån till
333
+ assessorns och scorerns `SPRÅK:`-rad och till `kontroll.mjs --sprak`,
334
+ aldrig ur en ny gissning (#521).
335
+ Manifestet har sist sektionen `## Avgjorda val`: varje val som en människa
336
+ avgjort efter ett tidigare stopp, en rad var, ordagrant som svaret gavs;
337
+ `inga` om inga. Specifiern får aldrig flagga ett val som står där.
275
338
  3. **Eskaleringsbedömning.** Misstänkt bunt (flera beslut), behörighet/betalning/
276
339
  oåterkallelig data/personuppgifter, eller offertunderlag → utfallet är
277
340
  prisbärande och eskaleringen är ett MODELLBYTE, inte en flagga: dispatcha
@@ -279,10 +342,15 @@ Modellkörningar från `utvärdera` bor i ett eget träd och är aldrig prispost
279
342
  `request-specifier`, med `ESKALERAD: ja` plus orsaken i prompten.
280
343
  4. **Dispatcha `request-specifier`** (eller den eskalerade varianten enligt
281
344
  steg 3) enligt promptmallen i dispatch-kontraktet.
282
- 5. **Arkivera.** Dela svaret vid `## Till granskaren`. En spec → en katalog:
283
- allt före blocket är specarna; VARJE spec får sin egen katalog med egen
284
- `spec.md` och eget `state.json` (bunt: `bundle`-fältet ifyllt, blocket
285
- kopieras till varje katalog som `granskarblock.md`). Bunt ur en gid-post:
345
+ 5. **Arkivera.** Specifierns svar delas per spec (ADR-0118): varje titelrad
346
+ startar en ny spec, och specens EGET `## Till granskaren`-block avslutar
347
+ den. Ett svar med bara ETT block efter flera specar (gamla formatet) är en
348
+ brist: `korSpecifier` dispatchar om HELA specifierprompten en gång med
349
+ `RÄTTNINGAR:` innan någon katalog skapas, och kvarstår bristen (delat block
350
+ eller ett saknat block) blir steget `ofärdig` direkt, utan katalog. En
351
+ spec → en katalog: VARJE spec får sin egen katalog med egen `spec.md`,
352
+ eget `granskarblock.md` (delens eget block, aldrig ett kopierat) och eget
353
+ `state.json` (bunt: `bundle`-fältet ifyllt). Bunt ur en gid-post:
286
354
  del 1 behåller gid-katalogen, resterande delar får slug-kataloger med
287
355
  `bundle.parent_slug` satt till gid:t; syskontasks på brädan mintas först
288
356
  vid sync. Stämpla varje berörd `state.json` med
@@ -294,11 +362,14 @@ på request-write-spec/references/specformat.md>`. Scriptet sätter
294
362
  referens-hashen måste sättas explicit precis som för poängen, annars
295
363
  tappas specformatets proveniens (se runs-kontraktet).
296
364
  **Respec över befintlig katalog invaliderar analysen:** när `spec.md`
297
- ersätts döps ev. `impact.md` om till `impact-superseded-<datum>.md`,
298
- status sätts till `spec_draft`, `approval` nollas och `granskarutslag`
299
- töms — en ny spec är ogranskad per definition, ett utslag mot en ersatt
300
- spec är inte längre giltigt, och en gammal analys mot ny spec är exakt
301
- den skevhet proveniens-kontrollen finns för att stoppa.
365
+ ersätts i en katalog som redan har en, kör
366
+ `node <skillkatalog>/scripts/archive.mjs respec <katalog>` INNAN den nya
367
+ specen skrivs. Kommandot äger hela invalideringen (analysen läggs undan
368
+ som superseded, status, `approval`, `granskarutslag` och `stopp` återställs,
369
+ `runs[]` rörs aldrig); skriv aldrig de fälten för hand. Skälet står i
370
+ runs-kontraktet: en ny spec är ogranskad per definition, ett utslag mot en
371
+ ersatt spec är inte längre giltigt, och en gammal analys mot ny spec är
372
+ exakt den skevhet proveniens-kontrollen finns för att stoppa.
302
373
  6. **Formkontroll.** Kör `node <skillkatalog>/scripts/precheck.mjs <katalog>`
303
374
  per katalog. BRISTER i spec/granskarblock → dispatcha om specifiern EN gång
304
375
  enligt omdispatch-regeln. **Det förkastade utkastet får kvitto
@@ -309,14 +380,42 @@ på request-write-spec/references/specformat.md>`. Scriptet sätter
309
380
  dispatch får sin egen runs-rad — även den förkastade — så arkivet bär
310
381
  både att ett omtag skedde och varför; en spec godkänd i andra försöket
311
382
  får aldrig se ut som en som blev rätt direkt.
383
+ **Omdispatch gäller endast den spec som föll.** I en bunt rörs de specar
384
+ som passerade inte: omdispatchprompten bär hela ursprungsprompten plus
385
+ `RÄTTNINGAR:` med felraderna för just den specen och raden
386
+ `Returnera endast spec <N>`; svaret ersätter bara den katalogens utkast.
387
+ **Kvarstår brist efter omdispatch blir utkastet aldrig `spec.md`:** även
388
+ det andra utkastet arkiveras som `spec-avvisad-<datum>.md` med egen
389
+ runs-rad, katalogen står utan `spec.md` och behåller status `spec_draft`,
390
+ och i `utvärdera` blir körningen `"ofärdig"`. Visa bristerna och stanna.
391
+ **Kandidat stoppas här, aldrig i bedöm.** Skriver precheck raden
392
+ `KANDIDAT: KONCEPT` eller `KANDIDAT: DIAGNOS` (specen bär markörraden ur
393
+ specformatets kandidatform), kör direkt
394
+ `node <skillkatalog>/scripts/archive.mjs stopp <katalog> --utfall <utfallet ur raden> --kalla spec.md`
395
+ på den katalogen: en kandidat har inget analys- eller poängsteg att vänta
396
+ på, och `stopp`-fältet är det som gör utfallet synligt för `bluf`,
397
+ rapporten och `set-status`. Kandidatens katalog kan fortfarande läggas på
398
+ brädan i steg 7 (kunddialogen är nästa steg för den), men `bedöm` hoppar
399
+ över den.
400
+ **Prisavgörande öppet val stoppas här (ADR-0118).** Skriver precheck raden
401
+ `PRISAVGORANDE: <valet>` (granskarblockets rad `Prisavgörande öppet val:
402
+ ja: …`), kör `archive.mjs stopp <katalog> --utfall TILLBAKA_TILL_SPEC
403
+ --kalla granskarblock.md` och visa valet i kundens språk; ingen assessor.
404
+ Är `state.json.respec_varv` redan ≥ 1 kör i stället `--utfall ESKALERA
405
+ --kalla granskarblock.md --orsak "prisavgörande val kvarstår efter respec:
406
+ <valet>"`: en människa dömer, posten går aldrig en tredje specrunda. När
407
+ valet avgjorts skrivs svaret under `## Avgjorda val` i manifestet, sedan
408
+ `respec` och ny specifier-dispatch.
312
409
  BRISTER i `state.json` är wrapperns egna filer och
313
- rättas av wrappern själv, aldrig via omdispatch. Kvarstår brister, visa dem
314
- och stanna. VARNING:ar visas men stoppar inget.
410
+ rättas av wrappern själv, aldrig via omdispatch. VARNING:ar visas men
411
+ stoppar inget.
315
412
  7. **Svara BLUF:** en rad per spec (rubrik + ev. flaggor), sedan granskarblocket
316
413
  i sin helhet. Erbjud att lägga specarna på brädan via request-sync (🔍) — gör
317
414
  det bara på uttrycklig begäran. När en post läggs på brädan: sätt status
318
415
  `spec_synced`, döp om slug-katalogen till det myntade gid:t, notera sluggen
319
416
  i `former_slug` och uppdatera `request_id` i `state.json`.
417
+ **Avsluta enligt `## Avslutning: var posten står`** (tabellen ur
418
+ `archive.mjs nasta-steg`, brädraden, ISÄR-kontrollen), aldrig utan.
320
419
 
321
420
  ## Uppgift: `bedöm`
322
421
 
@@ -348,53 +447,100 @@ driftvakt.
348
447
  specblock är en BRIST — dela posten enligt arkivregeln innan något dispatchas.
349
448
  Övriga BRISTER → åtgärda ledet som brister (om spec: tillbaka till
350
449
  speca-steget) innan något dyrt dispatchas.
450
+ **`KANDIDAT:`-raden avgör före allt annat.** Skriver precheck
451
+ `KANDIDAT: KONCEPT` eller `KANDIDAT: DIAGNOS` (raden kommer alltid, även
452
+ vid exit 1) är posten en kandidat: hoppa över steg 3 till 7, kör
453
+ `archive.mjs stopp <katalog> --utfall <utfallet ur raden> --kalla spec.md`
454
+ om `stopp` inte redan är satt (speca steg 6 brukar ha gjort det;
455
+ ett identiskt omanrop är ofarligt), och routa posten som stopputfallet
456
+ nedan. Ingen analys och ingen poäng får någonsin dispatchas på en
457
+ kandidat.
351
458
  3. **Analys.** Saknas `impact.md`, eller fäller precheck den på
352
459
  underlagsproveniens (analysen gjordes mot en annan spec): kräver repo i
353
460
  sessionen (annars stopp — en gissad analys är förbjuden av skillen själv).
354
461
  Dispatcha `request-assessor` enligt promptmallen — specens granskarblock
355
462
  skickas med som internt underlag (samma förtroendedomän; flaggorna där är
356
- ofta analysens viktigaste ingång). Arkivera svaret som `impact.md`
463
+ ofta analysens viktigaste ingång). Promptens `KÄLLOR:`-rad bär
464
+ `kallmanifest.md` ur katalogen ordagrant, eller `inga` när filen saknas,
465
+ och `SPRÅK:`-raden bär manifestets `SPRÅK:`-värde (#521: kundtextens
466
+ språk gissas aldrig om, samma rad går till scorern i steg 4);
467
+ assessorn verifierar manifestet själv och ska aldrig ta det för sant
468
+ (ADR-0113: assessorn utforskade repot från noll trots att manifestet redan
469
+ fanns). Arkivera svaret som `impact.md`
357
470
  (inklusive dess "## Till granskaren"-block — visa blocket för användaren,
358
471
  aldrig till kundfält), stämpla med
359
472
  `node <skillkatalog>/scripts/archive.mjs add-run <katalog> --file impact.md
360
473
  --skill request-assess-impact --agent request-assessor
361
474
  --model <ur MODELL-svansraden>` (scriptet sätter `underlag_sha256` mot
362
- `spec.md`). Kör precheck igen med `--impact`. Fäller precheck det nya
475
+ `spec.md`). Kör precheck igen med `--impact`. Läs sedan raden
476
+ `AVVIKELSE: <etikett>` ur precheck-utskriften (etiketten på analysens rad
477
+ `**Avvikelse mot specen:**`; inte att förväxla med agenternas svansrad):
478
+ `motsägelse` betyder att specen förutsätter något som inte stämmer i
479
+ systemet, och då kör
480
+ `node <skillkatalog>/scripts/archive.mjs stopp <katalog> --utfall TILLBAKA_TILL_SPEC --kalla impact.md`
481
+ (kommandot sätter status `assessed`), hoppa över steg 4 till 7 och routa
482
+ posten som stopputfallet nedan: ingen scorer dispatchas på ett underlag
483
+ som redan säger att specen måste tillbaka. `ingen` och `precisering` går
484
+ vidare till steg 4. Fäller precheck det nya
363
485
  utkastet → omdispatcha assessorn EN gång enligt omdispatch-regeln, och ge
364
486
  det förkastade utkastet kvitto precis som i speca steg 6: arkivera det som
365
487
  `impact-avvisad-<datum>.md` och stämpla en egen runs-rad med kontrollens
366
488
  felrader i `--note` (ADR-0099).
367
489
  4. **Prissättning.** Dispatcha `request-scorer` enligt promptmallen.
368
490
  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
491
+ 5. **Ankarlösning och kontraktskontroll.** Skriv agentens hela svarstext (JSON
492
+ plus svansrader) till fil och kör
493
+ `node <skillkatalog>/scripts/kontroll.mjs <svar.txt> <spec.md> <impact.md> --expect-id <katalogens id> --ut <svar.json> --fixa-kundtext --state <katalog>/state.json --sprak <svenska|engelska>`.
494
+ `--state` ger contract-check split-status: `OVER_SKALAN` på något som inte
495
+ är en split-del fälls, och ett inget-tak-utfall utan steg mot A16 fälls
496
+ (#503). `--sprak` ger contract-check kundtextens språk, samma värde som
497
+ promptens `SPRÅK:`-rad (ur `experiment.json` i utvärdera, ur `SPRÅK:` i
498
+ `kallmanifest.md` här): kundtext med minst två funktionsord ur fel språk
499
+ fälls som BROTT och går till omdispatch, aldrig till kundtextfixen, som
500
+ byter ord men inte översätter (#521).
501
+ Scriptet strippar svansraderna (`MODELL:`, `LÄSTA:`, ev. `AVVIKELSE:`),
502
+ reparerar det dispatchern redan vet (`request_id`, enumform,
503
+ siffersträngar, LÄSTA-radens sökvägsform) och kör check-lasta,
504
+ `resolve-anchors.mjs` (beläggsankare till ordagranna källblock;
505
+ OLÖST/FLERTYDIG är ett kontraktsbrott) och contract-check i en process; ett
506
+ protokollterm-brott i ett rent kundtextfält skrivs om av en billig modell i
507
+ stället för att kosta en ny scorerkörning. `REPARERAT:`-raderna visas i
508
+ BLUF-svaret och skrivs i runs-radens `--note`.
509
+ `impact.md` skickas ALLTID med till contract-check, aldrig bara specen:
510
+ scriptet läser etiketten på `**Avvikelse mot specen:**` och fäller varje
511
+ annat utfall än TILLBAKA_TILL_SPEC när analysen bär `motsägelse`
512
+ (motsägelsegrinden i scriptet är bältet, steg 3 hängslena; en post som
513
+ ändå nått hit har ett underlag scriptet ska få se). Samma script kräver
514
+ att varje `bracket.steps[i]` är exakt `{anchor, relation, grund}`.
515
+ BROTT som kvarstår efter reparationerna → omdispatch EN gång enligt
379
516
  omdispatch-regeln, med felraderna i prompten. Kvarstår brott: visa dem och
380
517
  stanna utan att arkivera svaret som giltigt varv, men släng det inte:
381
518
  arkivera som `score-pricing-vN-avvisad.json` (utanför N-räkningen) med
382
519
  kontrollens felrader i en runs-rad, så även misslyckade körningar har
383
520
  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
393
- acceptanstest är att två oberoende körningar ger samma resultat.
521
+ 6. **Dubbelkörning (driftvakt).** Körning 2 startar ALLTID samtidigt med
522
+ körning 1, med samma prompt och i en ny isolerad körning; i produktion
523
+ dispatchas båda Agent-anropen på en gång (#500, ADR-0113), promptarna är
524
+ identiska och körningarna oberoende. Trigger: poängen är ≥ 1 (ADR-0098),
525
+ eller utfallet är `OVER_SKALAN` (ADR-0116: det utfallet skickar posten
526
+ till manuell offert och är prisbärande; två OVER_SKALAN med olika golv är
527
+ OLIKA och går till dom). Övriga stopputfall dubbelkörs inte: DELA_UPP körs
528
+ om per del, KONCEPT/DIAGNOS/TILLBAKA_TILL_SPEC går till människa ändå.
529
+ Tröskeln avgör bara om paret RÄKNAS som dubbelkörning, aldrig om
530
+ körning 2 startar. Blir körning 1 ett stopputfall utom OVER_SKALAN, eller
531
+ poäng under 1, arkiveras körning 2 som `-run2` med `superseded_by` mot
532
+ huvudfilen och en notering om att den inte krävdes, den räknas aldrig som
533
+ dubbelkörning, och stopproutningen nedan gäller som förut. I övriga fall
534
+ jämförs paret som nedan; skillens eget acceptanstest är att två oberoende
535
+ körningar ger samma resultat.
394
536
  - Kör hela steg 5 (ankarlösning, sedan kontraktskontroll) på det nya
395
537
  svaret först, med egen omdispatch-budget (EN).
396
538
  - Jämför sedan deterministiskt:
397
- `node <skillkatalog>/scripts/compare-scores.mjs <poäng1.json> <poäng2.json>`
539
+ `node <skillkatalog>/scripts/compare-scores.mjs <poäng1.json> <poäng2.json>`.
540
+ Scriptet diffar även de inre stegen: samma ankare i `bracket.steps` läst
541
+ som `storre` i ena körningen och `mindre` i den andra är en hård diffrad
542
+ (OLIKA), aldrig en toleransrad. Golv- och takankaren är undantagna, de
543
+ flippar av konstruktion när banden angränsar och ägs av toleransregeln.
398
544
  - **LIKA** → körning 1 gäller: `double_run: "bekräftad"` på dess
399
545
  runs-rad. Körning 2-filen arkiveras som `score-pricing-vN-run2.json`
400
546
  med SAMMA N och `superseded_by` satt till körning 1-filen.
@@ -422,7 +568,11 @@ driftvakt.
422
568
  skillnaden ligger inom toleransen. Sätt `double_run: "tolerans"` på
423
569
  bägge runs-raderna. Toleransen är en medveten prisregel: högsta
424
570
  värdet skyddar marginalen, och ett medelvärde räknas fortfarande
425
- aldrig fram. Visa toleransraderna i BLUF-svaret.
571
+ aldrig fram. Visa toleransraderna i BLUF-svaret. Ett par av två
572
+ `OVER_SKALAN` med SAMMA golv där bara konfidensen skiljer skriver
573
+ också `LIKA_TOLERANS`, men utan poäng att jämföra (OVER_SKALAN bär
574
+ `score: null`) vinner körning 1 deterministiskt, aldrig "högsta
575
+ poängen" (ADR-0116).
426
576
  - Exit 2 från jämförelsescriptet är ett dispatcherfel (ojämförbara
427
577
  körningar) — visa felet och stanna, rapportera aldrig LIKA.
428
578
  7. **Arkivera** godkända JSON-svar. Hämta varvets N med
@@ -430,14 +580,18 @@ driftvakt.
430
580
  skriv filerna (`score-pricing-vN.json`, körning 2
431
581
  som `-run2`, avvisade som `-avvisad`), och stämpla VARJE körning med
432
582
  `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:
583
+ MODELL-svansraden som förut, referens-hash via `--ref` för skillens
584
+ referensfil (se runs-kontraktet), double-run-utfall, superseded-by,
585
+ partial-rerun och pairs-with där de gäller). Effort läser scriptet självt
586
+ ur agentdefinitionen under Claude-konfigkatalogen när `--effort`
587
+ utelämnas, så utelämna flaggan; `--model` förblir obligatorisk och kommer
588
+ ur svansraden, aldrig ur definitionen (under `utvärdera` körs agenterna
589
+ med override, så definitionens modell är per konstruktion inte den som
590
+ kördes). Exempel för huvudfilen vid ett bekräftat par:
437
591
  `node <skillkatalog>/scripts/archive.mjs add-run <katalog>
438
592
  --file score-pricing-v1.json --skill request-score
439
593
  --agent request-scorer --model <ur MODELL-svansraden>
440
- --effort <ur agentdefinitionen> --ref poangskala.md=<hash via
594
+ --ref poangskala.md=<hash via
441
595
  ref-hash.mjs på request-score/references/poangskala.md>
442
596
  --double-run bekräftad`. `--double-run` tar ENDAST de tre
443
597
  literalerna `bekräftad`, `olika` eller `tolerans` (scriptet vägrar allt
@@ -449,13 +603,20 @@ ref-hash.mjs på request-score/references/poangskala.md>
449
603
  `archive.mjs set-status <katalog> priced` (scriptet vägrar vid spärr,
450
604
  stopputfall och experiment — se runs-kontraktet)
451
605
  eller `assessed`.
452
- 8. **Svara BLUF:** tabell `post · poäng · utfall · konfidens`, en rad
606
+ 8. **Svara BLUF:** tabellen kommer ur
607
+ `node <skillkatalog>/scripts/archive.mjs bluf <katalog>` och klistras in
608
+ som den är (`| post | poäng | utfall | konfidens | anm |`, en rad per
609
+ katalog och del; poängen ur filerna, stopputfall ur `stopp`, OLIKA och
610
+ tolerans i `anm`), ALDRIG handskriven: en handskriven tabell
611
+ felrapporterade i skarp körning. Under tabellen en rad
453
612
  per avvikelse värd att se (ESKALERA, DELA_UPP,
454
613
  INPUT_SAKNAS, AVVIKELSE-rader). JSON:en visas inte i chatten —
455
614
  `detaljer <post>` gör det. Skriv-tillbaka till brädan sker via request-sync
456
615
  i huvudkonversationen, bara på uttrycklig begäran — och det är den flytten
457
616
  som tar posten ✅ Spec ok → ⚖️ Poängsättning redo för granskning enligt
458
- syncens regler.
617
+ syncens regler. **Avsluta enligt `## Avslutning: var posten står`**
618
+ (tabellen ur `archive.mjs nasta-steg`, brädraden, ISÄR-kontrollen, och
619
+ stopputfall eller OLIKA som ett val användaren ska ta), aldrig utan.
459
620
 
460
621
  **Stopputfall routas, aldrig tolkas.** Ett stopputfall arkiveras som varvets
461
622
  fil (steg 7), statusen blir `assessed`, aldrig `priced`, och posten routas:
@@ -463,14 +624,23 @@ fil (steg 7), statusen blir `assessed`, aldrig `priced`, och posten routas:
463
624
  - `DELA_UPP` → föreslå ny analysrunda per del; delarna blir egna kataloger med
464
625
  `split` satt (`part`/`of`), och en del utan tak blir `OVER_SKALAN`, aldrig
465
626
  DELA_UPP igen. Delposter ärver enligt skillen postens spec och skriver aldrig
466
- en egen: förälderns `spec.md` och `granskarblock.md` KOPIERAS in i varje
467
- delkatalog (som buntens granskarblock), medan `impact.md` skrivs per del av
468
- den nya analysrundan.
627
+ en egen: förälderns `spec.md` och `granskarblock.md` KOPIERAS oförändrade in
628
+ i varje delkatalog, var för sig (ett granskarblock per delkatalog, ADR-0118,
629
+ ingen delkatalog delar sitt block med en annan), medan `impact.md` skrivs per
630
+ del av den nya analysrundan.
469
631
  - `OVER_SKALAN` → posten offereras separat; delas aldrig vidare (delningen görs
470
632
  ett steg, skillens egen regel).
471
633
  - `TILLBAKA_TILL_SPEC` / `KONCEPT` / `DIAGNOS` → tillbaka till spec-steget
472
- respektive kunddialog; visa skälet.
473
- - `ESKALERA` / `INPUT_SAKNAS` → visa skälet och stanna.
634
+ respektive kunddialog; visa skälet. Kom utfallet ur scripten (kandidat i
635
+ `spec.md`, `motsägelse` i `impact.md`, prisavgörande öppet val i
636
+ `granskarblock.md`, ADR-0118) bär `state.json.stopp` det via
637
+ `archive.mjs stopp` och det finns ingen varvsfil; kom det ur scorern
638
+ arkiveras varvsfilen som förut och `stopp` förblir `null` (kommandot tar
639
+ bara de tre scriptbevisade källorna: `spec.md`, `impact.md`,
640
+ `granskarblock.md`). En respec nollar `stopp`.
641
+ - `ESKALERA` / `INPUT_SAKNAS` → visa skälet och stanna, eller ett prisavgörande
642
+ val som kvarstår efter respec (källa `granskarblock.md`, orsak i
643
+ `stopp.orsak`, ADR-0118).
474
644
 
475
645
  ## Uppgift: `status`
476
646
 
@@ -507,6 +677,9 @@ missta:
507
677
  bekräftelsefråga. Säg tydligt att detta inte syns för någon annan än arkivet.
508
678
  - Verben rör Offer status och kommentarer — aldrig Score, Class eller
509
679
  beskrivningsfält.
680
+ - **Avsluta enligt `## Avslutning: var posten står`** när posten har en
681
+ arkivkatalog: tabellen ur `archive.mjs nasta-steg` plus brädraden, så
682
+ flytten syns tillsammans med vad den öppnar för (`bedöm`, kund, inget).
510
683
 
511
684
  ## Uppgift: `döm`
512
685
 
@@ -571,6 +744,10 @@ körd igen för samma post, vars återupptagbarhetsregel omvärderar en
571
744
  Notisen är råmaterial till nästa instruktionsversion (mekanismen bakom
572
745
  v0.2 till v0.6) och läses aldrig in i någon prompt. `archive.mjs kalibrering <arkivrot>`
573
746
  listar alla.
747
+ 4. **Avsluta enligt `## Avslutning: var posten står`**: tabellen ur
748
+ `archive.mjs nasta-steg` visar om spärren lyfts (raden pekar då vidare på
749
+ sync/godkänn) eller fortfarande väntar (`döm` igen efter en beställd
750
+ omkörning), plus brädraden.
574
751
 
575
752
  **Granskarutslag skrivs som citat, aldrig som prosa.** När granskarens
576
753
  beslut ska in i underlaget, alltså när det ändrar eller preciserar en
@@ -629,12 +806,11 @@ dubbelkörning väntar på `döm`, precis som i produktion.
629
806
  `utvärdera rapport` (se avsnitten nedan). Argumentreglerna nedan gäller aldrig
630
807
  de orden; de klassificeras varken som modell eller post-id.
631
808
 
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.
809
+ **Flaggorna plockas bort först.** `--om` och `--hoppa-över` lyfts ur
810
+ argumentlistan INNAN skanningen nedan börjar, och räknas varken som modell
811
+ eller post-id. Utan det steget stoppar en flagga skanningen, noll modeller
812
+ hittas, och kommandot faller på "Minst en modell krävs" trots att modellen
813
+ står där.
638
814
 
639
815
  **Argumenttolkning.** Kvarvarande avslutande argument avgörs höger till vänster
640
816
  för att hitta GRÄNSEN mot post-id-listan: ett argument räknas som modell bara
@@ -673,34 +849,62 @@ Frågan gäller alltså kostnaden, inte risken att förlora data.
673
849
 
674
850
  1. **Underlag.** Som `speca` steg 1: rått önskemål eller gid — vid gid hämtas
675
851
  posten läsande via request-sync i huvudkonversationen, uppgiftens ENDA
676
- Asana-kontakt. Skapa `experiments/<slug>/` och `experiment.json` med
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
-
700
- 2. **Källmanifest EN gång.** Bygg manifestet som i `speca` steg 2 och
701
- återanvänd det byteidentiskt i ALLA varv. Anteckna manifest-hash i
702
- `experiment.json` (`ref-hash.mjs` på manifesttexten). Enda avsedda
852
+ Asana-kontakt. Skapa `experiments/<slug>/` och skriv underlaget till disk
853
+ med fasta namn: önskemålet ordagrant som `experiments/<slug>/onskemal.md`
854
+ och källmanifestet (steg 2) som `experiments/<slug>/kallmanifest.md`.
855
+ Namnen är fasta därför att `korning.mjs` (steg 4) läser dem därifrån och
856
+ bygger varje agentprompt av dem; ett underlag som bara finns i
857
+ konversationen når aldrig en headless-process.
858
+
859
+ Skapa sedan `experiment.json` med ticketreferens, datum och `varianter` i
860
+ deklarationsordning, alltid genom kommandot:
861
+
862
+ ```
863
+ node <skillkatalog>/scripts/archive.mjs experiment experiments/<slug> \
864
+ --satt request_id=<gid> --satt title=<titel> --satt permalink_url=<url> \
865
+ --satt datum=<YYYY-MM-DD> --satt sprak=<svenska|engelska> --satt manifest_sha256=<hash> \
866
+ --satt eskalerad=<true|false> --satt eskaleringsorsak=<text> \
867
+ --variant <m>...
868
+ ```
869
+
870
+ Nyckelnamnen är rapportens: `report.mjs` läser `request_id`, `title`,
871
+ `permalink_url` och `datum`, och `svep.mjs` läser `request_id` (med
872
+ `ticket` som reserv i äldre filer) till varje varvs REQUEST_ID. Äldre
873
+ filer med `ticket`/`titel` läses som förut men skrivs inte längre.
874
+ `manifest_sha256` (steg 2) och `eskalerad`/`eskaleringsorsak` (steg 3)
875
+ sätts när de är kända, i egna anrop av samma kommando; ett fält som redan
876
+ bär samma värde är no-op. `sprak` avläses ur önskemålets brödtext av
877
+ huvudkonversationen, precis som i `speca` steg 2; `korning.mjs` vägrar
878
+ starta utan den (den gissar aldrig språk, ADR-0114).
879
+
880
+ Anteckna samtidigt postens `permalink_url` (`--satt permalink_url=<url>`)
881
+ när gid-svaret bär det. Det kommer ur samma läsande hämtning
882
+ som redan görs, så steget får ingen ny Asana-kontakt. Saknas fältet i
883
+ svaret skrivs det inte, och rapporten visar då gid som ren text: Asanas
884
+ permalänk bär ett workspace-gid som inte finns på experimentytan, så en
885
+ konstruerad URL vore en gissning.
886
+
887
+ **`archive.mjs experiment` är den ENDA skrivaren av `experiment.json`.**
888
+ Ingen process, varken huvudkonversationen, startaren eller den inre
889
+ processen, skriver filen för hand. Kommandot skapar filen om den saknas
890
+ och läser annars in den först: `varianter` slås ihop som UNION med bevarad
891
+ deklarationsordning (befintliga namn först, invokationens nya efter), en
892
+ befintlig körnings `status` skrivs aldrig över av en process som inte äger just den
893
+ körningen, och skrivningen är ATOMISK under ett lås i experimentkatalogen. Skälet till att det är ett kommando: `experiment.json`
894
+ är den enda filen på experimentytan som delas mellan samtidiga processer,
895
+ och låset är det som gör parallella körningar (steg 4) säkra. Utan det
896
+ tappar två samtidiga processer varandras rader, vilket är exakt vad som
897
+ hände sonnet/k4 i IPC 14001 (#493).
898
+
899
+ 2. **Källmanifest EN gång.** Bygg manifestet som i `speca` steg 2, skriv det
900
+ till `experiments/<slug>/kallmanifest.md` (steg 1) och
901
+ återanvänd det byteidentiskt i ALLA varv. Anteckna manifest-hash med
902
+ `archive.mjs experiment experiments/<slug> --satt manifest_sha256=<hash>`
903
+ (`ref-hash.mjs` på manifesttexten). Enda avsedda
703
904
  skillnaden mellan varven är modellen.
905
+ Manifestet har sist sektionen `## Avgjorda val`: varje val som en människa
906
+ avgjort efter ett tidigare stopp, en rad var, ordagrant som svaret gavs;
907
+ `inga` om inga. Specifiern får aldrig flagga ett val som står där.
704
908
  3. **Eskaleringsvalet görs en gång** enligt `speca`:s regler och gäller alla
705
909
  varv, även eskaleringsvalet är en del av det som hålls konstant.
706
910
  Triggar valet: agenten blir `request-specifier-eskalerad` i stället för
@@ -710,175 +914,255 @@ Frågan gäller alltså kostnaden, inte risken att förlora data.
710
914
  över av varvets modell. Eskaleringsmekanismen kollapsar alltså medvetet
711
915
  under override, det är avsett, inte en lucka: `utvärdera` mäter en ren
712
916
  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`:
917
+ eskaleringsbeslutet i sig. Valet antecknas med
918
+ `archive.mjs experiment experiments/<slug> --satt eskalerad=<true|false> --satt eskaleringsorsak=<text>`;
919
+ det är därifrån `svep.mjs` läser `ESKALERAD:` till varje varvs prompt.
920
+ 4. **Ett varv per modell i listan**, startat av `svep.mjs`. Huvudkonversationen
921
+ förbereder steg 1 till 3 och kör sedan, även för en enda post och en enda
922
+ modell:
738
923
 
739
924
  ```
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>
925
+ node <skillkatalog>/scripts/svep.mjs --repo-rot <abs> --konto <namn> \
926
+ --modell <modell> [--modell <modell>]... \
927
+ --exp experiments/<slug> [--exp experiments/<slug>]... \
928
+ [--tickets-parallellt 2] [--modeller-parallellt 4] [--om | --hoppa-över]
743
929
  ```
744
930
 
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
931
+ Varje varv startas av `svep.mjs` som EN `korning.mjs`-process, och det är
932
+ det som ger mätningen gratis. Parallellregeln: två tickets åt gången, högst
933
+ fyra modeller för en ticket i flykt (`--modeller-parallellt`, #526), och
934
+ nästa ticket startar först när en tickets samtliga
935
+ modeller är klara. Det är säkert därför att körningskatalogerna redan är
936
+ isolerade (`ny-korning` är atomisk, varje varv har egen katalog, manifest
937
+ och repo läses bara) och det enda delade tillståndet, `experiment.json`,
938
+ skrivs under lås (steg 1). Ansvaret delas, eftersom en process aldrig kan känna sin egen
939
+ slutkostnad:
940
+
941
+ **Startaren** är `svep.mjs`. Dess ansvar per varv, i en mening: allokera
942
+ körningskatalogen med
943
+ `archive.mjs ny-korning <modellkatalog> --request-id <gid eller slug>`
944
+ (kommandot skriver också skelettet till körningens `state.json`, se
945
+ Experimentytan; den inre processen skriver aldrig den filen från noll),
946
+ starta `korning.mjs` för varvet med körningskatalogen, experimentkatalogen,
947
+ repo-roten, request-id:t och modellen som argument (stderr till
948
+ `stderr.log`; `korning.json` skriver `korning.mjs` själv),
949
+ ta tiden runt processen och skriva
950
+ `<modell>/k<N>/matning.json` (`startad`, `avslutad`, `wall_ms`,
951
+ `exit_code`; skrivs ÄVEN när processen faller), stämpla `proveniens` i
952
+ körningens `state.json` med
953
+ `archive.mjs proveniens <modell>/k<N> --modell <varvets modellvärde> --konto <Claude-kontot som körde> --underlag <sökväg till kallmanifest.md>`
954
+ EFTER att `korning.json` skrivits, verifiera modellen mot `modelUsage`,
955
+ och sätta körningens status via `archive.mjs experiment`. Hur vart och ett
956
+ görs bor i scriptet och dess tester, inte här. Att stegen står som
957
+ kommandon är med flit (ADR-0094): ett steg som ålägger startaren en
958
+ skrivning utan att peka ut vem som utför den är en skrivning
754
959
  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,
960
+
961
+ Fyra regler i den kedjan är kontrakt mot rapporten och arkivet och står
962
+ därför kvar här med sitt skäl:
963
+
964
+ - **Väggtiden bor i `matning.json`, aldrig i `korning.json`.** Rapporten
965
+ läser `total_cost_usd`, `usage.input_tokens`, `usage.output_tokens`,
966
+ `usage.cache_read_input_tokens`, `usage.cache_creation_input_tokens` och
967
+ `subagent_stats.spawned` ur `korning.json`, men väggtiden ur
968
+ `matning.json`: en process kan inte känna sin egen klocktid när arbetet
969
+ ligger i bakgrundsprocesser, och stegsvarens `duration_ms` räknar inte in väntan på dem.
970
+ `korning.json` bär summan över stegen, därav den egna filen. En kraschad körning har en klocktid, ofta det intressantaste
971
+ talet i tabellen, därför skrivs `matning.json` även vid krasch. Lasten
972
+ väggtiden mättes under står i `korning.json`:s steg som
973
+ `samtidiga_processer` (levande agentprocesser på maskinen vid stegets
974
+ start, räknat med `pgrep`, maskinvitt och best effort, `null` när
975
+ räkningen föll); rapporten visar maxvärdet bredvid väggtiden och märker
976
+ en körning utan fältet som mätt under okänd samtidighet (#526).
977
+ - **Proveniensen stämplas EFTER `korning.json`, aldrig före**, annars
978
+ saknar stämpeln faktureringsdatan. Kommandot räknar själv `skill_sha256`
979
+ (över `SKILL.md` plus de fyra agentdefinitionerna) och `underlag_sha256`,
980
+ läser `korning.json` och antecknar `fakturerade_modeller` (den
981
+ kanoniserade nyckelmängden ur `modelUsage`, suffix som `[1m]` strippade,
982
+ ADR-0099), och stämplar även varje `del-N/`. Utan den stämpeln går det
983
+ inte att avgöra vad som skilde `k1` från `k2`, och en jämförelse över tid
984
+ blir en gissning.
985
+ - **Modellverifiering görs av startaren mot faktureringen (ADR-0099),
986
+ aldrig mot svansraden ensam.** När processen är klar: jämför
987
+ `fakturerade_modeller` mot varvets begärda modell plus de två tolererade
988
+ avvikelserna, kundtextfixens `sonnet` och CLI:ns `haiku`-hjälpanrop. En
989
+ modellnyckel som varken är varvets begärda modell, kundtextfixens
990
+ `sonnet` eller CLI:ns `haiku`-hjälpanrop är en faktisk MODELL-avvikelse →
991
+ körningen blir `"ofärdig"`. Startaren tolererar `haiku`-nyckeln i summan;
992
+ taket på 200 output-tokens, som skiljer hjälpanropet från en riktig
993
+ körning, prövas per steg av `korning.mjs`. Resultatet ligger i
994
+ `korning.json` som `modellverifiering` (`ok` plus `avvikelser`, där varje
995
+ rad namnger sitt steg), så avvikelsen går att peka ut i det steg den
996
+ uppstod. En MODELL-svansrad som säger något annat än vad
997
+ `modelUsage` visar är en RAPPORTERINGSAVVIKELSE: den visas i rapporten
998
+ och står kvar i `runs[]` (raden bär agentens uppgift i
999
+ `model.uppgiven`), men den fäller ALDRIG ensam körningen — modeller är
1000
+ opålitliga källor om sin egen identitet, och att kasta en korrekt
1001
+ körning på en felskriven svansrad betalar om ett riktigt resultat.
1002
+ Modellvärdet `default` kör agenternas egna defaults och får lagligt
1003
+ producera flera modellnycklar: ingen mängdjämförelse görs där.
1004
+ - **Statusen sätts via `archive.mjs experiment`**, aldrig genom att skriva
1005
+ filen: exit ≠ 0 eller en faktisk MODELL-avvikelse ger `"ofärdig"` med
1006
+ `--orsak`; har den inre processen redan satt `"stopp"`, `"väntar på dom"`
1007
+ eller `"ofärdig"` behålls den; annars `"klar"`.
1008
+
1009
+ **`korning.mjs` är den inre processen: ett script utan modellturer.** Det
1010
+ bygger prompterna ur `onskemal.md`, `kallmanifest.md` och `experiment.json`,
1011
+ startar varje agentsteg som en egen process
1012
+ `claude -p --agent <agent> --output-format json --max-turns <tak> [--model <modell>]`
1013
+ med prompten på stdin, sparar råsvaret under `k<N>/steg/`, kör precheck,
1014
+ `kontroll.mjs` och compare-scores, arkiverar via `archive.mjs` och skriver
1015
+ `korning.json` som summan. Det gör ingen Asana-hämtning och bygger inget
1016
+ nytt manifest. Buntens delar körs parallellt (två åt gången) och de två
1017
+ scorerkörningarna samtidigt (#500).
1018
+
1019
+ `korning.mjs` kör speca-stegen (dispatch, kontraktskontroll,
779
1020
  arkivering) och därefter bedöm-stegen (analys, poäng,
780
1021
  ankarlösning, dubbelkörning enligt bedöm) mot den anvisade katalogen, utan
781
1022
  godkännandegrind, och rör ingenting utanför den. Är modellen `default`
782
1023
  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
1024
+ den ett modellnamn får varje agentprocess modell-override till den på ALLA
1025
+ tre agenter; overriden är `--model` på processen, aldrig en promptrad, och
1026
+ agentdefinitionerna röres aldrig, effort tas ur definitionen
1027
+ som vanligt, utom när `korning.mjs` fått `--effort`: då får varje
1028
+ agentprocess samma `--effort`-override, stegposten och `runs[]` bär den
1029
+ som begärd (`effort_begard`, `add-run --effort`), aldrig som verifierad,
1030
+ eftersom faktureringen inte bär någon effort (#525). Overriden verifieras per steg mot `modelUsage` i råsvaret
1031
+ (ADR-0099, se modellverifieringen ovan), svansraden arkiveras men avgör
1032
+ aldrig ensam. `approval` förblir `null`, `state.json`
1033
+ får `"experiment": true` ur skelettet `ny-korning` skrev. `REQUEST_ID`
1034
+ och `--expect-id` använder alltid ticketens gid/slug, aldrig katalogens
1035
+ namn.
1036
+
1037
+ **Bunt i den inre processen:** ger specifiern flera specar läggs de som
1038
+ `del-1/ … del-N/` under körningskatalogen med `bundle` satt (buntregeln i
1039
+ Experimentytan), aldrig del 1 i roten; varje del får sin egen `spec.md`,
1040
+ `granskarblock.md` och `state.json`, och speca steg 6 (precheck,
1041
+ omdispatch per del, `stopp` vid kandidat) körs per del. Faller en del på
1042
+ kvarstående brist blir körningen `"ofärdig"`; övriga delar körs ändå
1043
+ klart så arkivet visar vad som gick att bedöma.
1044
+
1045
+ **DELA_UPP körs inte autonomt av `korning.mjs` (v1).** Utfallet arkiveras
1046
+ och körningen får status `stopp` med orsak; delningsanalysen saknar
1047
+ promptmall i den här skillen, och ett script får inte improvisera en
1048
+ (ADR-0113).
1049
+
1050
+ Ett fallet agentsteg, kontraktsbrott (kvarstående BROTT efter omdispatch) eller
796
1051
  en faktisk MODELL-avvikelse (obegärd nyckel i `modelUsage`, aldrig en
797
1052
  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
801
- delningsregler men utan paus för granskare: delkataloger
1053
+ körningens `status` till `"ofärdig"` via `archive.mjs experiment`
1054
+ (`experiments/<slug> --korning <modell>/k<N> --status ofärdig --orsak <vad som föll>`),
1055
+ aldrig genom att skriva filen.
1056
+
1057
+ 5. **DELA_UPP i experimentet.** Ett DELA_UPP-utfall följer bedöms ordinarie
1058
+ delningsregler men utan paus för granskare (i v1 kör `korning.mjs` inte
1059
+ delningen själv, se steg 4; reglerna här gäller när den körs):
1060
+ delkataloger
802
1061
  `<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.
1062
+ `granskarblock.md` kopierade in, ny analys + poäng per del. Delas en
1063
+ buntdel blir det `del-N/del-M/` under den delen, aldrig ett nytt
1064
+ syskon i roten (`bundle` och `split` sätts aldrig på samma katalog).
804
1065
  En del utan tak blir `OVER_SKALAN` och delas aldrig vidare. Övriga
805
1066
  stopputfall (`TILLBAKA_TILL_SPEC`, `KONCEPT`, `DIAGNOS`, `ESKALERA`,
806
- `INPUT_SAKNAS`) kräver människa eller kund: arkivera utfallet, avbryt det
1067
+ `INPUT_SAKNAS`) kräver människa eller kund: arkivera utfallet, sätt
1068
+ körningens status till `"stopp"` via `archive.mjs experiment`, avbryt det
807
1069
  varvet, gör det synligt i rapporten när den körs, och övriga körningar i
808
1070
  listan fortsätter oberoende.
809
1071
 
810
1072
  **OLIKA väntar på dom.** Skriver dubbelkörningen OLIKA i ett varv:
811
1073
  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.
1074
+ `"väntar på dom"` via `archive.mjs experiment`, synligt i rapporten när
1075
+ den körs.
813
1076
  Domen är människans även i experimentet: `döm` körs mot körningskatalogen
814
1077
  när granskaren vill (adresserad via sin sökväg, se `döm`), och ingen dom
815
1078
  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
1079
+ det gör `svep.mjs` (via `archive.mjs experiment`) nästa gång `utvärdera`
1080
+ körs för samma post (se
817
1081
  Återupptagbart i avsnittet Batch nedan för hur och när statusen går
818
1082
  till `"klar"` och rapporten kan visa utfallet). Övriga körningar i listan
819
1083
  fortsätter oberoende oavsett när, eller om, domen fälls.
820
1084
 
821
1085
  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
1086
+ och sökvägen till `experiments/<slug>/`. Tabellen per körning kommer ur
1087
+ `node <skillkatalog>/scripts/archive.mjs bluf <modell>/k<N>` (rekursivt
1088
+ över delarna, rotraden för en bunt lyder `bunt (N delar)`) och klistras in
1089
+ som den är, aldrig handskriven. Nämn att `utvärdera rapport` finns
823
1090
  när underlaget ska läsas sida vid sida. Ingen skrivning mot Asana.
824
1091
 
825
1092
  ### Batch: flera poster
826
1093
 
827
1094
  `utvärdera <post1> <post2> … <modell…>` kör varje ticket och modell i en EGEN
828
- headless-process, sekventiellt:
1095
+ `korning.mjs`-process, parallellt enligt `svep.mjs`: två tickets åt gången, högst
1096
+ fyra modeller för en ticket i flykt, och nästa ticket startar när en ticket är
1097
+ helt klar (steg 4). Raden `svep.mjs` kör per varv:
829
1098
 
830
1099
  ```
831
- claude -p --model sonnet --output-format json "/request utvärdera <gid> <modell> --hoppa-över --körning <katalog>"
1100
+ node <skillkatalog>/scripts/korning.mjs --katalog <k<N>> --exp <experiments/<slug>> --repo-rot <abs> --request-id <gid> --modell <modell>
832
1101
  ```
833
1102
 
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.
1103
+ Underlaget läses från disk av `korning.mjs`; ingenting går via stdin till
1104
+ orkestreringen, bara till agentprocesserna. Vilka rader varje agentprompt bär
1105
+ (ÖNSKEMÅL, KÄLLOR, REPO-ROT, DEPS, SPRÅK, RUBRIKER, ESKALERAD, REQUEST_ID) bor
1106
+ i scriptet och dess tester.
1107
+
1108
+ Det finns ingen orkestratormodell: agenternas modeller styrs av definition plus
1109
+ `--model`-override och verifieras per steg mot `modelUsage` i råsvaret, aldrig
1110
+ av svansraden ensam.
838
1111
 
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.
1112
+ `--om` och `--hoppa-över` är svepets egna flaggor; `korning.mjs` frågar aldrig
1113
+ något och kan därför inte hänga. `--hoppa-över` är dessutom precis
1114
+ återupptagningens semantik, ett avbrutet svep som startas om plockar upp där
1115
+ det stannade i stället för att betala om för allt färdigt.
845
1116
 
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
1117
+ `--katalog` pekar ut den körningskatalog som `archive.mjs ny-korning`
1118
+ redan allokerat åt den ticketen och modellen. `korning.mjs` arkiverar
848
1119
  bara dit, aldrig någon annanstans.
849
1120
 
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.
1121
+ Svepets loop är startaren, och den heter `svep.mjs`: den bär startarens hela
1122
+ ansvar per rad (`ny-korning`, tidtagningen som skriver `matning.json`,
1123
+ `archive.mjs proveniens` mot körningskatalogen när processen är klar,
1124
+ modellverifieringen och status via `archive.mjs experiment`), se steg 4.
853
1125
 
854
- - **En ticket = ett färskt kontextfönster.** Allt tillstånd bor i arkivet på
855
- disk; huvudsessionen samlar bara exitstatus och en radssammanfattning per
856
- ticket. `/clear` mellan tickets behövs inte — processgränsen är
1126
+ - **En ticket, en modell = en `korning.mjs`-process; varje agentsteg är ett
1127
+ eget kontextfönster.** Allt tillstånd bor i arkivet på
1128
+ disk; `svep.mjs` skriver en rad per ticket och modell när den startar och
1129
+ när den slutar (status, väggtid, USD) och en sluttabell, och huvudsessionen
1130
+ samlar bara det. `/clear` mellan tickets behövs inte — processgränsen är
857
1131
  motsvarigheten. Headless-körningen förutsätter att uppgiften kan köra utan
858
1132
  interaktiva permission-frågor, vilket håller: den läser repot och skriver
859
- enbart under `.haus-workflow/requests/` — plus EN läsande Asana-hämtning per ticket
860
- (steg 1), så MCP-behörigheten för läsning måste täcka headless-körning.
861
- - **Kör vidare vid fel.** En ticket vars gid inte går att hämta eller vars
862
- process kraschar noteras i sammanfattningen; resten av listan fortsätter.
863
- - **Återupptagbart:** innan en körning startas kontrolleras arkivet, styrt av
1133
+ enbart under `.haus-workflow/requests/`. Asana-hämtningen gjordes redan i
1134
+ steg 1 av huvudkonversationen, som skrev `onskemal.md`, så `korning.mjs`
1135
+ behöver ingen MCP-kontakt alls.
1136
+ - **Kör vidare vid fel.** En ticket vars underlag saknas (`onskemal.md`,
1137
+ `kallmanifest.md` eller `experiment.json` med `request_id`) märks och hoppas
1138
+ över; en vars process kraschar blir `"ofärdig"` med orsak. Resten av svepet
1139
+ fortsätter, och `svep.mjs` avslutar med exit 1 när någon körning blev
1140
+ `"ofärdig"`, annars 0.
1141
+ - **Återupptagbart:** det gör `svep.mjs`: innan en körning startas
1142
+ kontrolleras arkivet, styrt av
864
1143
  körningens `status` i `experiment.json`. `"klar"` och `"stopp"` hoppas
865
1144
  över med en notis. `"ofärdig"` är avbruten data, inget resultat värt att
866
1145
  skydda: den utlöser aldrig omkörningsfrågan, utan körs om automatiskt som
867
1146
  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.
1147
+ ändå aldrig över, den står kvar som historik över vad som gick fel. Ett
1148
+ undantag: en `"ofärdig"`-rad som bär en `pid` vars process lever är ingen
1149
+ avbruten körning utan en som pågår i ett annat svep (två konton mot samma
1150
+ checkout, ADR-0093); den hoppas med notisen att den pågår i en annan
1151
+ process och står som `pågår` i sluttabellen, aldrig som en dubblett
1152
+ (#516). En död pid, eller ingen pid alls, körs om som förut.
869
1153
  `"väntar på dom"` OMVÄRDERAS i stället för att hoppas över, det är den
870
1154
  enda platsen som är aktören för den statusen: kör `archive.mjs sparr` på
871
1155
  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
1156
+ status till `"klar"` via `archive.mjs experiment` och fortsätt där den
1157
+ stannade (rapporten kan köras separat när alla körningar för posten är
1158
+ klara). Exit 1 betyder att den fortfarande väntar: notera det och gå vidare
1159
+ till nästa varv. `döm` fäller domen men rör aldrig `experiment.json` (se
1160
+ `döm`), det är bara genom att `utvärdera` körs igen för samma post som
1161
+ statusen uppdateras. Omkörningsfrågan (se `utvärdera` ovan) gäller alltså bara
878
1162
  `"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.
1163
+ en ofärdig körning har inget att skydda. Körningar är billiga
1164
+ relativt risken att lappa ihop halva resultat. Ett svep över många tickets
1165
+ kan därmed fortsätta i en senare session utan onödiga omkörningar.
882
1166
 
883
1167
  ### Sammanställning
884
1168
 
@@ -926,10 +1210,63 @@ tvärs poster med olika modelluppsättning, eller uttalar sig om vilket utfall
926
1210
  som var korrekt. Bedömningen av vad som är ett acceptabelt utfall är
927
1211
  människans, gjord på erfarenhet av systemet. Rapporten visar spridningen.
928
1212
 
1213
+ ## Avslutning: var posten står
1214
+
1215
+ Uppgifterna `speca`, `bedöm`, `godkänn`, `underkänn` och `döm` avslutas
1216
+ ALLTID med det här blocket (#530). Ett svar som slutar utan det lämnar
1217
+ användaren att gissa var posten står, vad hon ska göra och vilket kommando
1218
+ som kör vidare, och arkiv och bräda kan då gå isär tyst (`spec_draft`
1219
+ lokalt, tom Offer status på brädan). Blocket består av fyra delar, i ordning:
1220
+
1221
+ 1. **Tabellen ur `node <skillkatalog>/scripts/archive.mjs nasta-steg <katalog>`**
1222
+ klistras in som den är (`| post | står | väntar på | nästa |`, en rad per
1223
+ katalog och del), ALDRIG handskriven, av samma skäl som `bluf`: en
1224
+ handskriven avslutning tappar exakt den rad som skulle ha sagt vad som
1225
+ väntar. Scriptet räknar ut raderna ur `state.json` (status, `stopp`,
1226
+ `respec_varv`, `approval`, olyft OLIKA via spärren, scorerns gällande
1227
+ utfall ur prisfilen) med reglerna ur den här skillens egna grindar;
1228
+ regeltabellen med källhänvisning per rad står ovanför funktionen
1229
+ `nastaStegFor` i scriptet. Ett läge scriptet inte känner skrivs som
1230
+ `okänt läge: <status>`, aldrig som en gissning.
1231
+ 2. **Brädraden.** Har posten ett gid: hämta Offer status via request-sync i
1232
+ huvudkonversationen (aldrig ur minnet) och skriv raden
1233
+ `Bräda: <Offer status>` direkt under tabellen. Saknar posten gid: skriv
1234
+ `Bräda: ingen (posten ligger inte på brädan)`.
1235
+ 3. **ISÄR-kontrollen.** Arkivstatus och Offer status ska motsvara varandra
1236
+ enligt tabellen nedan (värdena är request-sync:s, normaliserade på
1237
+ alfabetiska kärnan som där). Avviker de skrivs en rad
1238
+ `ISÄR: arkiv <x>, bräda <y>` följd av det kommando som stänger gapet.
1239
+
1240
+ | arkivstatus (`state.json`) | förväntad Offer status | stänger gapet |
1241
+ | ---------------------------------------------- | ------------------------------------ | ----------------------------------------------------------------------------------- |
1242
+ | `spec_draft` | tom eller 📥 Inkommet | sync via request-sync (🔍) när specen ska granskas |
1243
+ | `spec_synced` | 🔍 Spec redo för granskning | `godkänn`/`underkänn`, eller sync om brädan står på 📥 |
1244
+ | `spec_synced`/`spec_draft` med `approval` satt | ✅ Spec ok | `bedöm`; står brädan på 🔍 är approval-anteckningen äldre än brädan, läs om grinden |
1245
+ | `assessed`/`priced`, poängen synkad | ⚖️ Poängsättning redo för granskning | sync via request-sync om brädan står kvar på ✅ |
1246
+ | klar (`godkänn` från ⚖️) | 📤 Klar – till kund | inget, kundens `Decision` avgör resten |
1247
+
1248
+ Arkivet är cache och Asana sanningen (Hårda regler): ett ISÄR löses alltid
1249
+ i riktning mot brädan, aldrig genom att skriva om brädan för att matcha
1250
+ arkivet. En experimentvariant (`experiment: true`) har ingen brädrad och
1251
+ ingen ISÄR-kontroll, den når aldrig Asana.
1252
+
1253
+ 4. **Valet, när det finns ett.** Ett stopputfall (`TILLBAKA_TILL_SPEC`,
1254
+ `ESKALERA`, `KONCEPT`, `DIAGNOS`) och en olyft `double_run: olika`
1255
+ presenteras som ett val användaren ska ta, inte bara som routing:
1256
+ "här är ditt val", följt av alternativen (vid TILLBAKA_TILL_SPEC det
1257
+ öppna valet ur `stopp.orsak` eller källfilen; vid OLIKA de två
1258
+ körningarnas belägg sida vid sida, se bedöm steg 6) och kommandot som
1259
+ verkställer svaret (`speca <post>` efter `## Avgjorda val` + `respec`,
1260
+ `döm <post>`, kunddialog). Ett stopp som bara routas läses som "maskinen
1261
+ jobbar vidare", och ingen gör något.
1262
+
929
1263
  ## Mekaniska script: `scripts/`
930
1264
 
931
- Nio deterministiska script bor i katalogen: de fem kontrollerna nedan,
932
- `archive.mjs` som äger arkivets kataloger och spärrar, `report.mjs` som
1265
+ Tolv deterministiska script bor i katalogen: de fem kontrollerna nedan,
1266
+ `kontroll.mjs` som kör scorerns kontrollkedja i en process,
1267
+ `archive.mjs` som äger arkivets kataloger, spärrar och `experiment.json`,
1268
+ `svep.mjs` som är utvärderas startare,
1269
+ `korning.mjs` som är utvärderas inre process, `report.mjs` som
933
1270
  renderar modellrapporten (se `utvärdera rapport`), `lib.mjs` med de delade
934
1271
  byggstenarna och `ref-hash.mjs` för stabil hashning. Kontrollerna ersätter
935
1272
  modellkörningar där kontrollen är ren form,
@@ -940,13 +1277,23 @@ referensfiler vars regler det speglar och VARNAR när en katalogbump ändrat dem
940
1277
  - `scripts/precheck.mjs <arkivkatalog> [--impact]` — formkontroll FÖRE dyra
941
1278
  dispatcher: rubriker, exakt ett specblock, 2–5 punkter, förbjuden vokabulär,
942
1279
  granskarblocksrader, state.json-fält. Skiljer på BRISTER (exit 1, stoppar)
943
- och VARNING (heuristik — visas, stoppar inte).
1280
+ och VARNING (heuristik — visas, stoppar inte). Rubrikerna matchas via
1281
+ aliastabellen i `lib.mjs` på svenska eller engelska; rubriker ur båda
1282
+ språken i samma spec är en BRIST. Kandidatformen känns igen på markörraden
1283
+ (`**Koncept-kandidat**`/`**Diagnos-kandidat**`, eller de engelska
1284
+ motsvarigheterna) och skrivs som `KANDIDAT: KONCEPT` eller
1285
+ `KANDIDAT: DIAGNOS` på stdout, alltid, även vid exit 1; en kandidat med en
1286
+ Lösning är en BRIST. Med `--impact` läses etiketten på analysens rad
1287
+ `**Avvikelse mot specen:**` och skrivs som `AVVIKELSE: ingen`,
1288
+ `AVVIKELSE: precisering` eller `AVVIKELSE: motsägelse`; en rad utan
1289
+ etikett är en BRIST. Orkestratorn läser kandidat och avvikelse ur de
1290
+ raderna, aldrig ur filerna själv.
944
1291
  - `scripts/resolve-anchors.mjs <run.json> <spec.md> [impact.md]` — löser
945
1292
  beläggsankare (`belagg_ankare`/`ankare`) till ordagranna källblock, körs
946
1293
  FÖRE contract-check. Exit 0 = allt löst (eller inga ankare i filen), 1 =
947
1294
  minst ett OLÖST/FLERTYDIG ankare (kontraktsbrott, mekanisk omdispatch),
948
1295
  2 = användningsfel.
949
- - `scripts/contract-check.mjs <run.json> <spec.md> [impact.md] [--expect-id <id>]`
1296
+ - `scripts/contract-check.mjs <run.json> <spec.md> [impact.md] [--expect-id <id>] [--state <state.json>] [--sprak <svenska|engelska>]`
950
1297
  — kontraktskontroll av score-JSON EFTER körning: enums,
951
1298
  konfidensintervall, poängupplösning, inringningens närvaro (även per
952
1299
  alternativ; under-A1-fallet med floor null och tak A1 är giltigt), belägg/
@@ -955,6 +1302,19 @@ referensfiler vars regler det speglar och VARNAR när en katalogbump ändrat dem
955
1302
  INPUT_SAKNAS kräver saknas-lista i stället för frågerader. Ett olöst ankare
956
1303
  (belägg saknas trots `belagg_ankare`, eller `ankare` utan `citat`) är
957
1304
  dispatcherns fel, inte agentens: exit 2 med hänvisning till resolve-anchors.mjs.
1305
+ Stegschemat: varje `bracket.steps[i]` (även per alternativ) är exakt
1306
+ `{anchor, relation, grund}` med `relation` ur `storre`, `mindre`, `lika`;
1307
+ egna nycklar och tomma grunder är BROTT. Motsägelsegrinden: ges `impact.md`
1308
+ och analysens `**Avvikelse mot specen:**` bär `motsägelse` är varje annat
1309
+ utfall än TILLBAKA_TILL_SPEC ett BROTT. Prövar dessutom inringningens
1310
+ konsistens (golv före tak, between angränsande och inuti golv–tak, poäng
1311
+ mellan between-paret, relation mot poäng; poäng PÅ ett ankare trots
1312
+ storre/mindre är VARNING bara vid avrundningskollaps), att `type` är en av
1313
+ skalans fem typer, och undantar svartlistade ord som står i specen
1314
+ (ADR-0115). Med `--sprak` prövas varje kundtextfält mot språket (#521):
1315
+ minst två OLIKA funktionsord ur fel språk, hela ord, skiftlägesokänsligt
1316
+ (listorna i `lib.mjs` `FUNKTIONSORD`), är BROTT `kundtext på fel språk`;
1317
+ diakritiska tecken är ingen signal. Utan flaggan ingen språkkontroll.
958
1318
  - `scripts/check-lasta.mjs "<LÄSTA-innehåll>" <spec.md> <impact.md>` —
959
1319
  kontrollerar scorerns läs-scope: LÄSTA-raden ska bära underlagens två
960
1320
  sökvägar och får därutöver bara bära filer under skillens egen katalog.
@@ -965,8 +1325,97 @@ referensfiler vars regler det speglar och VARNAR när en katalogbump ändrat dem
965
1325
  `LIKA_TOLERANS` (poängskillnad ≤ 0,5 med samma outcome, oavsett confidence,
966
1326
  och ankarband som överlappar eller angränsar; exit 0, verdiktraden pekar ut
967
1327
  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.
1328
+ ojämförbara körningar (dispatcherfel, aldrig LIKA). Diffar även de inre
1329
+ stegen: samma ankare i `bracket.steps` med `storre` i ena körningen och
1330
+ `mindre` i den andra ger diffraden
1331
+ `bracket.steps[A<n>].relation: "mindre" ↔ "storre"`, en hård diff som
1332
+ aldrig faller under toleransen; golv- och takankare i någon av körningarna
1333
+ undantas, de flippar av konstruktion när banden angränsar. Scriptet kräver
1334
+ mode `pricing`; guard-jämförelser är en medveten utbyggnad i guard-steget.
1335
+ Angränsning avgörs av skalans ordning (lib.mjs ANKARE, A17 mellan A12 och
1336
+ A13), och utöver poäng och ankare diffas `type` (toleransrad), antalet
1337
+ `conditions` (hård diff) och `alternatives` per label (ADR-0115).
1338
+ - `scripts/archive.mjs <kommando> <katalog> [flaggor]`: arkivets mekanik
1339
+ (fullständig lista i scriptets huvud): `add-run`, `next-n`, `sparr`,
1340
+ `utslag`, `set-status`, `kalibrering`, `ny-korning`, `proveniens`, samt
1341
+ `stopp <katalog> --utfall KONCEPT|DIAGNOS|TILLBAKA_TILL_SPEC --kalla spec.md|impact.md`
1342
+ (skriver `state.stopp` efter att ha kontrollerat att källan bär utfallet;
1343
+ `--kalla impact.md` sätter status `assessed`, `--kalla spec.md` rör inte
1344
+ status), `respec <katalog>` (lägger undan `impact.md` som
1345
+ `impact-superseded-<datum>.md`, återställer status `spec_draft`,
1346
+ `approval`, `granskarutslag` och `stopp`, rör aldrig `runs[]`) och
1347
+ `bluf <katalog>` (markdown-tabellen `| post | poäng | utfall | konfidens | anm |`
1348
+ över katalogen och dess `del-N/` rekursivt, bunt-roten som
1349
+ `bunt (N delar)`; värdena ur filerna, aldrig ur prosa) och
1350
+ `nasta-steg <katalog>` (läs-enbart, markdown-tabellen
1351
+ `| post | står | väntar på | nästa |` över katalogen och dess `del-N/`
1352
+ rekursivt: var posten står, vad en människa väntas göra och vilket
1353
+ `/request`-kommando som kör vidare, ur `state.json` och scorerns gällande
1354
+ utfall enligt regeltabellen i scriptet; okänt läge skrivs som
1355
+ `okänt läge: <status>` med exit 0, trasig `state.json` är exit 2, se
1356
+ `## Avslutning: var posten står`). `add-run` läser
1357
+ `effort:` ur agentdefinitionen när `--effort` utelämnas och kanoniserar
1358
+ `--model`; `set-status priced` vägrar när `stopp` är satt.
1359
+ `experiment <experimentkatalog> [--satt nyckel=värde]... [--variant <modell>]... [--korning <modell>/k<N> --status <klar|ofärdig|stopp|väntar på dom> [--orsak <text>] [--pid <heltal>]]`
1360
+ är den ENDA skrivaren av `experiment.json` (se `utvärdera` steg 1): låst
1361
+ läs-modifiera-skriv med atomisk skrivning, katalog och fil skapas om de
1362
+ saknas. `--satt` skriver toppnivåfält append-only (samma värde är no-op,
1363
+ ett annat värde är exit 2; `eskalerad` tolkas som boolean), `--variant` är
1364
+ UNION med bevarad ordning, och `--korning` kräver `--status` ur de fyra
1365
+ statusliteralerna, rör ingen annan körning och kräver att katalogen
1366
+ `<modell>/k<N>` finns. `--pid` (heltal, bara med `--korning`) skriver
1367
+ startarens pid på raden; en statusskrivning utan `--pid` ersätter raden
1368
+ helt, så pid:n försvinner med slutstatusen (#516). Exit 0 ok, 2
1369
+ användnings- eller tillståndsfel.
1370
+ - `scripts/svep.mjs --repo-rot <abs> --konto <namn> --modell <m>... --exp experiments/<slug>... [--tickets-parallellt 2] [--modeller-parallellt 4] [--om | --hoppa-över]`:
1371
+ utvärderas startare (se `utvärdera` steg 4 och Batch). Kör två tickets åt
1372
+ gången med högst fyra modeller per ticket i flykt, nästa ticket när en är helt
1373
+ klar, och gör per varv `ny-korning`, starten av `korning.mjs`,
1374
+ `stderr.log`, `matning.json`, `proveniens`,
1375
+ modellverifiering mot `modelUsage` och status via `archive.mjs experiment`.
1376
+ Startraden i `experiment.json` bär svepets pid (#516).
1377
+ Återupptar ur `experiment.json`: `klar` och `stopp` hoppas utan `--om`,
1378
+ `väntar på dom` prövas med `sparr`, `ofärdig` körs om, utom när raden bär
1379
+ en levande `pid` (pågår i ett annat svep, hoppas som `pågår`, #516). En `--exp` utan
1380
+ `onskemal.md`, `kallmanifest.md` eller `experiment.json` med `request_id` märks
1381
+ och hoppas över. Exit 0 när ingen körning blev `ofärdig`, 1 annars, 2 vid
1382
+ användningsfel.
1383
+ - `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] [--effort <low|medium|high|xhigh|max|default>]`:
1384
+ utvärderas inre process (se `utvärdera` steg 4), ett script utan
1385
+ modellturer. Bygger prompterna ur `onskemal.md`, `kallmanifest.md` och
1386
+ `experiment.json`, där `sprak` är OBLIGATORISK: saknas den skrivs en
1387
+ instruktion på stderr och scriptet avslutar med exit 2 innan någon prompt
1388
+ byggs (ADR-0114). Varje agentsteg är en egen `claude -p --agent`-process med
1389
+ råsvaret under `k<N>/steg/`, och kontrollerna (precheck, `kontroll.mjs`,
1390
+ compare-scores) plus arkiveringen (`archive.mjs`) är scriptanrop.
1391
+ Buntens delar körs två åt gången och scorerns två körningar samtidigt;
1392
+ `DELA_UPP` körs inte autonomt i v1 utan ger status `stopp` med orsak.
1393
+ `--effort` är en override på varje agentprocess, symmetrisk med
1394
+ `--modell`: `default` ger ingen flagga och add-run läser definitionen som
1395
+ förut; allt annat skickas till processen och till `add-run --effort` som
1396
+ begärd, aldrig verifierad (#525). `korning.json` är summan över stegen
1397
+ (`korningsmotor`, `steg` med `effort_begard` och `samtidiga_processer` per
1398
+ steg, `modellverifiering` plus rapportens vanliga nycklar). Exit 0 när
1399
+ körningen gick igenom, 1 när minst en del blev ofärdig, 2 vid
1400
+ användningsfel.
1401
+ - `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>]`:
1402
+ scorerns kontrollkedja i EN process (se `bedöm` steg 5): svansrader,
1403
+ JSON-extraktion, deterministiska reparationer, sedan check-lasta,
1404
+ resolve-anchors och contract-check i EN process. Reparerar det dispatchern
1405
+ redan vet: `request_id`, versaler och gemener på `outcome`, `mode`,
1406
+ `confidence` och `bracket.steps[].relation`, sträng till tal på `score`, och
1407
+ LÄSTA-radens sökvägsform. Reparationerna rör aldrig poäng, utfall eller
1408
+ belägg, och varje reparation skrivs som en `REPARERAT:`-rad som anroparen
1409
+ bokför i runs-radens `note`. `--fixa-kundtext` skriver om ett
1410
+ protokollterm-brott i ett rent kundtextfält med en billig modell i stället
1411
+ för att kosta en hel scorerkörning, högst en runda. `--state <state.json>`
1412
+ och `--sprak <svenska|engelska>` går rakt vidare till contract-check
1413
+ (split-status respektive kundtextens språk; `korning.mjs` tar språket ur
1414
+ `experiment.json`, det manuella flödet ur `SPRÅK:` i `kallmanifest.md`).
1415
+ Ett språkbrott är aldrig kundtextfixbart och går till omdispatch (#521).
1416
+ stdout bär `MODELL:`,
1417
+ `LÄSTA:`, ev. `AVVIKELSE:`, `REPARERAT:`-rader och sist verdiktet. Exit 0 =
1418
+ kedjan håller, 1 = BROTT (omdispatch enligt ADR-0092), 2 = användningsfel.
970
1419
 
971
1420
  Kontrollerna bedömer aldrig innehåll och returnerar aldrig protokollutfall —
972
1421
  en post som passerar kan fortfarande stoppas av protokollen själva, och det är
@@ -988,13 +1437,36 @@ aldrig vidare som om kontrollen passerat.
988
1437
  <manifest med absoluta sökvägar, eller "inga — allt blir antaganden">
989
1438
 
990
1439
  REPO-ROT: <absolut sökväg eller "saknas">
1440
+ DEPS: <installerade | saknas>
1441
+ SPRÅK: <svenska | engelska>
1442
+ RUBRIKER:
1443
+ **Problem**
1444
+ **Lösning**
1445
+ **När det är klart**
1446
+ **Ingår inte**
1447
+ **Utseende**
1448
+ **Kräver**
1449
+ **Villkor**
991
1450
  ESKALERAD: <ja|nej>
992
1451
  ```
993
1452
 
1453
+ `RUBRIKER:` är skelettet på specens språk, literalerna ordagrant. Det
1454
+ svenska står ovan; vid `SPRÅK: engelska` tas de sju raderna ur
1455
+ rubriktabellen i `request-write-spec/references/specformat.md`
1456
+ (Problem, Solution, Done when, Not included, Appearance, Requires,
1457
+ Conditions). Fotnot: en kandidat ersätter Lösning och När det är klart med
1458
+ markörraden (`**Koncept-kandidat**` eller `**Diagnos-kandidat**`, på
1459
+ engelska `**Concept candidate**` eller `**Diagnosis candidate**`), det
1460
+ avgör specifiern själv enligt skillen.
1461
+
994
1462
  `request-assessor`:
995
1463
 
996
1464
  ```
997
1465
  REPO-ROT: <absolut sökväg>
1466
+ DEPS: <installerade | saknas>
1467
+ SPRÅK: <svenska | engelska>
1468
+ KÄLLOR:
1469
+ <kallmanifest.md ordagrant, eller "inga">
998
1470
 
999
1471
  SPEC (godkänd):
1000
1472
  <spec.md bytetroget>
@@ -1009,6 +1481,7 @@ aldrig vidare som om kontrollen passerat.
1009
1481
  REQUEST_ID: <gid eller slug>
1010
1482
  MODE: <pricing|guard>
1011
1483
  DELPOST: <nej | ja, del N av M>
1484
+ SPRÅK: <svenska | engelska>
1012
1485
 
1013
1486
  SPEC: <absolut sökväg till spec.md>
1014
1487
  PÅVERKANSANALYS: <absolut sökväg till impact.md>
@@ -1017,6 +1490,12 @@ aldrig vidare som om kontrollen passerat.
1017
1490
  (guard-läge lägger till `FULLPLAN:` och `TIDIGARE_POANG:` —
1018
1491
  guard-kommandot kommer i ett senare steg.)
1019
1492
 
1493
+ `SPRÅK:` är kundtextens språk för assessorn och scorern (#521): samma
1494
+ värde som specifiern fick, ur `experiment.json` (`sprak`) i utvärdera och
1495
+ ur `SPRÅK:` i `kallmanifest.md` i det manuella flödet. Analysens
1496
+ kundvända delar och kuvertets kundtextfält skrivs på det språket;
1497
+ `contract-check --sprak` fäller kundtext på fel språk.
1498
+
1020
1499
  - **Svansrader efter huvudinnehållet**, i denna ordning: `MODELL: <modell-id>`
1021
1500
  (alla agenter), `LÄSTA: <kommaseparerade absoluta sökvägar eller "inga">`
1022
1501
  (endast scorern — dess Read är promptbegränsad, raden gör regeln