@haus-tech/haus-workflow 5.6.0 → 5.7.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.
- package/CHANGELOG.md +8 -0
- package/dist/cli.js +66 -2
- package/library/global/agents/request-assessor.md +52 -51
- package/library/global/agents/request-scorer.md +52 -52
- package/library/global/agents/request-specifier-eskalerad.md +46 -44
- package/library/global/agents/request-specifier.md +43 -40
- package/library/global/skills/request/SKILL.md +1415 -1360
- package/library/global/skills/request/scripts/contract-check.mjs +17 -1
- package/library/global/skills/request/scripts/kontroll.mjs +7 -5
- package/library/global/skills/request/scripts/korning.mjs +16 -3
- package/library/global/skills/request/scripts/lib.mjs +289 -17
- package/library/global/skills/request/scripts/precheck.mjs +105 -40
- package/library/global/skills/request-assess-impact/SKILL.md +144 -140
- package/library/global/skills/request-score/SKILL.md +290 -266
- package/library/global/skills/request-sync/SKILL.md +246 -245
- package/library/global/skills/request-write-spec/SKILL.md +63 -61
- package/library/global/skills/request-write-spec/references/specformat.md +64 -60
- package/package.json +1 -1
|
@@ -1,123 +1,161 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: request
|
|
3
|
-
description:
|
|
3
|
+
description: The single entry point to Haus's request flow. Use when the user says "/request", "kör requestflödet", "speca och bedöm", "prisa den här posten", "ta önskemålet hela vägen", "vad ligger i kön", "detaljer <post>", or when several request skills are to be chained in sequence. Individual request skills (request-write-spec, request-score and others) still work standalone; this skill owns the chain, the archive and the dispatch to subagents.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# /request
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
##
|
|
13
|
-
|
|
14
|
-
`/request [
|
|
15
|
-
|
|
16
|
-
|
|
|
17
|
-
|
|
|
18
|
-
| `speca
|
|
19
|
-
| `bedöm <gid, slug
|
|
20
|
-
| `status`
|
|
21
|
-
| `godkänn <nr
|
|
22
|
-
| `underkänn <nr
|
|
23
|
-
| `döm <
|
|
24
|
-
| `detaljer <gid, slug
|
|
25
|
-
| `utvärdera <
|
|
26
|
-
| `hjälp`
|
|
27
|
-
|
|
28
|
-
##
|
|
29
|
-
|
|
30
|
-
- **request-sync
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
Skill
|
|
34
|
-
- **
|
|
35
|
-
|
|
36
|
-
- **
|
|
37
|
-
(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
- **
|
|
41
|
-
prompt
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
- **
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
- **
|
|
58
|
-
(`granskarblock.md`).
|
|
59
|
-
`impact.md` (
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
- **
|
|
63
|
-
- **
|
|
64
|
-
|
|
65
|
-
- **
|
|
66
|
-
request-sync;
|
|
67
|
-
|
|
68
|
-
batch
|
|
69
|
-
|
|
70
|
-
- **
|
|
71
|
-
(budget
|
|
72
|
-
contract-check
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
##
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
6
|
+
# /request: the entry point to the request flow
|
|
7
|
+
|
|
8
|
+
Thin orchestration on top of the four request skills. This skill contains
|
|
9
|
+
**zero domain rules**: everything about the spec format, scores and Asana fields lives in
|
|
10
|
+
the respective skill. Only routing, context carrying, the archive and the dispatch live here.
|
|
11
|
+
|
|
12
|
+
## Tasks
|
|
13
|
+
|
|
14
|
+
`/request [task]`: without an argument, show the menu below.
|
|
15
|
+
|
|
16
|
+
| Task | Does |
|
|
17
|
+
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
18
|
+
| `speca <request or gid>` | Source manifest → dispatch `request-specifier` → archive spec + reviewer block |
|
|
19
|
+
| `bedöm <gid, slug or nr>` | Gate → secure an analysis → dispatch `request-scorer` → archive JSON |
|
|
20
|
+
| `status` | Queue state via request-sync, BLUF table with row numbers |
|
|
21
|
+
| `godkänn <nr or gid>` | Move Offer status forward via request-sync; see the gate section |
|
|
22
|
+
| `underkänn <nr or gid> <reason>` | Reason as an Asana comment + iteration counter via request-sync |
|
|
23
|
+
| `döm <item>` | Human ruling on OLIKA: choose a run, order a rerun or reject the source material |
|
|
24
|
+
| `detaljer <gid, slug or nr> [step]` | Print archived JSON/documents for the item |
|
|
25
|
+
| `utvärdera <item…> <model…>` | Run the flow once per named model against the experiment area, at least one model; `sammanställ` gives the sweep view, `rapport` a readable comparison |
|
|
26
|
+
| `hjälp` | Explain the tasks, touch no files |
|
|
27
|
+
|
|
28
|
+
## Hard rules
|
|
29
|
+
|
|
30
|
+
- **request-sync NEVER runs in a subagent.** Setup and board verification are
|
|
31
|
+
interactive, the field map is session state, and a single writer against Asana
|
|
32
|
+
eliminates races. All Asana reading/writing happens in the main conversation via
|
|
33
|
+
the Skill tool (`request-sync`).
|
|
34
|
+
- **One writer at a time against Asana.** Batch results are written back sequentially,
|
|
35
|
+
one item at a time, never in parallel.
|
|
36
|
+
- **Documents are pasted byte-faithfully** into the specifier and assessor prompts
|
|
37
|
+
(request, spec, reviewer block). A truncated or reformatted
|
|
38
|
+
paste moves the source text out from under the agent's feet and gives a silently wrong
|
|
39
|
+
answer. Never paste a summary.
|
|
40
|
+
- **The pricing's source material is pointed out by path (ADR-0099).** The scorer's
|
|
41
|
+
prompt carries two named absolute paths (`spec.md`, `impact.md`) instead
|
|
42
|
+
of pasted text: the agent reads the files itself via Read, so
|
|
43
|
+
no transcript can be truncated, and the evidence anchors (source + substring)
|
|
44
|
+
are resolved by the scripts against exactly the same files on disk. The read scope is EXACTLY
|
|
45
|
+
these two files plus the skill's own directory, never
|
|
46
|
+
the run directory as a whole: `granskarblock.md` is the assessor's source material
|
|
47
|
+
and older pricing JSON would be pure contamination of the double run's
|
|
48
|
+
independence.
|
|
49
|
+
- **The price list and the repo are kept away from the scorer.** Its
|
|
50
|
+
agent definition lacks search and shell tools (Grep/Glob/Bash), and Read
|
|
51
|
+
is prompt-restricted per the rule above: the read scope is a prompt rule,
|
|
52
|
+
not a physical barrier. Therefore: never put the price list's content in the prompt,
|
|
53
|
+
and add no paths of your own to the dispatcher's lines beyond the labels in the template.
|
|
54
|
+
Paths that already stand in the source material (the analysis names files,
|
|
55
|
+
that is its job) are not a breach: the rule applies to what the dispatcher
|
|
56
|
+
adds. Check the `LÄSTA:` line in the reply (see the dispatch contract).
|
|
57
|
+
- **The reviewer blocks never reach the customer.** The spec's reviewer block is archived separately
|
|
58
|
+
(`granskarblock.md`). The analysis's "## For the reviewer" block stays in
|
|
59
|
+
`impact.md` (it is part of the source material for the scoring), but no
|
|
60
|
+
reviewer block may ever be sent along in material that goes to request-sync
|
|
61
|
+
for customer-visible fields.
|
|
62
|
+
- **The archive is a cache, Asana is the truth.** On conflict, the board wins.
|
|
63
|
+
- **Approval is never assumed.** That a spec exists in the archive does not mean it
|
|
64
|
+
has been reviewed. See the gate under `bedöm`.
|
|
65
|
+
- **Experiment data never reaches Asana.** `utvärdera` never writes via
|
|
66
|
+
request-sync; its only Asana contact is the read-only fetch of the request in the
|
|
67
|
+
main conversation. The `experiments/` tree is not item directories: sync,
|
|
68
|
+
batch and guard read only `.haus-workflow/requests/<gid-eller-slug>/` at the top level
|
|
69
|
+
and never touch `experiments/`.
|
|
70
|
+
- **The machine reruns only on a script-proven contract breach.** Redispatch
|
|
71
|
+
(budget ONE per agent) answers an error that precheck, resolve-anchors or
|
|
72
|
+
contract-check has proven. A disagreement between two VALID runs is a
|
|
73
|
+
judgment question and always goes to the human via the task `döm`: more
|
|
74
|
+
machine runs produce more votes, never more truth. No new
|
|
75
|
+
rerun mechanism may be added that breaks this.
|
|
76
|
+
|
|
77
|
+
## The language contract
|
|
78
|
+
|
|
79
|
+
One rule: **internal artifacts and instructions are written in English, customer-facing
|
|
80
|
+
text in the customer's language.** Ownership is per document, never per sentence. This
|
|
81
|
+
table is the only place in this skill where the rule is stated in full; every other
|
|
82
|
+
section follows it.
|
|
83
|
+
|
|
84
|
+
| Document | Owner | Language |
|
|
85
|
+
| --------------------------------------------------------------------------------------------------------- | ------------------- | ------------------------------------ |
|
|
86
|
+
| `request/SKILL.md` | internal | English |
|
|
87
|
+
| `request-write-spec/SKILL.md` + `references/specformat.md` | internal | English |
|
|
88
|
+
| `request-assess-impact/SKILL.md` | internal | English |
|
|
89
|
+
| `request-score/SKILL.md` | internal | English |
|
|
90
|
+
| `request-score/references/poangskala.md` | internal | **Swedish, untouched** (`sv, orörd`) |
|
|
91
|
+
| `request-sync/SKILL.md` | internal | English, Asana literals verbatim |
|
|
92
|
+
| `request-specifier(-eskalerad)`, `request-assessor`, `request-scorer` | internal | English |
|
|
93
|
+
| `spec.md` | **customer-facing** | the customer's (ADR-0114, unchanged) |
|
|
94
|
+
| `impact.md` | internal | English |
|
|
95
|
+
| `granskarblock.md` | internal | English |
|
|
96
|
+
| score JSON: `customer_rationale`, `conditions`, `assumptions_stated`, `alternatives`, `coverage_post.yta` | **customer-facing** | the customer's (unchanged) |
|
|
97
|
+
| score JSON: evidence, bracketing, drivers, everything else | internal | English |
|
|
98
|
+
| `korning.json`, `state.json`, `matning.json`, `experiment.json` | machine | unchanged |
|
|
99
|
+
| The Asana description's three sections | **customer-facing** | the customer's (unchanged) |
|
|
100
|
+
| Asana subtask `Analys` and its rows | internal | English body text, **Swedish title** |
|
|
101
|
+
|
|
102
|
+
The subtask titles (`Analys`, `Påverkansanalys vN`, `Poängsättning vN`) stay in
|
|
103
|
+
Swedish. They are lookup keys in Asana, the same class as the field names: `request-sync`
|
|
104
|
+
looks up an existing `Analys` and reuses it, and a rename would split the items into
|
|
105
|
+
before and after without anyone noticing.
|
|
106
|
+
|
|
107
|
+
Where this skill names a heading or row of the analysis or the reviewer block, it uses
|
|
108
|
+
the English literal the agents write: `## For the reviewer`, _Deviation from the spec_
|
|
109
|
+
with the labels `none`, `refinement`, `contradiction`, _Unanswered outcome choices_,
|
|
110
|
+
_Price-decisive open choice_. Archived runs carry the Swedish forms (`## Till granskaren`,
|
|
111
|
+
_Avvikelse mot specen_ with `ingen`, `precisering`, `motsägelse`,
|
|
112
|
+
_Obesvarade utfallsval_, _Prisavgörande öppet val_); the scripts read both, and their
|
|
113
|
+
output lines keep the Swedish canonical key (`AVVIKELSE: motsägelse`).
|
|
114
|
+
|
|
115
|
+
## The archive: `.haus-workflow/requests/`
|
|
116
|
+
|
|
117
|
+
The orchestration's envelope. The haus-managed request skills do not know about it;
|
|
118
|
+
this skill (and the agents through their prompts) maintains it.
|
|
119
|
+
|
|
120
|
+
**One directory = one spec = one priceable item.** A bundle (several specs from
|
|
121
|
+
the same request) becomes several directories: the score is set per item, so a directory
|
|
122
|
+
with several specs cannot be assessed.
|
|
85
123
|
|
|
86
124
|
```
|
|
87
125
|
<repo-rot>/.haus-workflow/requests/<gid-eller-slug>/
|
|
88
|
-
├── spec.md
|
|
89
|
-
├── granskarblock.md "##
|
|
90
|
-
│ block,
|
|
91
|
-
├── impact.md
|
|
92
|
-
│
|
|
93
|
-
├── score-pricing-vN.json
|
|
94
|
-
├── score-guard-vN.json guard
|
|
95
|
-
│ (
|
|
126
|
+
├── spec.md ONE spec, byte-faithful: what the reviewer approves
|
|
127
|
+
├── granskarblock.md "## For the reviewer" (bundle: the part's own
|
|
128
|
+
│ block, never a copied one, ADR-0118)
|
|
129
|
+
├── impact.md the impact analysis, verbatim incl. its
|
|
130
|
+
│ For the reviewer block
|
|
131
|
+
├── score-pricing-vN.json new round = new file, never overwrite
|
|
132
|
+
├── score-guard-vN.json guard outcomes are never synced, only archived
|
|
133
|
+
│ (own N sequence per type, separate from pricing)
|
|
96
134
|
└── state.json
|
|
97
135
|
```
|
|
98
136
|
|
|
99
|
-
|
|
100
|
-
`classification-guard-vN.json`
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
`spec-avvisad-<datum>.md` / `impact-avvisad-<datum>.md` (ADR-0099)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
-
|
|
110
|
-
spec
|
|
111
|
-
|
|
112
|
-
- **
|
|
113
|
-
haus-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
config)
|
|
118
|
-
- **
|
|
119
|
-
`.haus-workflow/` (ADR-0103).
|
|
120
|
-
|
|
137
|
+
Older archives (before ADR-0098) also carry `classification-pricing-vN.json` /
|
|
138
|
+
`classification-guard-vN.json` from the removed class axis. They are read and
|
|
139
|
+
shown as before (`detaljer`, the report, `archive.mjs`) but never written by
|
|
140
|
+
new runs.
|
|
141
|
+
|
|
142
|
+
Rejected attempts carry an `-avvisad` suffix and stand outside the N count:
|
|
143
|
+
the pricing's `<typ>-pricing-vN-avvisad.json` and the text steps'
|
|
144
|
+
`spec-avvisad-<datum>.md` / `impact-avvisad-<datum>.md` (ADR-0099); see the
|
|
145
|
+
redispatch rule in the dispatch contract.
|
|
146
|
+
|
|
147
|
+
- Directory name: the Asana gid when the item is on the board, otherwise a kebab slug of
|
|
148
|
+
the spec heading. A bundle without a gid: `<slug>-1`, `<slug>-2`, … When a gid is minted later:
|
|
149
|
+
rename the directory to the gid and note the slug in `state.json.former_slug`.
|
|
150
|
+
- **The gitignore is owned by haus, not by you.** `.haus-workflow/requests/` stands in the
|
|
151
|
+
haus-managed `.gitignore` block and is written by `haus apply`: never add
|
|
152
|
+
the line yourself, and never hand-edit the block. If the archive is still tracked in git:
|
|
153
|
+
run `haus apply --write`, which both writes the rule and untracks the files.
|
|
154
|
+
Pricing material must not enter the customer's history. `.haus-workflow/request.json` (the sync's
|
|
155
|
+
config) stays tracked: it is configuration, not working material.
|
|
156
|
+
- **Old archive under `.haus/`:** moved by `haus apply` to
|
|
157
|
+
`.haus-workflow/` (ADR-0103). Never write to `.haus/` again, and do not move it
|
|
158
|
+
yourself: run `haus apply --write` and let the command do it.
|
|
121
159
|
|
|
122
160
|
### `state.json`
|
|
123
161
|
|
|
@@ -140,51 +178,51 @@ omdispatch-regeln i dispatch-kontraktet.
|
|
|
140
178
|
}
|
|
141
179
|
```
|
|
142
180
|
|
|
143
|
-
- `bundle` =
|
|
144
|
-
`split` =
|
|
145
|
-
`{parent…, part: N, of: M}`.
|
|
146
|
-
- `stopp` =
|
|
181
|
+
- `bundle` = specs from the same request (set by speca on a bundle).
|
|
182
|
+
`split` = sub-items from a DELA_UPP outcome (set by bedöm). Both have the form
|
|
183
|
+
`{parent…, part: N, of: M}`. Never both on the same directory.
|
|
184
|
+
- `stopp` = a stop outcome that the scripts have proven without a scorer:
|
|
147
185
|
`{ "utfall": "KONCEPT" | "DIAGNOS" | "TILLBAKA_TILL_SPEC" | "ESKALERA", "kalla": "spec.md" | "impact.md" | "granskarblock.md", "date": "YYYY-MM-DD", "orsak"?: string }`.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
`impact.md`,
|
|
151
|
-
|
|
152
|
-
`ESKALERA` (
|
|
153
|
-
|
|
154
|
-
`archive.mjs set-status priced`
|
|
155
|
-
|
|
156
|
-
- `respec_varv`
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
`TILLBAKA_TILL_SPEC` (
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
- `coverage_post`
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
- `approval`
|
|
186
|
+
Written ONLY by `archive.mjs stopp` (which itself checks that the source carries
|
|
187
|
+
what is claimed: the candidate marker in `spec.md`, the label `contradiction` in
|
|
188
|
+
`impact.md`, the price-decisive open choice in `granskarblock.md`, ADR-0118)
|
|
189
|
+
and reset ONLY by `archive.mjs respec`. `orsak` is set when `utfall` is
|
|
190
|
+
`ESKALERA` (the command requires `--orsak` then, so a human sees why the item
|
|
191
|
+
did not go back to the spec once more) and is absent otherwise.
|
|
192
|
+
`archive.mjs set-status priced` refuses while the field is set. Older archives
|
|
193
|
+
lack the field and are read as `null`.
|
|
194
|
+
- `respec_varv` is a monotonic counter, raised by 1 by every `archive.mjs respec`
|
|
195
|
+
call (#505), never reset. The gate in bedöm reads it to
|
|
196
|
+
decide whether a remaining price-decisive open choice is to stop with
|
|
197
|
+
`TILLBAKA_TILL_SPEC` (the first round) or `ESKALERA` (the second round and
|
|
198
|
+
later): an item never goes a third spec round on the same choice. Older archives
|
|
199
|
+
lack the field and are read as `0`.
|
|
200
|
+
- `coverage_post` mirrors the score envelope's optional `coverage_post: { yta, omfattning }`
|
|
201
|
+
(ADR-0119). `archive.mjs add-run` copies the field from the
|
|
202
|
+
latest pricing file that carries it, the skeleton (`ny-korning`/`ny-del`) sets
|
|
203
|
+
it to `null`. `bluf` shows `täckningspost: <yta>` in the `anm` column when
|
|
204
|
+
the field is set.
|
|
205
|
+
- `approval` is written by the gate in bedöm:
|
|
168
206
|
`{ "source": "asana" | "manual", "by": "<namn, krävs vid manual>",
|
|
169
207
|
"status_read": "<värde>", "date": "YYYY-MM-DD" }`.
|
|
170
|
-
|
|
171
|
-
- `granskarutslag`
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
`archive.mjs sparr`
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
- `runs[]`
|
|
208
|
+
The note is traceability, never the approval itself.
|
|
209
|
+
- `granskarutslag` is a list, one row per ruling, written via `archive.mjs utslag`, never by hand.
|
|
210
|
+
Row form: `{ "typ": "viktning" | "kalibrering" | "sakandring", "date": "YYYY-MM-DD", "beslutad_av": "<namn>", ... }`.
|
|
211
|
+
A weighting (`viktning`) additionally carries `beslut` (verbatim), `axel` (`score`; `classification`
|
|
212
|
+
occurs in older archives), `n` and `galler_fil`: the row IS the lifting of the hold,
|
|
213
|
+
`archive.mjs sparr` reads it.
|
|
214
|
+
A calibration (`kalibrering`) carries `fraga`, `lasning1`, `lasning2`: a notice that a
|
|
215
|
+
protocol question let two valid readings diverge, material for the next
|
|
216
|
+
instruction version. A factual change (`sakandring`) carries `beslut`: the receipt in `state.json` for
|
|
217
|
+
the same event as the quote block in `impact.md` (see `döm`, the path "The source material
|
|
218
|
+
is wrong"). The verbatim decision lives in the quote block, the row here is only
|
|
219
|
+
the audit trail, never the source material itself.
|
|
220
|
+
The list is deliberately NEVER read back into any prompt, regardless of type: a
|
|
221
|
+
ruling that reaches the agents steers the next assessment instead of documenting
|
|
222
|
+
the previous one. It is reset (emptied) on respec, for the same reason that `approval`
|
|
223
|
+
is reset (see `speca` step 5): a ruling against a replaced spec is no longer
|
|
224
|
+
valid. It lives ONLY in `state.json`. Rows are never rewritten.
|
|
225
|
+
- `runs[]` gets one row per dispatch, including spec and analysis runs:
|
|
188
226
|
`{ "file": "spec.md" | "impact.md" | "<run>.json", "skill": "...",
|
|
189
227
|
"agent": "...",
|
|
190
228
|
"model": { "uppgiven": "<ur MODELL-svansraden>", "fakturerad": "<känd fakturerad modell, annars null>" },
|
|
@@ -194,670 +232,670 @@ omfattning }` (ADR-0119). `archive.mjs add-run` kopierar fältet från den
|
|
|
194
232
|
"underlag_sha256": { "spec.md": "<sha256>", "impact.md": "<sha256>" },
|
|
195
233
|
"double_run": "bekräftad" | "olika" | "tolerans" | null,
|
|
196
234
|
"superseded_by": null, "partial_rerun": false, "pairs_with": null }`.
|
|
197
|
-
`model`
|
|
198
|
-
MODELL
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
`model`
|
|
202
|
-
`quote_repairs`
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
suffix
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
`underlag_sha256`
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
`node <skillkatalog>/scripts/ref-hash.mjs <referensfil
|
|
215
|
-
`shasum`
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
- `status`
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
`archive.mjs sparr`
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
`double_run: "tolerans"`
|
|
231
|
-
|
|
232
|
-
`archive.mjs set-status priced`
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
- `spec_synced`
|
|
240
|
-
`guard_run`
|
|
241
|
-
|
|
242
|
-
###
|
|
243
|
-
|
|
244
|
-
|
|
235
|
+
`model` carries two fields (ADR-0099): `uppgiven` is the agent's self-report from
|
|
236
|
+
the MODELL tail line, `fakturerad` is filled when the dispatcher knows the
|
|
237
|
+
billed model (`add-run --fakturerad`), otherwise `null`: the billing's
|
|
238
|
+
truth per run lives in `proveniens.fakturerade_modeller`. Older archives carry
|
|
239
|
+
`model` as a plain string and are read as before.
|
|
240
|
+
`quote_repairs` occurs in older archives (before ADR-0092) and is read but
|
|
241
|
+
never written by new runs.
|
|
242
|
+
The stamping is always done via `archive.mjs add-run`; how N is computed, which
|
|
243
|
+
suffix a file gets and how a targeted rerun's pairing is written live in
|
|
244
|
+
the script and the tests. WHICH `--typ` values are passed to `archive.mjs next-n`,
|
|
245
|
+
that is, which axes were actually run in the round, is still
|
|
246
|
+
the orchestrator's decision, not the script's.
|
|
247
|
+
`underlag_sha256` is the source-material provenance: the hash of the documents the run
|
|
248
|
+
actually saw. Precheck refuses to assess when the analysis's spec hash does not match
|
|
249
|
+
the current `spec.md`, and guard must refuse to diff against a run whose source material
|
|
250
|
+
has been replaced: without this, a respec round against an old analysis is invisible.
|
|
251
|
+
The reference hash lives in the run row (not at the top level) and is computed at run time with
|
|
252
|
+
`node <skillkatalog>/scripts/ref-hash.mjs <referensfil>`, NEVER raw
|
|
253
|
+
`shasum` on the whole file: the installer stamps the files with the package version, so a
|
|
254
|
+
whole-file hash changes value at every haus release and would invalidate
|
|
255
|
+
all archived runs. The same tool is used for `underlag_sha256`.
|
|
256
|
+
The reference files lie under the Claude config directory (`CLAUDE_CONFIG_DIR`
|
|
257
|
+
or `~/.claude`). A later guard run compares the hash and refuses to diff on
|
|
258
|
+
skew ("rescoring required, the scale has changed").
|
|
259
|
+
- `status` NEVER becomes `priced` when any stop outcome exists in the round's
|
|
260
|
+
runs, or when the CURRENT row has `double_run: "olika"` unlifted, or
|
|
261
|
+
when the current row carries `partial_rerun: true` without a matching
|
|
262
|
+
ruling (an ordered rerun waits for the reviewer, see the task `döm`).
|
|
263
|
+
`archive.mjs sparr` decides both reasons identically (`sparrSkal`).
|
|
264
|
+
The hold is lifted by a weighting ruling (`archive.mjs sparr` is the only interpreter,
|
|
265
|
+
see the task `döm`) or by an ordered rerun whose ruling landed in a
|
|
266
|
+
weighting ruling; no other path lifts it, and the disagreeing axis's older
|
|
267
|
+
rows are never rewritten even after the lift.
|
|
268
|
+
`double_run: "tolerans"` counts as confirmed and does not block `priced`.
|
|
269
|
+
In older archives that carry a class axis (before ADR-0098),
|
|
270
|
+
`archive.mjs set-status priced` additionally refuses an INCOMPLETE pair:
|
|
271
|
+
the class and score axes' current round numbers differ without the difference
|
|
272
|
+
being an explained single-axis rerun (`partial_rerun: true` on the higher
|
|
273
|
+
axis's current row), or the score axis is missing entirely. An archive without a
|
|
274
|
+
class axis is never tested against the pair rule: the score is the only axis.
|
|
275
|
+
This reason is tested by `pricedBlockers` (`archive.mjs set-status`),
|
|
276
|
+
not by `archive.mjs sparr`: the rules live in the script and its tests.
|
|
277
|
+
- `spec_synced` is set by speca step 7 (the item placed on the board);
|
|
278
|
+
`guard_run` is reserved for the guard command (a later build step).
|
|
279
|
+
|
|
280
|
+
### The experiment area: `experiments/`
|
|
281
|
+
|
|
282
|
+
Model runs from `utvärdera` live in a tree of their own and are never pricing items:
|
|
245
283
|
|
|
246
284
|
```
|
|
247
285
|
<repo-rot>/.haus-workflow/requests/experiments/<slug>/
|
|
248
|
-
├── experiment.json
|
|
249
|
-
│ status per
|
|
250
|
-
├── <modell>/ `default`
|
|
286
|
+
├── experiment.json ticket reference, date, model list, manifest hash,
|
|
287
|
+
│ status per run
|
|
288
|
+
├── <modell>/ `default` as the model value gives the directory name
|
|
251
289
|
│ `baseline/`
|
|
252
290
|
│ ├── k1/
|
|
253
|
-
│ ├── k1/steg/
|
|
254
|
-
│ │
|
|
255
|
-
│ ├── k1/korning.json
|
|
256
|
-
│ │
|
|
257
|
-
│ ├── k1/matning.json
|
|
258
|
-
│ ├── k2/
|
|
259
|
-
│ └── k2/del-1/ …
|
|
260
|
-
│
|
|
291
|
+
│ ├── k1/steg/ the raw reply per agent step (claude -p JSON), including
|
|
292
|
+
│ │ redispatches and customer-text fixes
|
|
293
|
+
│ ├── k1/korning.json the sum over the steps: USD, tokens, modelUsage, number
|
|
294
|
+
│ │ of steps (report.mjs reads the same keys as before)
|
|
295
|
+
│ ├── k1/matning.json the starter's own measurement: startad, avslutad, wall_ms
|
|
296
|
+
│ ├── k2/ a later run of the SAME model
|
|
297
|
+
│ └── k2/del-1/ … part directories: a bundle from speca (`bundle`) or
|
|
298
|
+
│ a split from DELA_UPP (`split`), never both
|
|
261
299
|
└── <annan modell>/k1/ …
|
|
262
300
|
```
|
|
263
301
|
|
|
264
|
-
- **
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
(`korningsmotor`, `steg`, `modellverifiering`)
|
|
302
|
+
- **Two files, two owners.** `korning.json` is written by `korning.mjs` as the sum
|
|
303
|
+
over the steps' raw replies, which lie untouched in `steg/`; `matning.json` is
|
|
304
|
+
the starter's clock. A haus key in a raw reply under `steg/` would make it
|
|
305
|
+
a mixed format; the sum therefore carries its own keys
|
|
306
|
+
(`korningsmotor`, `steg`, `modellverifiering`) and the raw replies are never touched
|
|
269
307
|
(ADR-0113 amends ADR-0094).
|
|
270
|
-
- **
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
- **
|
|
275
|
-
|
|
276
|
-
`runs[]
|
|
277
|
-
|
|
278
|
-
- **
|
|
279
|
-
|
|
280
|
-
under
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
`korning.json`
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
`proveniens`
|
|
287
|
-
**
|
|
288
|
-
gid
|
|
289
|
-
|
|
290
|
-
- **
|
|
308
|
+
- **No run is ever overwritten.** The value of an old run is being
|
|
309
|
+
able to compare the same model before and after a change in the skill or the
|
|
310
|
+
agent prompts; if a new run overwrites it, that comparison is gone
|
|
311
|
+
forever. Every run therefore gets a directory of its own `k<N>` under its model.
|
|
312
|
+
- **The run directory IS the item directory.** It follows the archive contract
|
|
313
|
+
byte-faithfully (spec.md, granskarblock.md, impact.md, pricing JSON, state.json,
|
|
314
|
+
the `runs[]` rules), so `precheck.mjs` and `contract-check.mjs` work
|
|
315
|
+
unchanged, they are only pointed at `<modell>/k<N>/`. The provenance comes along for free.
|
|
316
|
+
- **The bundle rule: `del-N/` is either a bundle or a split, never both.** A
|
|
317
|
+
bundle from speca (several specs from the same request) is placed as `del-1/ … del-N/`
|
|
318
|
+
under the run directory with `bundle` set in each part's `state.json`; a
|
|
319
|
+
split from DELA_UPP is placed the same way but with `split` set. Part 1 NEVER lies
|
|
320
|
+
in the run root: the root carries only `state.json` (the skeleton `ny-korning` wrote),
|
|
321
|
+
`korning.json` and `matning.json`, so a root without `spec.md` but with parts
|
|
322
|
+
is read as `bunt (N delar)` by `bluf` and the report. DELA_UPP inside a
|
|
323
|
+
bundle part gives `del-N/del-M`. `sparr`, `precheck` and `stopp` run per part;
|
|
324
|
+
`proveniens` and `bluf` are pointed at `k<N>` and go recursively through the parts.
|
|
325
|
+
**The production layout is unchanged** (`speca` step 5: part 1 keeps the
|
|
326
|
+
gid directory, the siblings become `<slug>-N`); it is `korning.mjs` that chooses
|
|
327
|
+
the experiment layout, always against the directory `--katalog` points out.
|
|
328
|
+
- **The number is never allocated by the skill itself**, always with
|
|
291
329
|
`archive.mjs ny-korning <modellkatalog> [--request-id <gid eller slug>]`,
|
|
292
|
-
|
|
293
|
-
(`"experiment": true`, `"stopp": null`, `approval: null`,
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
- `state.json`
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
-
|
|
302
|
-
|
|
303
|
-
- `experiment.json`
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
(`"ofärdig"
|
|
307
|
-
|
|
308
|
-
(#516).
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
(
|
|
312
|
-
- `experiments/`
|
|
313
|
-
|
|
314
|
-
##
|
|
315
|
-
|
|
316
|
-
1. **
|
|
317
|
-
request-sync (
|
|
318
|
-
|
|
319
|
-
2. **
|
|
320
|
-
|
|
321
|
-
(
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
`SPRÅK:` (`svenska`
|
|
326
|
-
|
|
327
|
-
`DEPS:` (`installerade`
|
|
328
|
-
REPO-ROT,
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
`inga`
|
|
338
|
-
3. **
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
`request-specifier-eskalerad` (frontier
|
|
342
|
-
`request-specifier`,
|
|
343
|
-
4. **
|
|
344
|
-
|
|
345
|
-
5. **
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
`RÄTTNINGAR:`
|
|
350
|
-
|
|
351
|
-
spec →
|
|
352
|
-
|
|
353
|
-
`state.json` (
|
|
354
|
-
|
|
355
|
-
`bundle.parent_slug`
|
|
356
|
-
|
|
330
|
+
which creates the directory atomically, writes the skeleton to its `state.json`
|
|
331
|
+
(`"experiment": true`, `"stopp": null`, `approval: null`, empty `runs[]`)
|
|
332
|
+
and writes the directory's path to stdout. Two concurrent processes, for
|
|
333
|
+
example two Claude accounts against the same checkout, then get `k2` and `k3`,
|
|
334
|
+
never the same directory. If the skill counts by itself, one process's
|
|
335
|
+
run is silently lost.
|
|
336
|
+
- `state.json` in a run directory carries the marker `"experiment": true` from
|
|
337
|
+
the skeleton. `approval` stays `null` and `status` NEVER becomes `priced`: an
|
|
338
|
+
experiment result cannot be confused with a pricing item.
|
|
339
|
+
- The directory name `experiments` is reserved: a spec whose heading would be slugged
|
|
340
|
+
to `experiments` gets the suffix `-post`.
|
|
341
|
+
- `experiment.json` carries `varianter: []` in declaration order and `status` per
|
|
342
|
+
run: `"klar"`, `"ofärdig"`, `"stopp"`, `"väntar på dom"`. The literals
|
|
343
|
+
are written exactly like that, spaces and diacritics included. The start row
|
|
344
|
+
(`"ofärdig"` with `orsak` `"startad, inte avslutad"`, i.e. started, not
|
|
345
|
+
finished) additionally carries the starter's `pid` while the run is in
|
|
346
|
+
progress; the final status replaces the row and removes it (#516). Older files
|
|
347
|
+
with `baselinemodell` and `kandidatmodell` are read without error; new writes
|
|
348
|
+
use only `varianter`. The file is written ONLY by `archive.mjs experiment`
|
|
349
|
+
(see `utvärdera` step 1), never by hand.
|
|
350
|
+
- `experiments/` inherits the gitignore protection from `.haus-workflow/requests/`.
|
|
351
|
+
|
|
352
|
+
## Task: `speca`
|
|
353
|
+
|
|
354
|
+
1. **Source material.** A raw request pasted in, or a gid: then fetch the item via
|
|
355
|
+
request-sync (the main conversation) and use `## Ursprunglig förfrågan`
|
|
356
|
+
verbatim.
|
|
357
|
+
2. **Source manifest.** Isolated subagents cannot "look before they assume" without
|
|
358
|
+
help. Build a manifest before dispatch: relevant grep hits in the repo
|
|
359
|
+
(views/terms the request mentions), earlier specs for the same customer (the archive +
|
|
360
|
+
the board), any brief. **All paths in the manifest absolute.** If there is no repo
|
|
361
|
+
in the session: write `KÄLLOR: inga — allt blir antaganden` so the specifier knows.
|
|
362
|
+
The start also decides two lines that the specifier must never guess:
|
|
363
|
+
`SPRÅK:` (`svenska` or `engelska`, read from the request's body text;
|
|
364
|
+
the heading skeleton in `RUBRIKER:` follows that language, see the prompt template) and
|
|
365
|
+
`DEPS:` (`installerade` when `node_modules/` or equivalent exists under
|
|
366
|
+
REPO-ROT, otherwise `saknas`; without a repo always `saknas`).
|
|
367
|
+
Write the manifest to `<katalog>/kallmanifest.md` in every directory the spec
|
|
368
|
+
is archived in (step 5), so that `bedöm` can give the assessor the same manifest without
|
|
369
|
+
building a new one. The manifest's first line is `SPRÅK: <svenska|engelska>`,
|
|
370
|
+
the same value the prompt got: `bedöm` reads the language from there into
|
|
371
|
+
the assessor's and the scorer's `SPRÅK:` line and into `kontroll.mjs --sprak`,
|
|
372
|
+
never from a new guess (#521).
|
|
373
|
+
The manifest ends with the section `## Avgjorda val`: every choice that a human
|
|
374
|
+
has decided after an earlier stop, one line each, verbatim as the answer was given;
|
|
375
|
+
`inga` if there are none. The specifier must never flag a choice that stands there.
|
|
376
|
+
3. **Escalation assessment.** A suspected bundle (several decisions), permissions/payment/
|
|
377
|
+
irreversible data/personal data, or offer material → the outcome is
|
|
378
|
+
price-bearing and the escalation is a MODEL SWITCH, not a flag: dispatch
|
|
379
|
+
`request-specifier-eskalerad` (frontier model) instead of
|
|
380
|
+
`request-specifier`, with `ESKALERAD: ja` plus the reason in the prompt.
|
|
381
|
+
4. **Dispatch `request-specifier`** (or the escalated variant per
|
|
382
|
+
step 3) per the prompt template in the dispatch contract.
|
|
383
|
+
5. **Archive.** The specifier's reply is split per spec (ADR-0118): every title line
|
|
384
|
+
starts a new spec, and the spec's OWN `## For the reviewer` block ends
|
|
385
|
+
it. A reply with only ONE block after several specs (the old format) is a
|
|
386
|
+
defect: `korSpecifier` redispatches the WHOLE specifier prompt once with
|
|
387
|
+
`RÄTTNINGAR:` before any directory is created, and if the defect remains (a shared block
|
|
388
|
+
or a missing block) the step becomes `ofärdig` directly, without a directory. One
|
|
389
|
+
spec → one directory: EVERY spec gets its own directory with its own `spec.md`,
|
|
390
|
+
its own `granskarblock.md` (the part's own block, never a copied one) and its own
|
|
391
|
+
`state.json` (bundle: the `bundle` field filled in). A bundle from a gid item:
|
|
392
|
+
part 1 keeps the gid directory, the remaining parts get slug directories with
|
|
393
|
+
`bundle.parent_slug` set to the gid; sibling tasks on the board are minted only
|
|
394
|
+
at sync. Stamp every affected `state.json` with
|
|
357
395
|
`node <skillkatalog>/scripts/archive.mjs add-run <katalog> --file spec.md
|
|
358
396
|
--skill request-write-spec --agent <request-specifier|request-specifier-eskalerad>
|
|
359
397
|
--model <ur MODELL-svansraden> --ref references/specformat.md=<hash via ref-hash.mjs
|
|
360
|
-
på request-write-spec/references/specformat.md>`.
|
|
361
|
-
`underlag_sha256`
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
**
|
|
365
|
-
|
|
366
|
-
`node <skillkatalog>/scripts/archive.mjs respec <katalog>`
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
`runs[]`
|
|
370
|
-
runs
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
6. **
|
|
374
|
-
per
|
|
375
|
-
|
|
376
|
-
(ADR-0099,
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
--note "<kontrollens felrader>"` (
|
|
380
|
-
dispatch
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
**
|
|
384
|
-
|
|
385
|
-
`RÄTTNINGAR:`
|
|
386
|
-
`Returnera endast spec <N>`;
|
|
387
|
-
**
|
|
388
|
-
|
|
389
|
-
runs
|
|
390
|
-
|
|
391
|
-
**
|
|
392
|
-
`KANDIDAT: KONCEPT`
|
|
393
|
-
|
|
398
|
+
på request-write-spec/references/specformat.md>`. The script sets
|
|
399
|
+
`underlag_sha256` empty, a spec has no source material in the repo, but
|
|
400
|
+
the reference hash must be set explicitly just as for the score, otherwise
|
|
401
|
+
the spec format's provenance is lost (see the runs contract).
|
|
402
|
+
**A respec over an existing directory invalidates the analysis:** when `spec.md`
|
|
403
|
+
is replaced in a directory that already has one, run
|
|
404
|
+
`node <skillkatalog>/scripts/archive.mjs respec <katalog>` BEFORE the new
|
|
405
|
+
spec is written. The command owns the whole invalidation (the analysis is put aside
|
|
406
|
+
as superseded, status, `approval`, `granskarutslag` and `stopp` are reset,
|
|
407
|
+
`runs[]` is never touched); never write those fields by hand. The reason stands in
|
|
408
|
+
the runs contract: a new spec is unreviewed by definition, a ruling against a
|
|
409
|
+
replaced spec is no longer valid, and an old analysis against a new spec is
|
|
410
|
+
exactly the skew the provenance check exists to stop.
|
|
411
|
+
6. **Form check.** Run `node <skillkatalog>/scripts/precheck.mjs <katalog>`
|
|
412
|
+
per directory. BRISTER in the spec/reviewer block → redispatch the specifier ONCE
|
|
413
|
+
per the redispatch rule. **The rejected draft gets a receipt
|
|
414
|
+
(ADR-0099, the same rule as the pricing's `-avvisad`):** archive it
|
|
415
|
+
as `spec-avvisad-<datum>.md` before the redispatch, and stamp it with a
|
|
416
|
+
runs row of its own via `archive.mjs add-run --file spec-avvisad-<datum>.md
|
|
417
|
+
--note "<kontrollens felrader>"` (other flags as for the spec row). EVERY
|
|
418
|
+
dispatch gets its own runs row, even the rejected one, so the archive carries
|
|
419
|
+
both that a retake happened and why; a spec approved on the second attempt
|
|
420
|
+
must never look like one that was right directly.
|
|
421
|
+
**Redispatch applies only to the spec that failed.** In a bundle, the specs
|
|
422
|
+
that passed are not touched: the redispatch prompt carries the whole original prompt plus
|
|
423
|
+
`RÄTTNINGAR:` with the error lines for just that spec and the line
|
|
424
|
+
`Returnera endast spec <N>`; the reply replaces only that directory's draft.
|
|
425
|
+
**If a defect remains after redispatch, the draft never becomes `spec.md`:** the
|
|
426
|
+
second draft is also archived as `spec-avvisad-<datum>.md` with its own
|
|
427
|
+
runs row, the directory stands without `spec.md` and keeps the status `spec_draft`,
|
|
428
|
+
and in `utvärdera` the run becomes `"ofärdig"`. Show the defects and stop.
|
|
429
|
+
**A candidate is stopped here, never in bedöm.** If precheck writes the line
|
|
430
|
+
`KANDIDAT: KONCEPT` or `KANDIDAT: DIAGNOS` (the spec carries the marker line from
|
|
431
|
+
the spec format's candidate form), run directly
|
|
394
432
|
`node <skillkatalog>/scripts/archive.mjs stopp <katalog> --utfall <utfallet ur raden> --kalla spec.md`
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
**
|
|
401
|
-
`PRISAVGORANDE: <valet>` (
|
|
402
|
-
|
|
403
|
-
--kalla granskarblock.md`
|
|
404
|
-
|
|
433
|
+
on that directory: a candidate has no analysis or scoring step to wait
|
|
434
|
+
for, and the `stopp` field is what makes the outcome visible to `bluf`,
|
|
435
|
+
the report and `set-status`. The candidate's directory can still be placed on
|
|
436
|
+
the board in step 7 (the customer dialogue is the next step for it), but `bedöm` skips
|
|
437
|
+
it.
|
|
438
|
+
**A price-decisive open choice is stopped here (ADR-0118).** If precheck writes the line
|
|
439
|
+
`PRISAVGORANDE: <valet>` (the reviewer block's row `Price-decisive open choice: yes: …`),
|
|
440
|
+
run `archive.mjs stopp <katalog> --utfall TILLBAKA_TILL_SPEC
|
|
441
|
+
--kalla granskarblock.md` and show the choice in the customer's language; no assessor.
|
|
442
|
+
If `state.json.respec_varv` is already ≥ 1, run instead `--utfall ESKALERA
|
|
405
443
|
--kalla granskarblock.md --orsak "prisavgörande val kvarstår efter respec:
|
|
406
|
-
<valet>"`:
|
|
407
|
-
|
|
408
|
-
`respec`
|
|
409
|
-
BRISTER
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
7. **
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
`spec_synced`,
|
|
416
|
-
|
|
417
|
-
**
|
|
418
|
-
`archive.mjs nasta-steg`,
|
|
419
|
-
|
|
420
|
-
##
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
1. **
|
|
426
|
-
spec,
|
|
427
|
-
- **
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
gid
|
|
432
|
-
- **
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
- **
|
|
437
|
-
"
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
- **
|
|
441
|
-
(
|
|
442
|
-
|
|
443
|
-
-
|
|
444
|
-
|
|
445
|
-
2. **
|
|
446
|
-
(
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
speca
|
|
450
|
-
|
|
451
|
-
`KANDIDAT: KONCEPT`
|
|
452
|
-
|
|
444
|
+
<valet>"`: a human judges, the item never goes a third spec round. When
|
|
445
|
+
the choice has been decided, the answer is written under `## Avgjorda val` in the manifest, then
|
|
446
|
+
`respec` and a new specifier dispatch.
|
|
447
|
+
BRISTER in `state.json` are the wrapper's own files and
|
|
448
|
+
are fixed by the wrapper itself, never via redispatch. VARNING lines are shown but
|
|
449
|
+
stop nothing.
|
|
450
|
+
7. **Reply BLUF:** one line per spec (heading + any flags), then the reviewer block
|
|
451
|
+
in its entirety. Offer to place the specs on the board via request-sync (🔍); do
|
|
452
|
+
it only on explicit request. When an item is placed on the board: set status
|
|
453
|
+
`spec_synced`, rename the slug directory to the minted gid, note the slug
|
|
454
|
+
in `former_slug` and update `request_id` in `state.json`.
|
|
455
|
+
**Close per `## Closing: where the item stands`** (the table from
|
|
456
|
+
`archive.mjs nasta-steg`, the board line, the ISÄR check), never without.
|
|
457
|
+
|
|
458
|
+
## Task: `bedöm`
|
|
459
|
+
|
|
460
|
+
Runs the pricing leg: an impact analysis if it is missing, then scoring with a
|
|
461
|
+
drift guard.
|
|
462
|
+
|
|
463
|
+
1. **Gate: mandatory, never skippable.** Assessment requires a reviewed
|
|
464
|
+
spec, and the approval lives in Asana:
|
|
465
|
+
- **The item has a gid:** read Offer status via request-sync (the main conversation)
|
|
466
|
+
immediately before the run, never from memory or an earlier listing.
|
|
467
|
+
Only `✅ Spec ok` (normalized on the alphabetic core) lets it through.
|
|
468
|
+
Any other status: show it and stop. A pasted spec for an item that has a
|
|
469
|
+
gid does not count: it is the board status that holds, read it anyway.
|
|
470
|
+
- **The item lacks a gid BUT the repo has `.haus-workflow/request.json`:** refuse: the repo is
|
|
471
|
+
connected to a board, so a gid-less item must come in through the intake and be reviewed
|
|
472
|
+
there, not priced on the side. The gate is worth something only if it cannot
|
|
473
|
+
be stepped around.
|
|
474
|
+
- **The item lacks a gid AND the repo lacks a board connection:** ask explicitly
|
|
475
|
+
"Has the spec been approved by a human, and by whom?" via AskUserQuestion and
|
|
476
|
+
run only on yes with a name. Never assume, and never let "the spec looks done"
|
|
477
|
+
or an earlier conversation replace the question.
|
|
478
|
+
- **The item has a gid but lacks a local archive directory:** not supported yet
|
|
479
|
+
(hydration from the board comes with the batch step); say so and stop;
|
|
480
|
+
the reviewer block cannot be recreated from Asana.
|
|
481
|
+
- Note the passed gate in `state.json.approval` (the manual case with `by`).
|
|
482
|
+
The next `bedöm` reads the status again.
|
|
483
|
+
2. **Form check.** Run `node <skillkatalog>/scripts/precheck.mjs <katalog> --impact`
|
|
484
|
+
(without `--impact` if the analysis does not exist yet). A `spec.md` with more than one
|
|
485
|
+
spec block is a BRIST: split the item per the archive rule before anything is dispatched.
|
|
486
|
+
Other BRISTER → fix the link that is defective (if the spec: back to the
|
|
487
|
+
speca step) before anything expensive is dispatched.
|
|
488
|
+
**The `KANDIDAT:` line decides before everything else.** If precheck writes
|
|
489
|
+
`KANDIDAT: KONCEPT` or `KANDIDAT: DIAGNOS` (the line always comes, even
|
|
490
|
+
at exit 1), the item is a candidate: skip steps 3 to 7, run
|
|
453
491
|
`archive.mjs stopp <katalog> --utfall <utfallet ur raden> --kalla spec.md`
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
3. **
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
`kallmanifest.md`
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
(ADR-0113:
|
|
469
|
-
|
|
470
|
-
(
|
|
471
|
-
|
|
492
|
+
if `stopp` is not already set (speca step 6 has usually done it;
|
|
493
|
+
an identical repeat call is harmless), and route the item as the stop outcome
|
|
494
|
+
below. No analysis and no score may ever be dispatched on a
|
|
495
|
+
candidate.
|
|
496
|
+
3. **Analysis.** If `impact.md` is missing, or precheck rejects it on
|
|
497
|
+
source-material provenance (the analysis was made against another spec): this requires a repo in
|
|
498
|
+
the session (otherwise stop: a guessed analysis is forbidden by the skill itself).
|
|
499
|
+
Dispatch `request-assessor` per the prompt template: the spec's reviewer block
|
|
500
|
+
is sent along as internal source material (the same trust domain; the flags there are
|
|
501
|
+
often the analysis's most important input). The prompt's `KÄLLOR:` line carries
|
|
502
|
+
`kallmanifest.md` from the directory verbatim, or `inga` when the file is missing,
|
|
503
|
+
and the `SPRÅK:` line carries the manifest's `SPRÅK:` value (#521: the customer text's
|
|
504
|
+
language is never guessed again, the same line goes to the scorer in step 4);
|
|
505
|
+
the assessor verifies the manifest itself and must never take it as true
|
|
506
|
+
(ADR-0113: the assessor explored the repo from zero even though the manifest already
|
|
507
|
+
existed). Archive the reply as `impact.md`
|
|
508
|
+
(including its "## For the reviewer" block: show the block to the user,
|
|
509
|
+
never to customer fields), stamp with
|
|
472
510
|
`node <skillkatalog>/scripts/archive.mjs add-run <katalog> --file impact.md
|
|
473
511
|
--skill request-assess-impact --agent request-assessor
|
|
474
|
-
--model <ur MODELL-svansraden>` (
|
|
475
|
-
`spec.md`).
|
|
476
|
-
`AVVIKELSE: <etikett>`
|
|
477
|
-
`**
|
|
478
|
-
`motsägelse`
|
|
479
|
-
|
|
512
|
+
--model <ur MODELL-svansraden>` (the script sets `underlag_sha256` against
|
|
513
|
+
`spec.md`). Run precheck again with `--impact`. Then read the line
|
|
514
|
+
`AVVIKELSE: <etikett>` from the precheck output (the label on the analysis's row
|
|
515
|
+
`**Deviation from the spec:**`; not to be confused with the agents' tail line):
|
|
516
|
+
`motsägelse` means that the spec presupposes something that does not hold in
|
|
517
|
+
the system, and then run
|
|
480
518
|
`node <skillkatalog>/scripts/archive.mjs stopp <katalog> --utfall TILLBAKA_TILL_SPEC --kalla impact.md`
|
|
481
|
-
(
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
`impact-avvisad-<datum>.md`
|
|
488
|
-
|
|
489
|
-
4. **
|
|
490
|
-
|
|
491
|
-
5. **
|
|
492
|
-
plus
|
|
519
|
+
(the command sets status `assessed`), skip steps 4 to 7 and route
|
|
520
|
+
the item as the stop outcome below: no scorer is dispatched on source material
|
|
521
|
+
that already says the spec must go back. `ingen` and `precisering` go
|
|
522
|
+
on to step 4. If precheck rejects the new
|
|
523
|
+
draft → redispatch the assessor ONCE per the redispatch rule, and give
|
|
524
|
+
the rejected draft a receipt exactly as in speca step 6: archive it as
|
|
525
|
+
`impact-avvisad-<datum>.md` and stamp a runs row of its own with the check's
|
|
526
|
+
error lines in `--note` (ADR-0099).
|
|
527
|
+
4. **Pricing.** Dispatch `request-scorer` per the prompt template.
|
|
528
|
+
No price list, no repo, no other items' documents in the prompt.
|
|
529
|
+
5. **Anchor resolution and contract check.** Write the agent's whole reply text (JSON
|
|
530
|
+
plus tail lines) to a file and run
|
|
493
531
|
`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`
|
|
495
|
-
|
|
496
|
-
(#503). `--sprak`
|
|
497
|
-
|
|
498
|
-
`kallmanifest.md`
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
`resolve-anchors.mjs` (
|
|
505
|
-
OLÖST/FLERTYDIG
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
BLUF
|
|
509
|
-
`impact.md`
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
(
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
BROTT
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
6. **
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
OLIKA
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
-
|
|
537
|
-
|
|
538
|
-
-
|
|
532
|
+
`--state` gives contract-check the split status: `OVER_SKALAN` on something that is not
|
|
533
|
+
a split part is rejected, and a no-ceiling outcome without a step against A16 is rejected
|
|
534
|
+
(#503). `--sprak` gives contract-check the customer text's language, the same value as
|
|
535
|
+
the prompt's `SPRÅK:` line (from `experiment.json` in utvärdera, from `SPRÅK:` in
|
|
536
|
+
`kallmanifest.md` here): customer text with at least two function words from the wrong language
|
|
537
|
+
is rejected as BROTT and goes to redispatch, never to the customer-text fix, which
|
|
538
|
+
swaps words but does not translate (#521).
|
|
539
|
+
The script strips the tail lines (`MODELL:`, `LÄSTA:`, any `AVVIKELSE:`),
|
|
540
|
+
repairs what the dispatcher already knows (`request_id`, enum form,
|
|
541
|
+
digit strings, the LÄSTA line's path form) and runs check-lasta,
|
|
542
|
+
`resolve-anchors.mjs` (evidence anchors to verbatim source blocks;
|
|
543
|
+
OLÖST/FLERTYDIG is a contract breach) and contract-check in one process; a
|
|
544
|
+
protocol-term breach in a pure customer-text field is rewritten by a cheap model
|
|
545
|
+
instead of costing a new scorer run. The `REPARERAT:` lines are shown in
|
|
546
|
+
the BLUF reply and written into the runs row's `--note`.
|
|
547
|
+
`impact.md` is ALWAYS sent to contract-check, never only the spec:
|
|
548
|
+
the script reads the label on `**Deviation from the spec:**` and rejects every
|
|
549
|
+
outcome other than TILLBAKA_TILL_SPEC when the analysis carries `contradiction`
|
|
550
|
+
(the contradiction gate in the script is the belt, step 3 the braces; an item that
|
|
551
|
+
has still reached this point has source material the script must get to see). The same script requires
|
|
552
|
+
every `bracket.steps[i]` to be exactly `{anchor, relation, grund}`.
|
|
553
|
+
BROTT that remain after the repairs → redispatch ONCE per
|
|
554
|
+
the redispatch rule, with the error lines in the prompt. If breaches remain: show them and
|
|
555
|
+
stop without archiving the reply as a valid round, but do not throw it away:
|
|
556
|
+
archive it as `score-pricing-vN-avvisad.json` (outside the N count) with
|
|
557
|
+
the check's error lines in a runs row, so even failed runs have a
|
|
558
|
+
receipt. An `AVVIKELSE:` line is always shown to the user.
|
|
559
|
+
6. **Double run (drift guard).** Run 2 ALWAYS starts at the same time as
|
|
560
|
+
run 1, with the same prompt and in a new isolated run; in production
|
|
561
|
+
both Agent calls are dispatched at once (#500, ADR-0113), the prompts are
|
|
562
|
+
identical and the runs independent. Trigger: the score is ≥ 1 (ADR-0098),
|
|
563
|
+
or the outcome is `OVER_SKALAN` (ADR-0116: that outcome sends the item
|
|
564
|
+
to a manual offer and is price-bearing; two OVER_SKALAN with different floors are
|
|
565
|
+
OLIKA and go to a ruling). Other stop outcomes are not double-run: DELA_UPP is
|
|
566
|
+
rerun per part, KONCEPT/DIAGNOS/TILLBAKA_TILL_SPEC go to a human anyway.
|
|
567
|
+
The threshold only decides whether the pair COUNTS as a double run, never whether
|
|
568
|
+
run 2 starts. If run 1 becomes a stop outcome other than OVER_SKALAN, or
|
|
569
|
+
a score below 1, run 2 is archived as `-run2` with `superseded_by` against
|
|
570
|
+
the main file and a note that it was not required, it never counts as a
|
|
571
|
+
double run, and the stop routing below applies as before. In other cases
|
|
572
|
+
the pair is compared as below; the skill's own acceptance test is that two independent
|
|
573
|
+
runs give the same result.
|
|
574
|
+
- Run the whole of step 5 (anchor resolution, then contract check) on the new
|
|
575
|
+
reply first, with its own redispatch budget (ONE).
|
|
576
|
+
- Then compare deterministically:
|
|
539
577
|
`node <skillkatalog>/scripts/compare-scores.mjs <poäng1.json> <poäng2.json>`.
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
(OLIKA),
|
|
543
|
-
|
|
544
|
-
- **LIKA** →
|
|
545
|
-
runs
|
|
546
|
-
|
|
547
|
-
`-run2
|
|
548
|
-
`detaljer`
|
|
549
|
-
- **OLIKA** →
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
Status `assessed`.
|
|
553
|
-
request-sync
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
- **LIKA_TOLERANS** (
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
`OVER_SKALAN`
|
|
573
|
-
|
|
574
|
-
`score: null`)
|
|
575
|
-
|
|
576
|
-
- Exit 2
|
|
577
|
-
|
|
578
|
-
7. **
|
|
578
|
+
The script also diffs the inner steps: the same anchor in `bracket.steps` read
|
|
579
|
+
as `storre` in one run and `mindre` in the other is a hard diff row
|
|
580
|
+
(OLIKA), never a tolerance row. The floor and ceiling anchors are exempt, they
|
|
581
|
+
flip by construction when the bands are adjacent and are owned by the tolerance rule.
|
|
582
|
+
- **LIKA** → run 1 holds: `double_run: "bekräftad"` on its
|
|
583
|
+
runs row. The run 2 file is archived as `score-pricing-vN-run2.json`
|
|
584
|
+
with the SAME N and `superseded_by` set to the run 1 file.
|
|
585
|
+
`-run2` files are NEVER counted when the next N is determined, and guard and
|
|
586
|
+
`detaljer` always read the file without a suffix.
|
|
587
|
+
- **OLIKA** → the ruling is the human's, never the machine's. Archive both
|
|
588
|
+
runs (run 1 without a suffix, run 2 as `-run2`; on both
|
|
589
|
+
rows `double_run: "olika"` with `superseded_by: null`).
|
|
590
|
+
Status `assessed`. No customer text may go to
|
|
591
|
+
request-sync while the hold stands (`archive.mjs sparr` is the interpreter).
|
|
592
|
+
Show in the BLUF reply the evidence behind the disagreement, side by side:
|
|
593
|
+
the diff rows (outcome, score, anchor, confidence) have no
|
|
594
|
+
row link to the evidence, `drivers[]` is a list of its own, so show
|
|
595
|
+
all `drivers[].citat`/`pastaende` from both runs
|
|
596
|
+
as support for the whole outcome. It was the evidence that decided the ruling in
|
|
597
|
+
every measured genuine disagreement. Then refer to `döm <item>` and
|
|
598
|
+
STOP: no rerun, no escalation, no choice of run.
|
|
599
|
+
- **LIKA_TOLERANS** (a score difference ≤ 0.5 with the same outcome, regardless of
|
|
600
|
+
confidence, a confidence difference alone never rejects a pair within
|
|
601
|
+
the tolerance) → the pair counts as confirmed. The run the verdict line points out
|
|
602
|
+
(the highest score) becomes the main file `score-pricing-vN.json`,
|
|
603
|
+
and the other one is archived as `-run2` with `superseded_by` set against
|
|
604
|
+
the main file, regardless of which of them was run 1. That is
|
|
605
|
+
deliberate: both runs have passed the contract check and
|
|
606
|
+
the difference lies within the tolerance. Set `double_run: "tolerans"` on
|
|
607
|
+
both runs rows. The tolerance is a deliberate pricing rule: the highest
|
|
608
|
+
value protects the margin, and an average is still never
|
|
609
|
+
computed. Show the tolerance rows in the BLUF reply. A pair of two
|
|
610
|
+
`OVER_SKALAN` with the SAME floor where only the confidence differs
|
|
611
|
+
also writes `LIKA_TOLERANS`, but with no score to compare (OVER_SKALAN carries
|
|
612
|
+
`score: null`) run 1 wins deterministically, never "the highest
|
|
613
|
+
score" (ADR-0116).
|
|
614
|
+
- Exit 2 from the comparison script is a dispatcher error (incomparable
|
|
615
|
+
runs): show the error and stop, never report LIKA.
|
|
616
|
+
7. **Archive** the approved JSON replies. Fetch the round's N with
|
|
579
617
|
`node <skillkatalog>/scripts/archive.mjs next-n <katalog> --typ score`,
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
`archive.mjs add-run` (
|
|
583
|
-
MODELL
|
|
584
|
-
|
|
585
|
-
partial-rerun
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
618
|
+
write the files (`score-pricing-vN.json`, run 2
|
|
619
|
+
as `-run2`, rejected ones as `-avvisad`), and stamp EVERY run with
|
|
620
|
+
`archive.mjs add-run` (flags for file, skill, agent, model from
|
|
621
|
+
the MODELL tail line as before, reference hash via `--ref` for the skill's
|
|
622
|
+
reference file (see the runs contract), double-run outcome, superseded-by,
|
|
623
|
+
partial-rerun and pairs-with where they apply). The script reads the effort itself
|
|
624
|
+
from the agent definition under the Claude config directory when `--effort`
|
|
625
|
+
is omitted, so omit the flag; `--model` stays mandatory and comes
|
|
626
|
+
from the tail line, never from the definition (under `utvärdera` the agents run
|
|
627
|
+
with an override, so the definition's model is by construction not the one that
|
|
628
|
+
ran). Example for the main file of a confirmed pair:
|
|
591
629
|
`node <skillkatalog>/scripts/archive.mjs add-run <katalog>
|
|
592
630
|
--file score-pricing-v1.json --skill request-score
|
|
593
631
|
--agent request-scorer --model <ur MODELL-svansraden>
|
|
594
632
|
--ref poangskala.md=<hash via
|
|
595
633
|
ref-hash.mjs på request-score/references/poangskala.md>
|
|
596
|
-
--double-run bekräftad`. `--double-run`
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
`--superseded-by <fil>`
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
`archive.mjs set-status <katalog> priced` (
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
8. **
|
|
607
|
-
`node <skillkatalog>/scripts/archive.mjs bluf <katalog>`
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
per
|
|
613
|
-
INPUT_SAKNAS, AVVIKELSE
|
|
614
|
-
`detaljer <
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
(
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
**
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
- `DELA_UPP` →
|
|
625
|
-
`split`
|
|
626
|
-
DELA_UPP
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
- `OVER_SKALAN` →
|
|
632
|
-
|
|
633
|
-
- `TILLBAKA_TILL_SPEC` / `KONCEPT` / `DIAGNOS` →
|
|
634
|
-
|
|
635
|
-
`spec.md`, `
|
|
636
|
-
`granskarblock.md`, ADR-0118)
|
|
637
|
-
`archive.mjs stopp`
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
`granskarblock.md`).
|
|
641
|
-
- `ESKALERA` / `INPUT_SAKNAS` →
|
|
642
|
-
|
|
634
|
+
--double-run bekräftad`. `--double-run` takes ONLY the three
|
|
635
|
+
literals `bekräftad`, `olika` or `tolerans` (the script refuses everything
|
|
636
|
+
else; the flag name with a hyphen, never `--double_run` with an underscore
|
|
637
|
+
as in the JSON field). Omit the flag entirely when no double run has been done.
|
|
638
|
+
`--superseded-by <fil>` and `--partial-rerun` (without a value) are added where
|
|
639
|
+
they apply, see the runs contract. The stamping rules
|
|
640
|
+
live in the script and its tests, not here. Set the status via
|
|
641
|
+
`archive.mjs set-status <katalog> priced` (the script refuses on a hold,
|
|
642
|
+
a stop outcome and an experiment; see the runs contract)
|
|
643
|
+
or `assessed`.
|
|
644
|
+
8. **Reply BLUF:** the table comes from
|
|
645
|
+
`node <skillkatalog>/scripts/archive.mjs bluf <katalog>` and is pasted
|
|
646
|
+
as it is (`| post | poäng | utfall | konfidens | anm |`, one row per
|
|
647
|
+
directory and part; the score from the files, stop outcomes from `stopp`, OLIKA and
|
|
648
|
+
tolerance in `anm`), NEVER handwritten: a handwritten table
|
|
649
|
+
misreported in a live run. Under the table, one line
|
|
650
|
+
per deviation worth seeing (ESKALERA, DELA_UPP,
|
|
651
|
+
INPUT_SAKNAS, AVVIKELSE lines). The JSON is not shown in the chat:
|
|
652
|
+
`detaljer <item>` does that. Writing back to the board happens via request-sync
|
|
653
|
+
in the main conversation, only on explicit request, and it is that move
|
|
654
|
+
that takes the item ✅ Spec ok → ⚖️ Poängsättning redo för granskning per
|
|
655
|
+
the sync's rules. **Close per `## Closing: where the item stands`**
|
|
656
|
+
(the table from `archive.mjs nasta-steg`, the board line, the ISÄR check, and
|
|
657
|
+
a stop outcome or OLIKA as a choice the user must make), never without.
|
|
658
|
+
|
|
659
|
+
**Stop outcomes are routed, never interpreted.** A stop outcome is archived as the round's
|
|
660
|
+
file (step 7), the status becomes `assessed`, never `priced`, and the item is routed:
|
|
661
|
+
|
|
662
|
+
- `DELA_UPP` → propose a new analysis round per part; the parts become directories of their own with
|
|
663
|
+
`split` set (`part`/`of`), and a part without a ceiling becomes `OVER_SKALAN`, never
|
|
664
|
+
DELA_UPP again. Sub-items inherit, per the skill, the item's spec and never write
|
|
665
|
+
one of their own: the parent's `spec.md` and `granskarblock.md` are COPIED unchanged into
|
|
666
|
+
each part directory, separately (one reviewer block per part directory, ADR-0118,
|
|
667
|
+
no part directory shares its block with another), while `impact.md` is written per
|
|
668
|
+
part by the new analysis round.
|
|
669
|
+
- `OVER_SKALAN` → the item is quoted separately; never split further (the split is done
|
|
670
|
+
one step, the skill's own rule).
|
|
671
|
+
- `TILLBAKA_TILL_SPEC` / `KONCEPT` / `DIAGNOS` → back to the spec step
|
|
672
|
+
or the customer dialogue respectively; show the reason. If the outcome came from the scripts (a candidate in
|
|
673
|
+
`spec.md`, `contradiction` in `impact.md`, a price-decisive open choice in
|
|
674
|
+
`granskarblock.md`, ADR-0118), `state.json.stopp` carries it via
|
|
675
|
+
`archive.mjs stopp` and there is no round file; if it came from the scorer,
|
|
676
|
+
the round file is archived as before and `stopp` stays `null` (the command takes
|
|
677
|
+
only the three script-proven sources: `spec.md`, `impact.md`,
|
|
678
|
+
`granskarblock.md`). A respec resets `stopp`.
|
|
679
|
+
- `ESKALERA` / `INPUT_SAKNAS` → show the reason and stop, or a price-decisive
|
|
680
|
+
choice that remains after respec (source `granskarblock.md`, reason in
|
|
643
681
|
`stopp.orsak`, ADR-0118).
|
|
644
682
|
|
|
645
|
-
##
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
📥 Inkommet, 🔍 Spec redo för granskning, ✅ Spec ok
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
##
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
- **
|
|
662
|
-
(
|
|
663
|
-
fuzzy
|
|
664
|
-
|
|
665
|
-
- **
|
|
666
|
-
|
|
667
|
-
- **
|
|
668
|
-
request-sync
|
|
669
|
-
(🔍 Spec redo för granskning, ⚖️ Poängsättning redo för granskning).
|
|
670
|
-
|
|
671
|
-
- **`godkänn <nr|gid>`:** 🔍 → ✅ Spec ok,
|
|
672
|
-
- **`underkänn <nr|gid> <
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
- **
|
|
676
|
-
`approval: {source: "manual", date}`
|
|
677
|
-
|
|
678
|
-
-
|
|
679
|
-
|
|
680
|
-
- **
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
##
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
`godkänn` (
|
|
688
|
-
(`archive.mjs sparr` exit 1),
|
|
689
|
-
|
|
690
|
-
**
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
`archive.mjs`
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
**
|
|
698
|
-
`experiment.json`
|
|
699
|
-
|
|
700
|
-
`experiment.json`
|
|
701
|
-
|
|
702
|
-
`"väntar på dom"
|
|
703
|
-
(
|
|
704
|
-
|
|
705
|
-
1. **
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
2. **
|
|
709
|
-
- **"
|
|
710
|
-
|
|
711
|
-
(`--axel classification`
|
|
712
|
-
|
|
713
|
-
`--n`
|
|
714
|
-
|
|
715
|
-
`score-pricing-v<n>.json` (
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
`--galler-fil`
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
(
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
- **"
|
|
731
|
-
|
|
732
|
-
`partial_rerun`
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
- **"
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
3. **
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
v0.2
|
|
746
|
-
|
|
747
|
-
4. **
|
|
748
|
-
`archive.mjs nasta-steg`
|
|
749
|
-
sync/godkänn)
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
**
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
`bedöm`
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
via `archive.mjs utslag --typ viktning` (
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
683
|
+
## Task: `status`
|
|
684
|
+
|
|
685
|
+
Run request-sync's queue reading in the main conversation for the states
|
|
686
|
+
📥 Inkommet, 🔍 Spec redo för granskning, ✅ Spec ok and ⚖️ Poängsättning redo för granskning.
|
|
687
|
+
Reply as a BLUF table with **row numbers**: `nr · item · Offer status`.
|
|
688
|
+
Save the nr → gid mapping in the session (the gid need not be shown in
|
|
689
|
+
the table): the numbers are what `godkänn`/`underkänn`/`bedöm`/`detaljer` refer
|
|
690
|
+
to, and every verb looks up and acts on the **gid**, never on the number itself.
|
|
691
|
+
No JSON in the chat.
|
|
692
|
+
|
|
693
|
+
## Tasks: `godkänn` / `underkänn`
|
|
694
|
+
|
|
695
|
+
Shortcuts to the same lever as in Asana: **Offer status on the board remains the only
|
|
696
|
+
decision**. The verbs make the decision faster to carry out, never easier to
|
|
697
|
+
get wrong:
|
|
698
|
+
|
|
699
|
+
- **Addressing:** a row number from the latest `status` listing in the same session
|
|
700
|
+
(looked up to a gid via the saved mapping), or a gid directly. NEVER
|
|
701
|
+
fuzzy heading matching: the wrong item approved is worse than a stop. If there is no
|
|
702
|
+
fresh listing and no gid: run `status` first.
|
|
703
|
+
- **One item per verb call.** `godkänn 2 och 4` is two calls in a row, each
|
|
704
|
+
reading and moving its own item. Never batch approval on "everything looks fine".
|
|
705
|
+
- **State gate for both verbs:** read the item's current Offer status via
|
|
706
|
+
request-sync first. The verbs act only from the review states
|
|
707
|
+
(🔍 Spec redo för granskning, ⚖️ Poängsättning redo för granskning). If
|
|
708
|
+
the item stands anywhere else: show the status and stop instead of guessing.
|
|
709
|
+
- **`godkänn <nr|gid>`:** 🔍 → ✅ Spec ok, and ⚖️ → 📤 Klar – till kund respectively.
|
|
710
|
+
- **`underkänn <nr|gid> <reason>`:** the reason is mandatory: without a reason, ask
|
|
711
|
+
for it. request-sync adds the reason as a comment on the item, raises
|
|
712
|
+
the iteration counter and moves the status backward per its backward-move rules.
|
|
713
|
+
- **An item without a board** (archive only): `godkänn` notes
|
|
714
|
+
`approval: {source: "manual", date}` in `state.json` after an explicit
|
|
715
|
+
confirmation question. Say clearly that this is not visible to anyone other than the archive.
|
|
716
|
+
- The verbs touch Offer status and comments, never Score, Class or
|
|
717
|
+
description fields.
|
|
718
|
+
- **Close per `## Closing: where the item stands`** when the item has an
|
|
719
|
+
archive directory: the table from `archive.mjs nasta-steg` plus the board line, so
|
|
720
|
+
the move is visible together with what it opens up for (`bedöm`, customer, nothing).
|
|
721
|
+
|
|
722
|
+
## Task: `döm`
|
|
723
|
+
|
|
724
|
+
The human's ruling on an item whose double run wrote OLIKA. Addressing as for
|
|
725
|
+
`godkänn` (row number or gid, never fuzzy). Requires the hold to stand
|
|
726
|
+
(`archive.mjs sparr` exit 1), otherwise: show the status and stop.
|
|
727
|
+
|
|
728
|
+
**A variant directory (from `utvärdera`) is addressed with its path directly,
|
|
729
|
+
never with a row number or gid.** The experiment's slug is the same string that an
|
|
730
|
+
ordinary gid would be, so gid addressing would resolve to the item directory
|
|
731
|
+
at the top level, not to the variant directory under `experiments/<slug>/`.
|
|
732
|
+
`archive.mjs` takes a directory as its argument regardless of where it lies, so
|
|
733
|
+
the path works identically.
|
|
734
|
+
|
|
735
|
+
**A ruling on a directory under `experiments/` never rewrites
|
|
736
|
+
`experiment.json` itself.** `archive.mjs utslag` and `archive.mjs sparr`
|
|
737
|
+
act only on the variant directory's own `state.json`. The status in
|
|
738
|
+
`experiment.json` is not updated by `döm`: it is updated by `utvärdera`,
|
|
739
|
+
run again for the same item, whose resumability rule re-evaluates a
|
|
740
|
+
`"väntar på dom"` variant and makes the outcome visible to the report when it runs
|
|
741
|
+
(see `utvärdera`).
|
|
742
|
+
|
|
743
|
+
1. **Show the source material.** Run the comparison script on the current pair and show
|
|
744
|
+
every diff row with the evidence side by side (as in bedöm step 6). No
|
|
745
|
+
summary instead of the evidence: the ruling is made on it.
|
|
746
|
+
2. **Ask the reviewer** (AskUserQuestion) with three paths.
|
|
747
|
+
- **"Run N holds"** (N is 1 or 2, whichever of the two measured
|
|
748
|
+
runs was right): write the ruling via `archive.mjs utslag <katalog> --typ viktning --beslut "<ordagrant>" --beslutad-av <namn> --axel score --n <n> --galler-fil <fil>`.
|
|
749
|
+
(`--axel classification` remains solely for rulings in older archives
|
|
750
|
+
with a class axis, before ADR-0098.)
|
|
751
|
+
`--n` is NOT the run number in the heading above: it is the CURRENT
|
|
752
|
+
round, read from the latest pricing file
|
|
753
|
+
`score-pricing-v<n>.json` (never `next-n`'s output, which points to the
|
|
754
|
+
NEXT, not yet run round). `--galler-fil` is the archived file
|
|
755
|
+
the reviewer rules in
|
|
756
|
+
favor of: run 1 → `score-pricing-v<n>.json` without a suffix,
|
|
757
|
+
run 2 → `score-pricing-v<n>-run2.json`. After an ordered
|
|
758
|
+
rerun (the next path) there are THREE archived files, and
|
|
759
|
+
`--galler-fil` can point to ANY of them: the rerun's own
|
|
760
|
+
file (version = the current `n`), or either of the two original
|
|
761
|
+
runs' files from a LOWER round, if the reviewer rules that one of
|
|
762
|
+
them was right all along. The script refuses only a HIGHER round than `--n`
|
|
763
|
+
(a ruling can never apply to a round that did not exist when it was written) and a
|
|
764
|
+
file that was not actually archived via `add-run`. Run `archive.mjs sparr`
|
|
765
|
+
afterwards: exit 0 means that the hold is lifted, exit 1 means that
|
|
766
|
+
something is still waiting (the output names what).
|
|
767
|
+
No files are renamed or rewritten.
|
|
768
|
+
- **"Order a rerun":** ONE new run, no forced doubling: dispatch
|
|
769
|
+
the scorer (the ordinary prompt template and contract chain), archive with
|
|
770
|
+
`partial_rerun` and `pairs_with` against the standing file. The new
|
|
771
|
+
row waits, just like the original row, for a ruling (`archive.mjs sparr`
|
|
772
|
+
holds it on its own, regardless of what it happens to carry in `double_run`): show
|
|
773
|
+
the script diff against BOTH standing runs and go to step 2 again,
|
|
774
|
+
it is still the reviewer who rules. If the reviewer wants two new
|
|
775
|
+
measurements, two are ordered.
|
|
776
|
+
- **"The source material is wrong":** the reviewer's decision is appended to `impact.md` as a
|
|
777
|
+
quote block per the template below, the assessor is rerun (a new `impact.md`),
|
|
778
|
+
after which `bedöm` runs a new full round. A run against
|
|
779
|
+
old source material is never paired with one against new.
|
|
780
|
+
3. **Calibration notice.** If the disagreement is genuine (a question or an anchor choice that
|
|
781
|
+
two valid runs read differently, not a label): write `archive.mjs utslag <katalog> --typ kalibrering --beslutad-av <namn> --fraga <id> --lasning1 "<körning 1:s läsning, en mening>" --lasning2 "<körning 2:s läsning>"`.
|
|
782
|
+
The notice is raw material for the next instruction version (the mechanism behind
|
|
783
|
+
v0.2 to v0.6) and is never read into any prompt. `archive.mjs kalibrering <arkivrot>`
|
|
784
|
+
lists all of them.
|
|
785
|
+
4. **Close per `## Closing: where the item stands`**: the table from
|
|
786
|
+
`archive.mjs nasta-steg` shows whether the hold has been lifted (the row then points on to
|
|
787
|
+
sync/godkänn) or is still waiting (`döm` again after an ordered
|
|
788
|
+
rerun), plus the board line.
|
|
789
|
+
|
|
790
|
+
**Reviewer rulings are written as quotes, never as prose.** When the reviewer's
|
|
791
|
+
decision must go into the source material, that is, when it changes or refines a
|
|
792
|
+
fact in the analysis, or for another reason must stand in the source material for
|
|
793
|
+
traceability: it is appended last in `impact.md` as a delimited block per
|
|
794
|
+
the template below, the decision quoted verbatim, never reworded by
|
|
795
|
+
the dispatcher and never woven into the analysis's running text. A freely worded
|
|
796
|
+
ruling sentence is read by the next run as a fact and steers
|
|
797
|
+
the assessment, which was exactly the error in the pilot runs, therefore always the template,
|
|
798
|
+
never free prose. If the decision requires an actual factual change in the analysis: rerun
|
|
799
|
+
the assessor (a new `impact.md`). The source material has changed in both cases, so
|
|
800
|
+
`bedöm` runs a new full round (see step 2 above, the path "The source material is wrong").
|
|
801
|
+
At the same time, write the receipt `archive.mjs utslag <katalog> --typ sakandring --beslut "<ordagrant>" --beslutad-av <namn>`
|
|
802
|
+
in `state.json` (see `granskarutslag` above): the quote in `impact.md` is
|
|
803
|
+
the source material, the sakandring row is only the audit trail. A pure
|
|
804
|
+
weighting ruling, where the reviewer only states that a run was right
|
|
805
|
+
without touching any fact, NEVER belongs here: it is written instead
|
|
806
|
+
via `archive.mjs utslag --typ viktning` (see step 2 above, the path "Run N
|
|
807
|
+
holds"), precisely so that the source material stays unchanged. The block is internal
|
|
808
|
+
source material with the same status as the For the reviewer block and never goes
|
|
809
|
+
along in customer text.
|
|
772
810
|
|
|
773
811
|
```
|
|
774
|
-
##
|
|
812
|
+
## Reviewer ruling (round <N>, <YYYY-MM-DD>)
|
|
775
813
|
|
|
776
|
-
> <
|
|
814
|
+
> <the reviewer's decision, verbatim>
|
|
777
815
|
|
|
778
|
-
|
|
779
|
-
|
|
816
|
+
The ruling applies only to the weighting between the runs and changes no
|
|
817
|
+
facts in the analysis above.
|
|
780
818
|
```
|
|
781
819
|
|
|
782
|
-
##
|
|
783
|
-
|
|
784
|
-
`detaljer <gid|slug|nr> [
|
|
785
|
-
|
|
786
|
-
`poäng`, `state`)
|
|
787
|
-
classification
|
|
788
|
-
`-pricing-vN.json`
|
|
789
|
-
|
|
790
|
-
##
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
`default`
|
|
798
|
-
|
|
799
|
-
`<modell>/`.
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
**
|
|
805
|
-
`rapport`,
|
|
806
|
-
`utvärdera rapport` (
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
**
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
**
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
**
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
**
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
under
|
|
835
|
-
|
|
836
|
-
**
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
**
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
1. **
|
|
851
|
-
|
|
852
|
-
Asana
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
820
|
+
## Task: `detaljer`
|
|
821
|
+
|
|
822
|
+
`detaljer <gid|slug|nr> [step]`: print archived files for the item: without
|
|
823
|
+
a step, a list of what exists; with a step (`spec`, `granskarblock`, `analys`,
|
|
824
|
+
`poäng`, `state`) the file's content raw; `klass` reads older archives'
|
|
825
|
+
classification files (before ADR-0098). For `klass`/`poäng`: the latest
|
|
826
|
+
`-pricing-vN.json` without a suffix (never `-run2`/`-avvisad`).
|
|
827
|
+
|
|
828
|
+
## Task: `utvärdera`
|
|
829
|
+
|
|
830
|
+
Model evaluation: `utvärdera <item…> <model…>`, with at least one model. Runs an
|
|
831
|
+
item through the whole flow once per named model, each round with all
|
|
832
|
+
three agents (the specifier, the assessor, the scorer) forced to the round's
|
|
833
|
+
model, so that each round measures an actual model difference instead of noise between
|
|
834
|
+
a fixed production mix and a single experiment model.
|
|
835
|
+
`default` as the model value gives the agent defaults (today's production mix) and
|
|
836
|
+
runs without an override; the directory name then becomes `baseline/`, for all other values
|
|
837
|
+
`<modell>/`. Archive all output side by side under the experiment area.
|
|
838
|
+
The task gathers material; the human analyzes. No ruling is made on which
|
|
839
|
+
model is best, and no ruling is simulated: an OLIKA in any round's
|
|
840
|
+
double run waits for `döm`, just as in production.
|
|
841
|
+
|
|
842
|
+
**Subcommands are resolved first.** If the only argument is `sammanställ` or
|
|
843
|
+
`rapport`, it is the subcommand `utvärdera sammanställ` or
|
|
844
|
+
`utvärdera rapport` respectively (see the sections below). The argument rules below never apply to
|
|
845
|
+
those words; they are classified neither as a model nor as an item id.
|
|
846
|
+
|
|
847
|
+
**The flags are removed first.** `--om` and `--hoppa-över` are lifted out of
|
|
848
|
+
the argument list BEFORE the scan
|
|
849
|
+
below begins, and count neither as a model nor as an item id. Without that step a flag stops the scan, zero models
|
|
850
|
+
are found, and the command fails on "At least one model is required" even though the model
|
|
851
|
+
is there.
|
|
852
|
+
|
|
853
|
+
**Argument parsing.** The remaining trailing arguments are decided right to left
|
|
854
|
+
to find the BOUNDARY with the item id list: an argument counts as a model only
|
|
855
|
+
if it is a known agent model name (`fable`, `opus`, `sonnet`, `haiku`, …) or
|
|
856
|
+
the literal `default`, everything else is read as an item id, and the scan stops at
|
|
857
|
+
the first argument that is not a model name. An argument that is neither
|
|
858
|
+
a known model name nor a valid gid/slug is an ERROR, never a guess:
|
|
859
|
+
show the error and stop instead of gambling on which side of the boundary it
|
|
860
|
+
belongs. The model arguments the scan finds then form the model list in
|
|
861
|
+
DECLARATION ORDER, left to right.
|
|
862
|
+
|
|
863
|
+
**At least one model is required.** If the scan finds no model at all, it is an ERROR,
|
|
864
|
+
never a default. A command that costs wall-clock time and money must say which
|
|
865
|
+
models it runs; a silent default set means that a run can start with
|
|
866
|
+
a different model than the one intended, and that cost shows only afterwards.
|
|
867
|
+
|
|
868
|
+
**Duplicates are refused.** If the same model value stands twice in the list, refuse
|
|
869
|
+
the run with an error message that explains why: two equal values point to
|
|
870
|
+
the same directory name and measure no difference against each other. If you want to run the same
|
|
871
|
+
model once more, do it in a separate invocation; then it becomes a new run
|
|
872
|
+
under the same model per the rule in The experiment area.
|
|
873
|
+
|
|
874
|
+
**A named model touches only its own directory.** Other models' directories
|
|
875
|
+
are neither read nor written. That is what allows a comparison to be built in
|
|
876
|
+
stages: `utvärdera <gid> opus sonnet` today and `utvärdera <gid> fable` a
|
|
877
|
+
week later give three models under the same item, without the first two being rerun.
|
|
878
|
+
|
|
879
|
+
**A rerun is asked for, never assumed.** If the invocation names a model that
|
|
880
|
+
already has runs under it, stop BEFORE anything expensive starts and ask whether they
|
|
881
|
+
are to be rerun. Ask ONE question that covers all colliding models, not one per
|
|
882
|
+
model. The answer can be given in advance with `--om` (rerun) or `--hoppa-över` (do not touch
|
|
883
|
+
them); if the invocation carries either of them, no question is asked.
|
|
884
|
+
|
|
885
|
+
A rerun never overwrites the old run, it becomes `k<N+1>`.
|
|
886
|
+
The question is therefore about the cost, not the risk of losing data.
|
|
887
|
+
|
|
888
|
+
1. **Source material.** As in `speca` step 1: a raw request or a gid; with a gid the
|
|
889
|
+
item is fetched read-only via request-sync in the main conversation, the task's ONLY
|
|
890
|
+
Asana contact. Create `experiments/<slug>/` and write the source material to disk
|
|
891
|
+
with fixed names: the request verbatim as `experiments/<slug>/onskemal.md`
|
|
892
|
+
and the source manifest (step 2) as `experiments/<slug>/kallmanifest.md`.
|
|
893
|
+
The names are fixed because `korning.mjs` (step 4) reads them from there and
|
|
894
|
+
builds every agent prompt from them; source material that exists only in
|
|
895
|
+
the conversation never reaches a headless process.
|
|
896
|
+
|
|
897
|
+
Then create `experiment.json` with the ticket reference, date and `varianter` in
|
|
898
|
+
declaration order, always through the command:
|
|
861
899
|
|
|
862
900
|
```
|
|
863
901
|
node <skillkatalog>/scripts/archive.mjs experiment experiments/<slug> \
|
|
@@ -867,59 +905,59 @@ Frågan gäller alltså kostnaden, inte risken att förlora data.
|
|
|
867
905
|
--variant <m>...
|
|
868
906
|
```
|
|
869
907
|
|
|
870
|
-
|
|
871
|
-
`permalink_url`
|
|
872
|
-
`ticket`
|
|
873
|
-
|
|
874
|
-
`manifest_sha256` (
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
**`archive.mjs experiment`
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
2. **
|
|
900
|
-
|
|
901
|
-
|
|
908
|
+
The key names are the report's: `report.mjs` reads `request_id`, `title`,
|
|
909
|
+
`permalink_url` and `datum`, and `svep.mjs` reads `request_id` (with
|
|
910
|
+
`ticket` as a fallback in older files) into every round's REQUEST_ID. Older
|
|
911
|
+
files with `ticket`/`titel` are read as before but no longer written.
|
|
912
|
+
`manifest_sha256` (step 2) and `eskalerad`/`eskaleringsorsak` (step 3)
|
|
913
|
+
are set when they are known, in separate calls of the same command; a field that already
|
|
914
|
+
carries the same value is a no-op. `sprak` is read from the request's body text by
|
|
915
|
+
the main conversation, just as in `speca` step 2; `korning.mjs` refuses
|
|
916
|
+
to start without it (it never guesses the language, ADR-0114).
|
|
917
|
+
|
|
918
|
+
At the same time, note the item's `permalink_url` (`--satt permalink_url=<url>`)
|
|
919
|
+
when the gid reply carries it. It comes from the same read-only fetch
|
|
920
|
+
that is already made, so the step gets no new Asana contact. If the field is missing from
|
|
921
|
+
the reply, it is not written, and the report then shows the gid as plain text: Asana's
|
|
922
|
+
permalink carries a workspace gid that does not exist in the experiment area, so a
|
|
923
|
+
constructed URL would be a guess.
|
|
924
|
+
|
|
925
|
+
**`archive.mjs experiment` is the ONLY writer of `experiment.json`.**
|
|
926
|
+
No process, neither the main conversation, the starter nor the inner
|
|
927
|
+
process, writes the file by hand. The command creates the file if it is missing
|
|
928
|
+
and otherwise reads it in first: `varianter` is merged as a UNION with preserved
|
|
929
|
+
declaration order (existing names first, the invocation's new ones after), an
|
|
930
|
+
existing run's `status` is never overwritten by a process that does not own that very
|
|
931
|
+
run, and the write is ATOMIC under a lock in the experiment directory. The reason it is a command: `experiment.json`
|
|
932
|
+
is the only file in the experiment area that is shared between
|
|
933
|
+
concurrent processes, and the lock is what makes parallel runs (step 4) safe. Without it,
|
|
934
|
+
two concurrent processes lose each other's rows, which is exactly what
|
|
935
|
+
happened to sonnet/k4 in IPC 14001 (#493).
|
|
936
|
+
|
|
937
|
+
2. **Source manifest ONCE.** Build the manifest as in `speca` step 2, write it
|
|
938
|
+
to `experiments/<slug>/kallmanifest.md` (step 1) and
|
|
939
|
+
reuse it byte-identically in ALL rounds. Note the manifest hash with
|
|
902
940
|
`archive.mjs experiment experiments/<slug> --satt manifest_sha256=<hash>`
|
|
903
|
-
(`ref-hash.mjs`
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
`inga`
|
|
908
|
-
3. **
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
`request-specifier`,
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
under override
|
|
916
|
-
|
|
917
|
-
|
|
941
|
+
(`ref-hash.mjs` on the manifest text). The only intended
|
|
942
|
+
difference between the rounds is the model.
|
|
943
|
+
The manifest ends with the section `## Avgjorda val`: every choice that a human
|
|
944
|
+
has decided after an earlier stop, one line each, verbatim as the answer was given;
|
|
945
|
+
`inga` if there are none. The specifier must never flag a choice that stands there.
|
|
946
|
+
3. **The escalation choice is made once** per `speca`'s rules and applies to all
|
|
947
|
+
rounds; the escalation choice too is part of what is held constant.
|
|
948
|
+
If the choice triggers: the agent becomes `request-specifier-eskalerad` instead of
|
|
949
|
+
`request-specifier`, the same in all rounds. The model override in step 4
|
|
950
|
+
still applies to the escalated agent just as to the other two: its own
|
|
951
|
+
pinned model, the one that per the agent definition IS the escalation, is
|
|
952
|
+
overwritten by the round's model. The escalation mechanism thus deliberately collapses
|
|
953
|
+
under override; that is intended, not a gap: `utvärdera` measures a pure
|
|
954
|
+
model difference across all three roles and never tests
|
|
955
|
+
the escalation decision itself. The choice is noted with
|
|
918
956
|
`archive.mjs experiment experiments/<slug> --satt eskalerad=<true|false> --satt eskaleringsorsak=<text>`;
|
|
919
|
-
|
|
920
|
-
4. **
|
|
921
|
-
|
|
922
|
-
|
|
957
|
+
it is from there that `svep.mjs` reads `ESKALERAD:` into every round's prompt.
|
|
958
|
+
4. **One round per model in the list**, started by `svep.mjs`. The main conversation
|
|
959
|
+
prepares steps 1 to 3 and then runs, even for a single item and a single
|
|
960
|
+
model:
|
|
923
961
|
|
|
924
962
|
```
|
|
925
963
|
node <skillkatalog>/scripts/svep.mjs --repo-rot <abs> --konto <namn> \
|
|
@@ -928,504 +966,504 @@ Frågan gäller alltså kostnaden, inte risken att förlora data.
|
|
|
928
966
|
[--tickets-parallellt 2] [--modeller-parallellt 4] [--om | --hoppa-över]
|
|
929
967
|
```
|
|
930
968
|
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
**
|
|
942
|
-
|
|
969
|
+
Every round is started by `svep.mjs` as ONE `korning.mjs` process, and that is
|
|
970
|
+
what gives the measurement for free. The parallel rule: two tickets at a time, at most
|
|
971
|
+
four models for one ticket in flight (`--modeller-parallellt`, #526), and
|
|
972
|
+
the next ticket starts only when all of a ticket's
|
|
973
|
+
models are done. That is safe because the run directories are already
|
|
974
|
+
isolated (`ny-korning` is atomic, every round has its own directory, manifest
|
|
975
|
+
and repo are only read) and the only shared state, `experiment.json`,
|
|
976
|
+
is written under a lock (step 1). The responsibility is split, since a process can never know its own
|
|
977
|
+
final cost:
|
|
978
|
+
|
|
979
|
+
**The starter** is `svep.mjs`. Its responsibility per round, in one sentence: allocate
|
|
980
|
+
the run directory with
|
|
943
981
|
`archive.mjs ny-korning <modellkatalog> --request-id <gid eller slug>`
|
|
944
|
-
(
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
repo
|
|
948
|
-
`stderr.log`; `korning.json`
|
|
949
|
-
|
|
982
|
+
(the command also writes the skeleton to the run's `state.json`, see
|
|
983
|
+
The experiment area; the inner process never writes that file from scratch),
|
|
984
|
+
start `korning.mjs` for the round with the run directory, the experiment directory,
|
|
985
|
+
the repo root, the request id and the model as arguments (stderr to
|
|
986
|
+
`stderr.log`; `korning.json` is written by `korning.mjs` itself),
|
|
987
|
+
time the process and write
|
|
950
988
|
`<modell>/k<N>/matning.json` (`startad`, `avslutad`, `wall_ms`,
|
|
951
|
-
`exit_code`;
|
|
952
|
-
|
|
989
|
+
`exit_code`; written EVEN when the process fails), stamp `proveniens` in
|
|
990
|
+
the run's `state.json` with
|
|
953
991
|
`archive.mjs proveniens <modell>/k<N> --modell <varvets modellvärde> --konto <Claude-kontot som körde> --underlag <sökväg till kallmanifest.md>`
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
- **
|
|
965
|
-
|
|
966
|
-
`usage.cache_read_input_tokens`, `usage.cache_creation_input_tokens`
|
|
967
|
-
`subagent_stats.spawned`
|
|
968
|
-
`matning.json`:
|
|
969
|
-
|
|
970
|
-
`korning.json`
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
`samtidiga_processer` (
|
|
974
|
-
start,
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
- **
|
|
978
|
-
|
|
979
|
-
(
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
ADR-0099),
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
- **
|
|
986
|
-
|
|
987
|
-
`fakturerade_modeller`
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
`sonnet`
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
`korning.json`
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
`modelUsage`
|
|
998
|
-
|
|
999
|
-
`model.uppgiven`),
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
- **
|
|
1005
|
-
|
|
1006
|
-
`--orsak`;
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
**`korning.mjs`
|
|
1010
|
-
|
|
1011
|
-
|
|
992
|
+
AFTER `korning.json` has been written, verify the model against `modelUsage`,
|
|
993
|
+
and set the run's status via `archive.mjs experiment`. How each of these
|
|
994
|
+
is done lives in the script and its tests, not here. That the steps stand as
|
|
995
|
+
commands is deliberate (ADR-0094): a step that imposes a
|
|
996
|
+
write on the starter without pointing out who performs it is a write
|
|
997
|
+
that never happens, and it shows only as empty cells much later.
|
|
998
|
+
|
|
999
|
+
Four rules in that chain are contracts against the report and the archive and therefore
|
|
1000
|
+
stay here with their reason:
|
|
1001
|
+
|
|
1002
|
+
- **The wall time lives in `matning.json`, never in `korning.json`.** The report
|
|
1003
|
+
reads `total_cost_usd`, `usage.input_tokens`, `usage.output_tokens`,
|
|
1004
|
+
`usage.cache_read_input_tokens`, `usage.cache_creation_input_tokens` and
|
|
1005
|
+
`subagent_stats.spawned` from `korning.json`, but the wall time from
|
|
1006
|
+
`matning.json`: a process cannot know its own clock time when the work
|
|
1007
|
+
lies in background processes, and the step replies' `duration_ms` does not count the waiting for them.
|
|
1008
|
+
`korning.json` carries the sum over the steps, hence the separate file. A crashed run has a clock time, often the most interesting
|
|
1009
|
+
number in the table, therefore `matning.json` is written even on a crash. The load
|
|
1010
|
+
the wall time was measured under stands in `korning.json`'s steps as
|
|
1011
|
+
`samtidiga_processer` (living agent processes on the machine at the step's
|
|
1012
|
+
start, counted with `pgrep`, machine-wide and best effort, `null` when
|
|
1013
|
+
the count failed); the report shows the maximum next to the wall time and marks
|
|
1014
|
+
a run without the field as measured under unknown concurrency (#526).
|
|
1015
|
+
- **The provenance is stamped AFTER `korning.json`, never before**, otherwise
|
|
1016
|
+
the stamp lacks the billing data. The command itself computes `skill_sha256`
|
|
1017
|
+
(over `SKILL.md` plus the four agent definitions) and `underlag_sha256`,
|
|
1018
|
+
reads `korning.json` and notes `fakturerade_modeller` (the
|
|
1019
|
+
canonicalized key set from `modelUsage`, suffixes like `[1m]` stripped,
|
|
1020
|
+
ADR-0099), and also stamps every `del-N/`. Without that stamp it is
|
|
1021
|
+
not possible to determine what separated `k1` from `k2`, and a comparison over time
|
|
1022
|
+
becomes a guess.
|
|
1023
|
+
- **Model verification is done by the starter against the billing (ADR-0099),
|
|
1024
|
+
never against the tail line alone.** When the process is done: compare
|
|
1025
|
+
`fakturerade_modeller` against the round's requested model plus the two tolerated
|
|
1026
|
+
deviations, the customer-text fix's `sonnet` and the CLI's `haiku` helper calls. A
|
|
1027
|
+
model key that is neither the round's requested model, the customer-text fix's
|
|
1028
|
+
`sonnet` nor the CLI's `haiku` helper call is an actual `MODELL-avvikelse` →
|
|
1029
|
+
the run becomes `"ofärdig"`. The starter tolerates the `haiku` key in the sum;
|
|
1030
|
+
the cap of 200 output tokens, which distinguishes the helper call from a real
|
|
1031
|
+
run, is tested per step by `korning.mjs`. The result lies in
|
|
1032
|
+
`korning.json` as `modellverifiering` (`ok` plus `avvikelser`, where every
|
|
1033
|
+
row names its step), so the deviation can be pointed out in the step where it
|
|
1034
|
+
arose. A MODELL tail line that says something other than what
|
|
1035
|
+
`modelUsage` shows is a REPORTING DEVIATION: it is shown in the report
|
|
1036
|
+
and stays in `runs[]` (the row carries the agent's claim in
|
|
1037
|
+
`model.uppgiven`), but it NEVER alone fails the run: models are
|
|
1038
|
+
unreliable sources about their own identity, and throwing away a correct
|
|
1039
|
+
run over a miswritten tail line pays again for a real result.
|
|
1040
|
+
The model value `default` runs the agents' own defaults and may legitimately
|
|
1041
|
+
produce several model keys: no set comparison is made there.
|
|
1042
|
+
- **The status is set via `archive.mjs experiment`**, never by writing
|
|
1043
|
+
the file: exit ≠ 0 or an actual `MODELL-avvikelse` gives `"ofärdig"` with
|
|
1044
|
+
`--orsak`; if the inner process has already set `"stopp"`, `"väntar på dom"`
|
|
1045
|
+
or `"ofärdig"`, it is kept; otherwise `"klar"`.
|
|
1046
|
+
|
|
1047
|
+
**`korning.mjs` is the inner process: a script without model turns.** It
|
|
1048
|
+
builds the prompts from `onskemal.md`, `kallmanifest.md` and `experiment.json`,
|
|
1049
|
+
starts every agent step as a process of its own
|
|
1012
1050
|
`claude -p --agent <agent> --output-format json --max-turns <tak> [--model <modell>]`
|
|
1013
|
-
|
|
1014
|
-
`kontroll.mjs`
|
|
1015
|
-
`korning.json`
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
`korning.mjs`
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
(ADR-0099,
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
**
|
|
1038
|
-
`del-1/ … del-N/` under
|
|
1039
|
-
|
|
1040
|
-
`granskarblock.md`
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
**DELA_UPP
|
|
1046
|
-
|
|
1047
|
-
|
|
1051
|
+
with the prompt on stdin, saves the raw reply under `k<N>/steg/`, runs precheck,
|
|
1052
|
+
`kontroll.mjs` and compare-scores, archives via `archive.mjs` and writes
|
|
1053
|
+
`korning.json` as the sum. It does no Asana fetch and builds no
|
|
1054
|
+
new manifest. The bundle's parts run in parallel (two at a time) and the two
|
|
1055
|
+
scorer runs at the same time (#500).
|
|
1056
|
+
|
|
1057
|
+
`korning.mjs` runs the speca steps (dispatch, contract check,
|
|
1058
|
+
archiving) and then the bedöm steps (analysis, score,
|
|
1059
|
+
anchor resolution, double run per bedöm) against the directory it is given, without
|
|
1060
|
+
the approval gate, and touches nothing outside it. If the model is `default`,
|
|
1061
|
+
the ordinary prompt templates and the agents' defaults are used, no override. If
|
|
1062
|
+
it is a model name, every agent process gets a model override to it on ALL
|
|
1063
|
+
three agents; the override is `--model` on the process, never a prompt line, and
|
|
1064
|
+
the agent definitions are never touched; the effort is taken from the definition
|
|
1065
|
+
as usual, except when `korning.mjs` has been given `--effort`: then every
|
|
1066
|
+
agent process gets the same `--effort` override, the step entry and `runs[]` carry it
|
|
1067
|
+
as requested (`effort_begard`, `add-run --effort`), never as verified,
|
|
1068
|
+
since the billing carries no effort (#525). The override is verified per step against `modelUsage` in the raw reply
|
|
1069
|
+
(ADR-0099, see the model verification above); the tail line is archived but never
|
|
1070
|
+
decides alone. `approval` stays `null`, `state.json`
|
|
1071
|
+
gets `"experiment": true` from the skeleton `ny-korning` wrote. `REQUEST_ID`
|
|
1072
|
+
and `--expect-id` always use the ticket's gid/slug, never the directory's
|
|
1073
|
+
name.
|
|
1074
|
+
|
|
1075
|
+
**A bundle in the inner process:** if the specifier gives several specs, they are placed as
|
|
1076
|
+
`del-1/ … del-N/` under the run directory with `bundle` set (the bundle rule in
|
|
1077
|
+
The experiment area), never part 1 in the root; every part gets its own `spec.md`,
|
|
1078
|
+
`granskarblock.md` and `state.json`, and speca step 6 (precheck,
|
|
1079
|
+
redispatch per part, `stopp` on a candidate) runs per part. If a part fails on a
|
|
1080
|
+
remaining defect, the run becomes `"ofärdig"`; the other parts are still run
|
|
1081
|
+
to completion so the archive shows what could be assessed.
|
|
1082
|
+
|
|
1083
|
+
**DELA_UPP is not run autonomously by `korning.mjs` (v1).** The outcome is archived
|
|
1084
|
+
and the run gets the status `stopp` with a reason; the split analysis lacks
|
|
1085
|
+
a prompt template in this skill, and a script must not improvise one
|
|
1048
1086
|
(ADR-0113).
|
|
1049
1087
|
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1088
|
+
A failed agent step, a contract breach (remaining BROTT after redispatch) or
|
|
1089
|
+
an actual `MODELL-avvikelse` (an unrequested key in `modelUsage`, never a
|
|
1090
|
+
lone tail-line deviation), in whichever step it occurs, sets
|
|
1091
|
+
the run's `status` to `"ofärdig"` via `archive.mjs experiment`
|
|
1054
1092
|
(`experiments/<slug> --korning <modell>/k<N> --status ofärdig --orsak <vad som föll>`),
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
5. **DELA_UPP
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
`<modell>/k<N>/del-1/ … del-N/`
|
|
1062
|
-
`granskarblock.md`
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
`INPUT_SAKNAS`)
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
**OLIKA
|
|
1073
|
-
|
|
1074
|
-
`"väntar på dom"` via `archive.mjs experiment`,
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
6. **
|
|
1086
|
-
|
|
1087
|
-
`node <skillkatalog>/scripts/archive.mjs bluf <modell>/k<N>` (
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
### Batch:
|
|
1093
|
-
|
|
1094
|
-
`utvärdera <
|
|
1095
|
-
`korning.mjs
|
|
1096
|
-
|
|
1097
|
-
|
|
1093
|
+
never by writing the file.
|
|
1094
|
+
|
|
1095
|
+
5. **DELA_UPP in the experiment.** A DELA_UPP outcome follows bedöm's ordinary
|
|
1096
|
+
split rules but without a pause for a reviewer (in v1 `korning.mjs` does not run
|
|
1097
|
+
the split itself, see step 4; the rules here apply when it runs):
|
|
1098
|
+
part directories
|
|
1099
|
+
`<modell>/k<N>/del-1/ … del-N/` with `split` set, the parent's `spec.md` +
|
|
1100
|
+
`granskarblock.md` copied in, a new analysis + score per part. If a
|
|
1101
|
+
bundle part is split, it becomes `del-N/del-M/` under that part, never a new
|
|
1102
|
+
sibling in the root (`bundle` and `split` are never set on the same directory).
|
|
1103
|
+
A part without a ceiling becomes `OVER_SKALAN` and is never split further. Other
|
|
1104
|
+
stop outcomes (`TILLBAKA_TILL_SPEC`, `KONCEPT`, `DIAGNOS`, `ESKALERA`,
|
|
1105
|
+
`INPUT_SAKNAS`) require a human or the customer: archive the outcome, set
|
|
1106
|
+
the run's status to `"stopp"` via `archive.mjs experiment`, abort that
|
|
1107
|
+
round, make it visible in the report when it runs, and the other runs in
|
|
1108
|
+
the list continue independently.
|
|
1109
|
+
|
|
1110
|
+
**OLIKA waits for a ruling.** If the double run writes OLIKA in a round:
|
|
1111
|
+
archive per bedöm's rules (the hold stands), set the run's status to
|
|
1112
|
+
`"väntar på dom"` via `archive.mjs experiment`, visible in the report when
|
|
1113
|
+
it runs.
|
|
1114
|
+
The ruling is the human's even in the experiment: `döm` is run against the run directory
|
|
1115
|
+
when the reviewer wants (addressed via its path, see `döm`), and no ruling is
|
|
1116
|
+
simulated by the task. The ruling itself does not rewrite `experiment.json`;
|
|
1117
|
+
`svep.mjs` does that (via `archive.mjs experiment`) the next time `utvärdera`
|
|
1118
|
+
is run for the same item (see
|
|
1119
|
+
Resumable in the Batch section below for how and when the status goes
|
|
1120
|
+
to `"klar"` and the report can show the outcome). The other runs in the list
|
|
1121
|
+
continue independently regardless of when, or whether, the ruling is made.
|
|
1122
|
+
|
|
1123
|
+
6. **Report.** BLUF in the chat: done or unfinished per run, stop outcomes,
|
|
1124
|
+
and the path to `experiments/<slug>/`. The table per run comes from
|
|
1125
|
+
`node <skillkatalog>/scripts/archive.mjs bluf <modell>/k<N>` (recursively
|
|
1126
|
+
over the parts, the root row for a bundle reads `bunt (N delar)`) and is pasted
|
|
1127
|
+
as it is, never handwritten. Mention that `utvärdera rapport` exists
|
|
1128
|
+
when the material is to be read side by side. No write against Asana.
|
|
1129
|
+
|
|
1130
|
+
### Batch: several items
|
|
1131
|
+
|
|
1132
|
+
`utvärdera <item1> <item2> … <model…>` runs every ticket and model in a SEPARATE
|
|
1133
|
+
`korning.mjs` process, in parallel per `svep.mjs`: two tickets at a time, at most
|
|
1134
|
+
four models for one ticket in flight, and the next ticket starts when a ticket is
|
|
1135
|
+
completely done (step 4). The line `svep.mjs` runs per round:
|
|
1098
1136
|
|
|
1099
1137
|
```
|
|
1100
1138
|
node <skillkatalog>/scripts/korning.mjs --katalog <k<N>> --exp <experiments/<slug>> --repo-rot <abs> --request-id <gid> --modell <modell>
|
|
1101
1139
|
```
|
|
1102
1140
|
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
(ÖNSKEMÅL, KÄLLOR, REPO-ROT, DEPS, SPRÅK, RUBRIKER, ESKALERAD, REQUEST_ID)
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
`--model
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
`--om`
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
`--katalog`
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
`archive.mjs proveniens`
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
- **
|
|
1127
|
-
|
|
1128
|
-
disk; `svep.mjs`
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
- **
|
|
1137
|
-
`kallmanifest.md`
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
`"ofärdig"`,
|
|
1141
|
-
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
checkout, ADR-0093);
|
|
1151
|
-
process
|
|
1152
|
-
(#516).
|
|
1153
|
-
`"väntar på dom"`
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
status
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
`döm`)
|
|
1161
|
-
|
|
1162
|
-
`"klar"
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
###
|
|
1168
|
-
|
|
1169
|
-
`utvärdera sammanställ`
|
|
1170
|
-
`experiments/sammanstallning.md`:
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
`k<N>`
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
###
|
|
1179
|
-
|
|
1180
|
-
`utvärdera rapport [--ut <fil>]`
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
HTML
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
**
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
##
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
1. **
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
`respec_varv`, `approval`,
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
`nastaStegFor`
|
|
1230
|
-
`okänt läge: <status>`,
|
|
1231
|
-
2. **
|
|
1232
|
-
|
|
1233
|
-
`Bräda: <Offer status>`
|
|
1141
|
+
The source material is read from disk by `korning.mjs`; nothing goes via stdin to
|
|
1142
|
+
the orchestration, only to the agent processes. Which lines every agent prompt carries
|
|
1143
|
+
(ÖNSKEMÅL, KÄLLOR, REPO-ROT, DEPS, SPRÅK, RUBRIKER, ESKALERAD, REQUEST_ID) lives
|
|
1144
|
+
in the script and its tests.
|
|
1145
|
+
|
|
1146
|
+
There is no orchestrator model: the agents' models are steered by the definition plus
|
|
1147
|
+
the `--model` override and verified per step against `modelUsage` in the raw reply, never
|
|
1148
|
+
by the tail line alone.
|
|
1149
|
+
|
|
1150
|
+
`--om` and `--hoppa-över` are the sweep's own flags; `korning.mjs` never asks
|
|
1151
|
+
anything and can therefore not hang. `--hoppa-över` is moreover exactly
|
|
1152
|
+
the resumption's semantics: an interrupted sweep that is restarted picks up where
|
|
1153
|
+
it stopped instead of paying again for everything finished.
|
|
1154
|
+
|
|
1155
|
+
`--katalog` points out the run directory that `archive.mjs ny-korning`
|
|
1156
|
+
has already allocated for that ticket and model. `korning.mjs` archives
|
|
1157
|
+
only there, never anywhere else.
|
|
1158
|
+
|
|
1159
|
+
The sweep's loop is the starter, and it is called `svep.mjs`: it carries the starter's whole
|
|
1160
|
+
responsibility per row (`ny-korning`, the timing that writes `matning.json`,
|
|
1161
|
+
`archive.mjs proveniens` against the run directory when the process is done,
|
|
1162
|
+
the model verification and status via `archive.mjs experiment`), see step 4.
|
|
1163
|
+
|
|
1164
|
+
- **One ticket, one model = one `korning.mjs` process; every agent step is a
|
|
1165
|
+
context window of its own.** All state lives in the archive on
|
|
1166
|
+
disk; `svep.mjs` writes one line per ticket and model when it starts and
|
|
1167
|
+
when it ends (status, wall time, USD) and a final table, and the main session
|
|
1168
|
+
gathers only that. `/clear` between tickets is not needed: the process boundary is
|
|
1169
|
+
the equivalent. The headless run presupposes that the task can run without
|
|
1170
|
+
interactive permission questions, which holds: it reads the repo and writes
|
|
1171
|
+
only under `.haus-workflow/requests/`. The Asana fetch was already done in
|
|
1172
|
+
step 1 by the main conversation, which wrote `onskemal.md`, so `korning.mjs`
|
|
1173
|
+
needs no MCP contact at all.
|
|
1174
|
+
- **Continue on errors.** A ticket whose source material is missing (`onskemal.md`,
|
|
1175
|
+
`kallmanifest.md` or `experiment.json` with `request_id`) is marked and
|
|
1176
|
+
skipped; one whose process crashes becomes `"ofärdig"` with a reason. The rest of the sweep
|
|
1177
|
+
continues, and `svep.mjs` ends with exit 1 when any run became
|
|
1178
|
+
`"ofärdig"`, otherwise 0.
|
|
1179
|
+
- **Resumable:** `svep.mjs` does that: before a run is started,
|
|
1180
|
+
the archive is checked, steered by
|
|
1181
|
+
the run's `status` in `experiment.json`. `"klar"` and `"stopp"` are skipped
|
|
1182
|
+
with a notice. `"ofärdig"` is interrupted data, not a result worth
|
|
1183
|
+
protecting: it never triggers the rerun question, but is rerun automatically as
|
|
1184
|
+
a new `k<N+1>` under the same model. The old unfinished run is still
|
|
1185
|
+
never overwritten, it stays as history of what went wrong. One
|
|
1186
|
+
exception: an `"ofärdig"` row that carries a `pid` whose process is alive is not an
|
|
1187
|
+
interrupted run but one that is in progress in another sweep (two accounts against the same
|
|
1188
|
+
checkout, ADR-0093); it is skipped with the notice that it is in progress in another
|
|
1189
|
+
process and stands as `pågår` in the final table, never as a duplicate
|
|
1190
|
+
(#516). A dead pid, or no pid at all, is rerun as before.
|
|
1191
|
+
`"väntar på dom"` is RE-EVALUATED instead of skipped; it is the
|
|
1192
|
+
only place that is the actor for that status: run `archive.mjs sparr` on
|
|
1193
|
+
the run directory. Exit 0 means that the ruling has been made: set the run's
|
|
1194
|
+
status to `"klar"` via `archive.mjs experiment` and continue where it
|
|
1195
|
+
stopped (the report can be run separately when all runs for the item are
|
|
1196
|
+
done). Exit 1 means that it is still waiting: note it and move on
|
|
1197
|
+
to the next round. `döm` makes the ruling but never touches `experiment.json` (see
|
|
1198
|
+
`döm`); it is only by `utvärdera` being run again for the same item that
|
|
1199
|
+
the status is updated. The rerun question (see `utvärdera` above) thus applies only to
|
|
1200
|
+
`"klar"` runs: those are results worth protecting against a slip of the finger,
|
|
1201
|
+
an unfinished run has nothing to protect. Runs are cheap
|
|
1202
|
+
relative to the risk of patching together half results. A sweep over many tickets
|
|
1203
|
+
can thereby continue in a later session without unnecessary reruns.
|
|
1204
|
+
|
|
1205
|
+
### Compilation
|
|
1206
|
+
|
|
1207
|
+
`utvärdera sammanställ` reads all `experiments/*/` and writes
|
|
1208
|
+
`experiments/sammanstallning.md`: a table per ticket with one row per model that
|
|
1209
|
+
carries the score, split and stop outcomes, plus a deviation marking where
|
|
1210
|
+
the models differ. A model's row is taken from its CURRENT run, the highest
|
|
1211
|
+
`k<N>` whose status in `experiment.json` is `"klar"`. A model without a done run
|
|
1212
|
+
is represented by its latest `k<N>` and marked `ofullständig` together with
|
|
1213
|
+
that run's status. Half-finished directories are marked the same way, never silently
|
|
1214
|
+
left out. Reading only: no agent dispatches, no Asana calls.
|
|
1215
|
+
|
|
1216
|
+
### Report
|
|
1217
|
+
|
|
1218
|
+
`utvärdera rapport [--ut <fil>]` renders an HTML report over the experiment area
|
|
1219
|
+
with `scripts/report.mjs`. The report ALWAYS covers the whole area; there is no item
|
|
1220
|
+
filter, the menu in the report is where the reader chooses an item. Without `--ut` the
|
|
1221
|
+
HTML is written to stdout, otherwise to the file. Publish it as an artifact when it is to be
|
|
1222
|
+
read.
|
|
1223
|
+
|
|
1224
|
+
**The report is NEVER written automatically.** No run generates it when it
|
|
1225
|
+
is done. It costs almost nothing to regenerate, so there is no
|
|
1226
|
+
reason to write it on speculation, and a file nobody asked for is a file
|
|
1227
|
+
nobody trusts.
|
|
1228
|
+
|
|
1229
|
+
The report is pure reading: no agent dispatches, no Asana calls, no
|
|
1230
|
+
writes in the archive beyond the output file.
|
|
1231
|
+
|
|
1232
|
+
What it shows: a menu over items with the ticket's title, its directory slug and
|
|
1233
|
+
which models the item has, and per chosen item a column per run. Every sub-item from
|
|
1234
|
+
a DELA_UPP outcome is a column of its own after its run (`opus / k1 / del-1`),
|
|
1235
|
+
since that is where the outcomes live when an item has split. The sections are field-wise,
|
|
1236
|
+
the same field on the same row across the columns: outcome, the score's bracketing with
|
|
1237
|
+
floor, ceiling, steps and drivers,
|
|
1238
|
+
the spec's four fields, plus `impact.md` and `granskarblock.md` collapsed per
|
|
1239
|
+
run. Older runs with classification files (before ADR-0098) also get
|
|
1240
|
+
their Klassning section rendered as before: the calibration history is not thrown away.
|
|
1241
|
+
Last come the cost (USD, tokens, wall time, dispatches) and the provenance.
|
|
1242
|
+
A part column lacks a measurement of its own and says `ingår i k<N>`, never unmeasured.
|
|
1243
|
+
The matrix is uneven on purpose: one item can have two models and another three,
|
|
1244
|
+
since a more expensive model is added only where the cheaper ones were not enough.
|
|
1245
|
+
|
|
1246
|
+
What it NEVER does: rank models, recommend a level, compute aggregates
|
|
1247
|
+
across items with different model sets, or state which outcome
|
|
1248
|
+
was correct. The judgment of what is an acceptable outcome is
|
|
1249
|
+
the human's, made on experience of the system. The report shows the spread.
|
|
1250
|
+
|
|
1251
|
+
## Closing: where the item stands
|
|
1252
|
+
|
|
1253
|
+
The tasks `speca`, `bedöm`, `godkänn`, `underkänn` and `döm` ALWAYS end
|
|
1254
|
+
with this block (#530). A reply that ends without it leaves
|
|
1255
|
+
the user to guess where the item stands, what they should do and which command
|
|
1256
|
+
runs on, and the archive and the board can then drift apart silently (`spec_draft`
|
|
1257
|
+
locally, empty Offer status on the board). The block consists of four parts, in order:
|
|
1258
|
+
|
|
1259
|
+
1. **The table from `node <skillkatalog>/scripts/archive.mjs nasta-steg <katalog>`**
|
|
1260
|
+
is pasted as it is (`| post | står | väntar på | nästa |`, one row per
|
|
1261
|
+
directory and part), NEVER handwritten, for the same reason as `bluf`: a
|
|
1262
|
+
handwritten closing drops exactly the row that would have said what is
|
|
1263
|
+
waiting. The script computes the rows from `state.json` (status, `stopp`,
|
|
1264
|
+
`respec_varv`, `approval`, an unlifted OLIKA via the hold, the scorer's current
|
|
1265
|
+
outcome from the pricing file) with the rules from this skill's own gates;
|
|
1266
|
+
the rule table with a source reference per row stands above the function
|
|
1267
|
+
`nastaStegFor` in the script. A state the script does not know is written as
|
|
1268
|
+
`okänt läge: <status>`, never as a guess.
|
|
1269
|
+
2. **The board line.** If the item has a gid: fetch Offer status via request-sync in
|
|
1270
|
+
the main conversation (never from memory) and write the line
|
|
1271
|
+
`Bräda: <Offer status>` directly under the table. If the item lacks a gid: write
|
|
1234
1272
|
`Bräda: ingen (posten ligger inte på brädan)`.
|
|
1235
|
-
3. **ISÄR
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
`ISÄR: arkiv <x>, bräda <y>`
|
|
1239
|
-
|
|
1240
|
-
|
|
|
1241
|
-
| ---------------------------------------------- | ------------------------------------ |
|
|
1242
|
-
| `spec_draft` |
|
|
1243
|
-
| `spec_synced` | 🔍 Spec redo för granskning | `godkänn`/`underkänn`,
|
|
1244
|
-
| `spec_synced`/`spec_draft`
|
|
1245
|
-
| `assessed`/`priced`,
|
|
1246
|
-
|
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
4. **
|
|
1254
|
-
`ESKALERA`, `KONCEPT`, `DIAGNOS`)
|
|
1255
|
-
|
|
1256
|
-
"
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
`döm <
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
##
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
`kontroll.mjs`
|
|
1267
|
-
`archive.mjs`
|
|
1268
|
-
`svep.mjs`
|
|
1269
|
-
`korning.mjs`
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
(
|
|
1276
|
-
|
|
1277
|
-
- `scripts/precheck.mjs <arkivkatalog> [--impact]
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
(`**Koncept-kandidat**`/`**Diagnos-kandidat**`,
|
|
1284
|
-
|
|
1285
|
-
`KANDIDAT: DIAGNOS`
|
|
1286
|
-
|
|
1287
|
-
`**
|
|
1288
|
-
`AVVIKELSE: precisering`
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
- `scripts/resolve-anchors.mjs <run.json> <spec.md> [impact.md]
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
2 =
|
|
1296
|
-
- `scripts/contract-check.mjs <run.json> <spec.md> [impact.md] [--expect-id <id>] [--state <state.json>] [--sprak <svenska|engelska>]
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
driver
|
|
1301
|
-
|
|
1302
|
-
INPUT_SAKNAS
|
|
1303
|
-
(
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
`{anchor, relation, grund}`
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
storre/mindre
|
|
1313
|
-
|
|
1314
|
-
(ADR-0115).
|
|
1315
|
-
|
|
1316
|
-
(
|
|
1317
|
-
|
|
1318
|
-
- `scripts/check-lasta.mjs "<LÄSTA-innehåll>" <spec.md> <impact.md
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
Exit 0 = ok, 1 =
|
|
1322
|
-
- `scripts/compare-scores.mjs <a.json> <b.json
|
|
1323
|
-
|
|
1324
|
-
score, confidence,
|
|
1325
|
-
`LIKA_TOLERANS` (
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
`mindre`
|
|
1331
|
-
`bracket.steps[A<n>].relation: "mindre" ↔ "storre"`,
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
mode `pricing`; guard
|
|
1335
|
-
|
|
1336
|
-
A13),
|
|
1337
|
-
`conditions` (
|
|
1338
|
-
- `scripts/archive.mjs <kommando> <katalog> [flaggor]`:
|
|
1339
|
-
(
|
|
1340
|
-
`utslag`, `set-status`, `kalibrering`, `ny-korning`, `proveniens`,
|
|
1273
|
+
3. **The ISÄR check.** The archive status and the Offer status must correspond to each other
|
|
1274
|
+
per the table below (the values are request-sync's, normalized on
|
|
1275
|
+
the alphabetic core as there). If they differ, a line
|
|
1276
|
+
`ISÄR: arkiv <x>, bräda <y>` is written, followed by the command that closes the gap.
|
|
1277
|
+
|
|
1278
|
+
| archive status (`state.json`) | expected Offer status | closes the gap |
|
|
1279
|
+
| ---------------------------------------------- | ------------------------------------ | ---------------------------------------------------------------------------------------------- |
|
|
1280
|
+
| `spec_draft` | empty or 📥 Inkommet | sync via request-sync (🔍) when the spec is to be reviewed |
|
|
1281
|
+
| `spec_synced` | 🔍 Spec redo för granskning | `godkänn`/`underkänn`, or sync if the board stands at 📥 |
|
|
1282
|
+
| `spec_synced`/`spec_draft` with `approval` set | ✅ Spec ok | `bedöm`; if the board stands at 🔍, the approval note is older than the board, reread the gate |
|
|
1283
|
+
| `assessed`/`priced`, the score synced | ⚖️ Poängsättning redo för granskning | sync via request-sync if the board still stands at ✅ |
|
|
1284
|
+
| done (`godkänn` from ⚖️) | 📤 Klar – till kund | nothing, the customer's `Decision` decides the rest |
|
|
1285
|
+
|
|
1286
|
+
The archive is a cache and Asana the truth (Hard rules): an ISÄR is always resolved
|
|
1287
|
+
in the direction of the board, never by rewriting the board to match
|
|
1288
|
+
the archive. An experiment variant (`experiment: true`) has no board line and
|
|
1289
|
+
no ISÄR check; it never reaches Asana.
|
|
1290
|
+
|
|
1291
|
+
4. **The choice, when there is one.** A stop outcome (`TILLBAKA_TILL_SPEC`,
|
|
1292
|
+
`ESKALERA`, `KONCEPT`, `DIAGNOS`) and an unlifted `double_run: olika`
|
|
1293
|
+
are presented as a choice the user must make, not just as routing:
|
|
1294
|
+
"here is your choice", followed by the alternatives (for TILLBAKA_TILL_SPEC the
|
|
1295
|
+
open choice from `stopp.orsak` or the source file; for OLIKA the two
|
|
1296
|
+
runs' evidence side by side, see bedöm step 6) and the command that
|
|
1297
|
+
carries out the answer (`speca <item>` after `## Avgjorda val` + `respec`,
|
|
1298
|
+
`döm <item>`, customer dialogue). A stop that is only routed is read as "the machine
|
|
1299
|
+
is working on it", and nobody does anything.
|
|
1300
|
+
|
|
1301
|
+
## Mechanical scripts: `scripts/`
|
|
1302
|
+
|
|
1303
|
+
Twelve deterministic scripts live in the directory: the five checks below,
|
|
1304
|
+
`kontroll.mjs` which runs the scorer's check chain in one process,
|
|
1305
|
+
`archive.mjs` which owns the archive's directories, holds and `experiment.json`,
|
|
1306
|
+
`svep.mjs` which is utvärdera's starter,
|
|
1307
|
+
`korning.mjs` which is utvärdera's inner process, `report.mjs` which
|
|
1308
|
+
renders the model report (see `utvärdera rapport`), `lib.mjs` with the shared
|
|
1309
|
+
building blocks and `ref-hash.mjs` for stable hashing. The checks replace
|
|
1310
|
+
model runs where the check is pure form:
|
|
1311
|
+
cheaper, faster and without judgment drift. Every script carries sha256 for the
|
|
1312
|
+
reference files whose rules it mirrors and WARNS when a catalog bump has changed them
|
|
1313
|
+
(contract drift must be discovered by a check, not by a customer):
|
|
1314
|
+
|
|
1315
|
+
- `scripts/precheck.mjs <arkivkatalog> [--impact]`: the form check BEFORE expensive
|
|
1316
|
+
dispatches: headings, exactly one spec block, 2–5 bullets, forbidden vocabulary,
|
|
1317
|
+
reviewer block rows, state.json fields. Distinguishes BRISTER (exit 1, stops)
|
|
1318
|
+
from VARNING (a heuristic: shown, does not stop). The headings are matched via
|
|
1319
|
+
the alias table in `lib.mjs` in Swedish or English; headings from both
|
|
1320
|
+
languages in the same spec are a BRIST. The candidate form is recognized by the marker line
|
|
1321
|
+
(`**Koncept-kandidat**`/`**Diagnos-kandidat**`, or the English
|
|
1322
|
+
equivalents) and written as `KANDIDAT: KONCEPT` or
|
|
1323
|
+
`KANDIDAT: DIAGNOS` on stdout, always, even at exit 1; a candidate with a
|
|
1324
|
+
_Lösning_ (_Solution_) is a BRIST. With `--impact` the label on the analysis's row
|
|
1325
|
+
`**Deviation from the spec:**` is read and written as `AVVIKELSE: ingen`,
|
|
1326
|
+
`AVVIKELSE: precisering` or `AVVIKELSE: motsägelse`; a row without a
|
|
1327
|
+
label is a BRIST. The orchestrator reads candidate and deviation from those
|
|
1328
|
+
lines, never from the files itself.
|
|
1329
|
+
- `scripts/resolve-anchors.mjs <run.json> <spec.md> [impact.md]`: resolves
|
|
1330
|
+
evidence anchors (`belagg_ankare`/`ankare`) to verbatim source blocks, runs
|
|
1331
|
+
BEFORE contract-check. Exit 0 = everything resolved (or no anchors in the file), 1 =
|
|
1332
|
+
at least one OLÖST/FLERTYDIG anchor (a contract breach, mechanical redispatch),
|
|
1333
|
+
2 = usage error.
|
|
1334
|
+
- `scripts/contract-check.mjs <run.json> <spec.md> [impact.md] [--expect-id <id>] [--state <state.json>] [--sprak <svenska|engelska>]`:
|
|
1335
|
+
the contract check of the score JSON AFTER the run: enums,
|
|
1336
|
+
confidence interval, score resolution, the presence of bracketing (also per
|
|
1337
|
+
alternative; the under-A1 case with floor null and ceiling A1 is valid), evidence/
|
|
1338
|
+
driver quotes as verbatim substrings of the source material, the protocol-term
|
|
1339
|
+
blacklist in customer-text fields, a closed envelope key list, request_id against the directory.
|
|
1340
|
+
INPUT_SAKNAS requires a missing list instead of question rows. An unresolved anchor
|
|
1341
|
+
(evidence missing despite `belagg_ankare`, or `ankare` without `citat`) is
|
|
1342
|
+
the dispatcher's error, not the agent's: exit 2 with a reference to resolve-anchors.mjs.
|
|
1343
|
+
The step schema: every `bracket.steps[i]` (also per alternative) is exactly
|
|
1344
|
+
`{anchor, relation, grund}` with `relation` from `storre`, `mindre`, `lika`;
|
|
1345
|
+
keys of its own and empty grounds are BROTT. The contradiction gate: if `impact.md` is given
|
|
1346
|
+
and the analysis's `**Deviation from the spec:**` carries `contradiction`, every
|
|
1347
|
+
outcome other than TILLBAKA_TILL_SPEC is a BROTT. It also tests the bracketing's
|
|
1348
|
+
consistency (floor before ceiling, between adjacent and inside floor–ceiling, a score
|
|
1349
|
+
between the between pair, relation against score; a score ON an anchor despite
|
|
1350
|
+
storre/mindre is a VARNING only on rounding collapse), that `type` is one of
|
|
1351
|
+
the scale's five types, and exempts blacklisted words that stand in the spec
|
|
1352
|
+
(ADR-0115). With `--sprak` every customer-text field is tested against the language (#521):
|
|
1353
|
+
at least two DIFFERENT function words from the wrong language, whole words, case-insensitive
|
|
1354
|
+
(the lists in `lib.mjs` `FUNKTIONSORD`), is BROTT `kundtext på fel språk`;
|
|
1355
|
+
diacritics are no signal. Without the flag, no language check.
|
|
1356
|
+
- `scripts/check-lasta.mjs "<LÄSTA-innehåll>" <spec.md> <impact.md>`:
|
|
1357
|
+
checks the scorer's read scope: the LÄSTA line must carry the source material's two
|
|
1358
|
+
paths and may beyond that carry only files under the skill's own directory.
|
|
1359
|
+
Exit 0 = ok, 1 = contract breach (BROTT rows), 2 = usage error.
|
|
1360
|
+
- `scripts/compare-scores.mjs <a.json> <b.json>`: the deterministic
|
|
1361
|
+
double-run diff: outcome,
|
|
1362
|
+
score, confidence, floor and ceiling anchors. Never prose. Writes `LIKA`,
|
|
1363
|
+
`LIKA_TOLERANS` (a score difference ≤ 0.5 with the same outcome, regardless of confidence,
|
|
1364
|
+
and anchor bands that overlap or are adjacent; exit 0, the verdict line points out
|
|
1365
|
+
the run with the highest score) or `OLIKA` + diff rows; exit 0/1, exit 2 =
|
|
1366
|
+
incomparable runs (a dispatcher error, never LIKA). Also diffs the inner
|
|
1367
|
+
steps: the same anchor in `bracket.steps` with `storre` in one run and
|
|
1368
|
+
`mindre` in the other gives the diff row
|
|
1369
|
+
`bracket.steps[A<n>].relation: "mindre" ↔ "storre"`, a hard diff that
|
|
1370
|
+
never falls under the tolerance; floor and ceiling anchors in either run
|
|
1371
|
+
are exempt, they flip by construction when the bands are adjacent. The script requires
|
|
1372
|
+
mode `pricing`; guard comparisons are a deliberate extension in the guard step.
|
|
1373
|
+
Adjacency is decided by the scale's order (lib.mjs ANKARE, A17 between A12 and
|
|
1374
|
+
A13), and beyond score and anchors, `type` (a tolerance row), the number of
|
|
1375
|
+
`conditions` (a hard diff) and `alternatives` per label (ADR-0115) are diffed.
|
|
1376
|
+
- `scripts/archive.mjs <kommando> <katalog> [flaggor]`: the archive's mechanics
|
|
1377
|
+
(full list in the script's header): `add-run`, `next-n`, `sparr`,
|
|
1378
|
+
`utslag`, `set-status`, `kalibrering`, `ny-korning`, `proveniens`, plus
|
|
1341
1379
|
`stopp <katalog> --utfall KONCEPT|DIAGNOS|TILLBAKA_TILL_SPEC --kalla spec.md|impact.md`
|
|
1342
|
-
(
|
|
1343
|
-
`--kalla impact.md`
|
|
1344
|
-
status), `respec <katalog>` (
|
|
1345
|
-
`impact-superseded-<datum>.md`,
|
|
1346
|
-
`approval`, `granskarutslag`
|
|
1347
|
-
`bluf <katalog>` (markdown
|
|
1348
|
-
|
|
1349
|
-
`bunt (N delar)`;
|
|
1350
|
-
`nasta-steg <katalog>` (
|
|
1351
|
-
`| post | står | väntar på | nästa |`
|
|
1352
|
-
|
|
1353
|
-
`/request
|
|
1354
|
-
|
|
1355
|
-
`okänt läge: <status>`
|
|
1356
|
-
`##
|
|
1357
|
-
`effort:`
|
|
1358
|
-
`--model`; `set-status priced`
|
|
1380
|
+
(writes `state.stopp` after checking that the source carries the outcome;
|
|
1381
|
+
`--kalla impact.md` sets status `assessed`, `--kalla spec.md` does not touch
|
|
1382
|
+
the status), `respec <katalog>` (puts `impact.md` aside as
|
|
1383
|
+
`impact-superseded-<datum>.md`, resets status (to `spec_draft`),
|
|
1384
|
+
`approval`, `granskarutslag` and `stopp`, never touches `runs[]`) and
|
|
1385
|
+
`bluf <katalog>` (the markdown table `| post | poäng | utfall | konfidens | anm |`
|
|
1386
|
+
over the directory and its `del-N/` recursively, the bundle root as
|
|
1387
|
+
`bunt (N delar)`; the values from the files, never from prose) and
|
|
1388
|
+
`nasta-steg <katalog>` (read-only, the markdown table
|
|
1389
|
+
`| post | står | väntar på | nästa |` over the directory and its `del-N/`
|
|
1390
|
+
recursively: where the item stands, what a human is expected to do and which
|
|
1391
|
+
`/request` command runs on, from `state.json` and the scorer's current
|
|
1392
|
+
outcome per the rule table in the script; an unknown state is written as
|
|
1393
|
+
`okänt läge: <status>` with exit 0, a broken `state.json` is exit 2, see
|
|
1394
|
+
`## Closing: where the item stands`). `add-run` reads
|
|
1395
|
+
`effort:` from the agent definition when `--effort` is omitted and canonicalizes
|
|
1396
|
+
`--model`; `set-status priced` refuses when `stopp` is set.
|
|
1359
1397
|
`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
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
UNION
|
|
1365
|
-
|
|
1366
|
-
`<modell>/k<N>`
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1398
|
+
is the ONLY writer of `experiment.json` (see `utvärdera` step 1): a locked
|
|
1399
|
+
read-modify-write with an atomic write; the directory and file are created if they are
|
|
1400
|
+
missing. `--satt` writes top-level fields append-only (the same value is a no-op,
|
|
1401
|
+
another value is exit 2; `eskalerad` is interpreted as a boolean), `--variant` is
|
|
1402
|
+
a UNION with preserved order, and `--korning` requires `--status` from the four
|
|
1403
|
+
status literals, touches no other run and requires that the directory
|
|
1404
|
+
`<modell>/k<N>` exists. `--pid` (an integer, only with `--korning`) writes
|
|
1405
|
+
the starter's pid on the row; a status write without `--pid` replaces the row
|
|
1406
|
+
entirely, so the pid disappears with the final status (#516). Exit 0 ok, 2
|
|
1407
|
+
a usage or state error.
|
|
1370
1408
|
- `scripts/svep.mjs --repo-rot <abs> --konto <namn> --modell <m>... --exp experiments/<slug>... [--tickets-parallellt 2] [--modeller-parallellt 4] [--om | --hoppa-över]`:
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1409
|
+
utvärdera's starter (see `utvärdera` step 4 and Batch). Runs two tickets at
|
|
1410
|
+
a time with at most four models per ticket in flight, the next ticket when one is completely
|
|
1411
|
+
done, and does per round `ny-korning`, the start of `korning.mjs`,
|
|
1374
1412
|
`stderr.log`, `matning.json`, `proveniens`,
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
`väntar på dom`
|
|
1379
|
-
|
|
1380
|
-
`onskemal.md`, `kallmanifest.md`
|
|
1381
|
-
|
|
1382
|
-
|
|
1413
|
+
model verification against `modelUsage` and status via `archive.mjs experiment`.
|
|
1414
|
+
The start row in `experiment.json` carries the sweep's pid (#516).
|
|
1415
|
+
Resumes from `experiment.json`: `klar` and `stopp` are skipped without `--om`,
|
|
1416
|
+
`väntar på dom` is tested with `sparr`, `ofärdig` is rerun, except when the row carries
|
|
1417
|
+
a living `pid` (in progress in another sweep, skipped as `pågår`, #516). An `--exp` without
|
|
1418
|
+
`onskemal.md`, `kallmanifest.md` or `experiment.json` with `request_id` is marked
|
|
1419
|
+
and skipped. Exit 0 when no run became `ofärdig`, 1 otherwise, 2 on a
|
|
1420
|
+
usage error.
|
|
1383
1421
|
- `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
|
-
|
|
1385
|
-
|
|
1386
|
-
`experiment.json`,
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
compare-scores) plus
|
|
1391
|
-
|
|
1392
|
-
`DELA_UPP`
|
|
1393
|
-
`--effort`
|
|
1394
|
-
`--modell`: `default`
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
(`korningsmotor`, `steg`
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1422
|
+
utvärdera's inner process (see `utvärdera` step 4), a script without
|
|
1423
|
+
model turns. Builds the prompts from `onskemal.md`, `kallmanifest.md` and
|
|
1424
|
+
`experiment.json`, where `sprak` is MANDATORY: if it is missing, an
|
|
1425
|
+
instruction is written on stderr and the script ends with exit 2 before any prompt
|
|
1426
|
+
is built (ADR-0114). Every agent step is a `claude -p --agent` process of its own with
|
|
1427
|
+
the raw reply under `k<N>/steg/`, and the checks (precheck, `kontroll.mjs`,
|
|
1428
|
+
compare-scores) plus the archiving (`archive.mjs`) are script calls.
|
|
1429
|
+
The bundle's parts run two at a time and the scorer's two runs at the same time;
|
|
1430
|
+
`DELA_UPP` is not run autonomously in v1 but gives the status `stopp` with a reason.
|
|
1431
|
+
`--effort` is an override on every agent process, symmetric with
|
|
1432
|
+
`--modell`: `default` gives no flag and add-run reads the definition as
|
|
1433
|
+
before; everything else is passed to the process and to `add-run --effort` as
|
|
1434
|
+
requested, never verified (#525). `korning.json` is the sum over the steps
|
|
1435
|
+
(`korningsmotor`, `steg` with `effort_begard` and `samtidiga_processer` per
|
|
1436
|
+
step, `modellverifiering` plus the report's usual keys). Exit 0 when
|
|
1437
|
+
the run went through, 1 when at least one part became unfinished, 2 on a
|
|
1438
|
+
usage error.
|
|
1401
1439
|
- `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
|
-
|
|
1403
|
-
JSON
|
|
1404
|
-
resolve-anchors
|
|
1405
|
-
|
|
1406
|
-
`confidence`
|
|
1407
|
-
LÄSTA
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
(split
|
|
1414
|
-
`experiment.json`,
|
|
1415
|
-
|
|
1416
|
-
stdout
|
|
1417
|
-
`LÄSTA:`,
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
## Dispatch
|
|
1426
|
-
|
|
1427
|
-
-
|
|
1428
|
-
- **
|
|
1440
|
+
the scorer's check chain in ONE process (see `bedöm` step 5): tail lines,
|
|
1441
|
+
JSON extraction, deterministic repairs, then check-lasta,
|
|
1442
|
+
resolve-anchors and contract-check in ONE process. Repairs what the dispatcher
|
|
1443
|
+
already knows: `request_id`, upper and lower case on `outcome`, `mode`,
|
|
1444
|
+
`confidence` and `bracket.steps[].relation`, string to number on `score`, and
|
|
1445
|
+
the LÄSTA line's path form. The repairs never touch score, outcome or
|
|
1446
|
+
evidence, and every repair is written as a `REPARERAT:` line that the caller
|
|
1447
|
+
books into the runs row's `note`. `--fixa-kundtext` rewrites a
|
|
1448
|
+
protocol-term breach in a pure customer-text field with a cheap model instead
|
|
1449
|
+
of costing a whole scorer run, at most one round. `--state <state.json>`
|
|
1450
|
+
and `--sprak <svenska|engelska>` go straight on to contract-check
|
|
1451
|
+
(the split status and the customer text's language respectively; `korning.mjs` takes the language from
|
|
1452
|
+
`experiment.json`, the manual flow from `SPRÅK:` in `kallmanifest.md`).
|
|
1453
|
+
A language breach is never fixable by the customer-text fix and goes to redispatch (#521).
|
|
1454
|
+
stdout carries `MODELL:`,
|
|
1455
|
+
`LÄSTA:`, any `AVVIKELSE:`, `REPARERAT:` lines and last the verdict. Exit 0 =
|
|
1456
|
+
the chain holds, 1 = BROTT (redispatch per ADR-0092), 2 = usage error.
|
|
1457
|
+
|
|
1458
|
+
The checks never assess content and never return protocol outcomes:
|
|
1459
|
+
an item that passes can still be stopped by the protocols themselves, and that is
|
|
1460
|
+
their job. On a script error (exit 2 or a crash): show the error and stop, never
|
|
1461
|
+
continue as if the check had passed.
|
|
1462
|
+
|
|
1463
|
+
## Dispatch contract (applies to all three agents)
|
|
1464
|
+
|
|
1465
|
+
- The prompt is self-contained: the agent does not see this conversation.
|
|
1466
|
+
- **Prompt templates, the labels verbatim:**
|
|
1429
1467
|
|
|
1430
1468
|
`request-specifier`:
|
|
1431
1469
|
|
|
@@ -1450,14 +1488,14 @@ aldrig vidare som om kontrollen passerat.
|
|
|
1450
1488
|
ESKALERAD: <ja|nej>
|
|
1451
1489
|
```
|
|
1452
1490
|
|
|
1453
|
-
`RUBRIKER:`
|
|
1454
|
-
|
|
1455
|
-
|
|
1491
|
+
`RUBRIKER:` is the skeleton in the spec's language, the literals verbatim. The
|
|
1492
|
+
Swedish one stands above; with `SPRÅK: engelska` the seven lines are taken from
|
|
1493
|
+
the heading table in `request-write-spec/references/specformat.md`
|
|
1456
1494
|
(Problem, Solution, Done when, Not included, Appearance, Requires,
|
|
1457
|
-
Conditions).
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1495
|
+
Conditions). Footnote: a candidate replaces Lösning and När det är klart (Solution and Done when) with
|
|
1496
|
+
the marker line (`**Koncept-kandidat**` or `**Diagnos-kandidat**`, in
|
|
1497
|
+
English `**Concept candidate**` or `**Diagnosis candidate**`); the
|
|
1498
|
+
specifier decides that itself per the skill.
|
|
1461
1499
|
|
|
1462
1500
|
`request-assessor`:
|
|
1463
1501
|
|
|
@@ -1465,6 +1503,18 @@ aldrig vidare som om kontrollen passerat.
|
|
|
1465
1503
|
REPO-ROT: <absolut sökväg>
|
|
1466
1504
|
DEPS: <installerade | saknas>
|
|
1467
1505
|
SPRÅK: <svenska | engelska>
|
|
1506
|
+
RUBRIKER:
|
|
1507
|
+
**Affected repos and files**
|
|
1508
|
+
**Data storage**
|
|
1509
|
+
**Integration points**
|
|
1510
|
+
**Flows**
|
|
1511
|
+
**Interfaces**
|
|
1512
|
+
**Verification**
|
|
1513
|
+
**Splittability**
|
|
1514
|
+
**Consequence of failure**
|
|
1515
|
+
**Decisions taken**
|
|
1516
|
+
**Assumptions**
|
|
1517
|
+
**Dependencies**
|
|
1468
1518
|
KÄLLOR:
|
|
1469
1519
|
<kallmanifest.md ordagrant, eller "inga">
|
|
1470
1520
|
|
|
@@ -1475,7 +1525,12 @@ aldrig vidare som om kontrollen passerat.
|
|
|
1475
1525
|
<granskarblock.md bytetroget>
|
|
1476
1526
|
```
|
|
1477
1527
|
|
|
1478
|
-
`
|
|
1528
|
+
`RUBRIKER:` is the analysis's own heading skeleton, the English literals from
|
|
1529
|
+
`IMPACT_HEADINGS` (the analysis is internal, see The language contract).
|
|
1530
|
+
The skeleton is independent of `SPRÅK:`, which steers only the analysis's customer-facing
|
|
1531
|
+
parts, not this internal structure.
|
|
1532
|
+
|
|
1533
|
+
`request-scorer` (source material by path, ADR-0099: never pasted):
|
|
1479
1534
|
|
|
1480
1535
|
```
|
|
1481
1536
|
REQUEST_ID: <gid eller slug>
|
|
@@ -1487,48 +1542,48 @@ aldrig vidare som om kontrollen passerat.
|
|
|
1487
1542
|
PÅVERKANSANALYS: <absolut sökväg till impact.md>
|
|
1488
1543
|
```
|
|
1489
1544
|
|
|
1490
|
-
(guard
|
|
1491
|
-
guard
|
|
1492
|
-
|
|
1493
|
-
`SPRÅK:`
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
`contract-check --sprak`
|
|
1498
|
-
|
|
1499
|
-
- **
|
|
1500
|
-
(
|
|
1501
|
-
(
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
AVVIKELSE
|
|
1506
|
-
`scripts/check-lasta.mjs` (ADR-0099):
|
|
1507
|
-
|
|
1508
|
-
under
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
-
|
|
1512
|
-
|
|
1513
|
-
- **
|
|
1514
|
-
|
|
1545
|
+
(guard mode adds `FULLPLAN:` and `TIDIGARE_POANG:`; the
|
|
1546
|
+
guard command comes in a later step.)
|
|
1547
|
+
|
|
1548
|
+
`SPRÅK:` is the customer text's language for the assessor and the scorer (#521): the same
|
|
1549
|
+
value the specifier got, from `experiment.json` (`sprak`) in utvärdera and
|
|
1550
|
+
from `SPRÅK:` in `kallmanifest.md` in the manual flow. The analysis's
|
|
1551
|
+
customer-facing parts and the envelope's customer-text fields are written in that language;
|
|
1552
|
+
`contract-check --sprak` rejects customer text in the wrong language.
|
|
1553
|
+
|
|
1554
|
+
- **Tail lines after the main content**, in this order: `MODELL: <modell-id>`
|
|
1555
|
+
(all agents), `LÄSTA: <kommaseparerade absoluta sökvägar eller "inga">`
|
|
1556
|
+
(only the scorer: its Read is prompt-restricted, the line makes the rule
|
|
1557
|
+
reviewable), `AVVIKELSE: <text>` (only if something in the prompt broke the agent's
|
|
1558
|
+
rules, e.g. a price list in the source material). The tail lines are stripped before every
|
|
1559
|
+
mechanical check and before archiving; MODELL is noted in the runs row,
|
|
1560
|
+
AVVIKELSE is always shown to the user. LÄSTA is checked MECHANICALLY by
|
|
1561
|
+
`scripts/check-lasta.mjs` (ADR-0099): the line must carry the source material's two
|
|
1562
|
+
pointed-out paths (both mandatory) and may beyond that carry only files
|
|
1563
|
+
under the skill's own directory (the reference file); everything else is a
|
|
1564
|
+
contract breach and is treated as BROTT in bedöm step 5 (redispatch ONCE,
|
|
1565
|
+
then stop), never just a notice.
|
|
1566
|
+
- The agent's reply otherwise is raw data per the skill's contract (a prose document
|
|
1567
|
+
or JSON), no commentary around it.
|
|
1568
|
+
- **Redispatch (max ONE per agent and round):** send the WHOLE original prompt
|
|
1569
|
+
unchanged, followed by a block:
|
|
1515
1570
|
```
|
|
1516
1571
|
RÄTTNINGAR:
|
|
1517
1572
|
<felraderna från kontrollen, citerade ordagrant>
|
|
1518
1573
|
```
|
|
1519
|
-
|
|
1520
|
-
**
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
##
|
|
1527
|
-
|
|
1528
|
-
-
|
|
1529
|
-
|
|
1530
|
-
-
|
|
1531
|
-
|
|
1532
|
-
- Guard
|
|
1533
|
-
request-score
|
|
1534
|
-
|
|
1574
|
+
A new subagent remembers nothing: error lines alone without the source material are useless.
|
|
1575
|
+
**The retake is always booked (ADR-0099):** the rejected attempt is archived
|
|
1576
|
+
with an `-avvisad` suffix (pricing: `<typ>-pricing-vN-avvisad.json`;
|
|
1577
|
+
text steps: `spec-avvisad-<datum>.md` / `impact-avvisad-<datum>.md`) and gets
|
|
1578
|
+
a runs row of its own with the check's error lines: two runs of the same step
|
|
1579
|
+
without a trace of why is exactly the gap the receipt closes.
|
|
1580
|
+
|
|
1581
|
+
## Do not use when
|
|
1582
|
+
|
|
1583
|
+
- The user wants to run a single skill standalone ("skriv bara en spec"):
|
|
1584
|
+
invoke that skill directly.
|
|
1585
|
+
- The question concerns Asana setup ("koppla repot"): that is request-sync's
|
|
1586
|
+
setup flow, run it directly.
|
|
1587
|
+
- Guard mode ("kör guarden", before the first line of code): runs for now via
|
|
1588
|
+
request-score directly; a `guard` command comes in a
|
|
1589
|
+
later step.
|