@zenera/cli 1.1.24 → 1.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/README.md +65 -0
  2. package/dist/ask.js +28 -5
  3. package/dist/banner.js +2 -2
  4. package/dist/batch.d.ts +6 -0
  5. package/dist/batch.js +76 -1
  6. package/dist/catalog.d.ts +31 -1
  7. package/dist/catalog.js +140 -7
  8. package/dist/commands/index.js +2 -0
  9. package/dist/commands/inspect.d.ts +52 -2
  10. package/dist/commands/inspect.js +220 -31
  11. package/dist/commands/memory.js +156 -32
  12. package/dist/commands/meta.js +333 -24
  13. package/dist/commands/models.js +97 -12
  14. package/dist/commands/open.js +4 -4
  15. package/dist/commands/run.js +38 -4
  16. package/dist/commands/sandbox.js +148 -16
  17. package/dist/commands/view.d.ts +8 -0
  18. package/dist/commands/view.js +114 -0
  19. package/dist/engine.d.ts +8 -1
  20. package/dist/engine.js +7 -0
  21. package/dist/eventlog.d.ts +64 -0
  22. package/dist/eventlog.js +170 -0
  23. package/dist/liveness.d.ts +7 -0
  24. package/dist/liveness.js +35 -3
  25. package/dist/main.js +0 -0
  26. package/dist/meta/editor.d.ts +9 -0
  27. package/dist/meta/editor.js +43 -0
  28. package/dist/meta/finetune/analyze-brief.d.ts +26 -0
  29. package/dist/meta/finetune/analyze-brief.js +131 -0
  30. package/dist/meta/finetune/apply-brief.d.ts +23 -0
  31. package/dist/meta/finetune/apply-brief.js +247 -0
  32. package/dist/meta/finetune/brief.d.ts +45 -0
  33. package/dist/meta/finetune/brief.js +158 -0
  34. package/dist/meta/finetune/children.d.ts +8 -0
  35. package/dist/meta/finetune/children.js +76 -0
  36. package/dist/meta/finetune/command.d.ts +30 -0
  37. package/dist/meta/finetune/command.js +516 -0
  38. package/dist/meta/finetune/cost.d.ts +46 -0
  39. package/dist/meta/finetune/cost.js +105 -0
  40. package/dist/meta/finetune/dataset/anchors.d.ts +41 -0
  41. package/dist/meta/finetune/dataset/anchors.js +239 -0
  42. package/dist/meta/finetune/dataset/changes.d.ts +36 -0
  43. package/dist/meta/finetune/dataset/changes.js +319 -0
  44. package/dist/meta/finetune/dataset/command.d.ts +17 -0
  45. package/dist/meta/finetune/dataset/command.js +1011 -0
  46. package/dist/meta/finetune/dataset/drift.d.ts +33 -0
  47. package/dist/meta/finetune/dataset/drift.js +71 -0
  48. package/dist/meta/finetune/dataset/export.d.ts +26 -0
  49. package/dist/meta/finetune/dataset/export.js +51 -0
  50. package/dist/meta/finetune/dataset/index.d.ts +2 -0
  51. package/dist/meta/finetune/dataset/index.js +2 -0
  52. package/dist/meta/finetune/dataset/sample.d.ts +50 -0
  53. package/dist/meta/finetune/dataset/sample.js +197 -0
  54. package/dist/meta/finetune/dataset/schema.d.ts +33 -0
  55. package/dist/meta/finetune/dataset/schema.js +216 -0
  56. package/dist/meta/finetune/dataset/store.d.ts +35 -0
  57. package/dist/meta/finetune/dataset/store.js +177 -0
  58. package/dist/meta/finetune/dataset/types.d.ts +127 -0
  59. package/dist/meta/finetune/dataset/types.js +39 -0
  60. package/dist/meta/finetune/failure.d.ts +15 -0
  61. package/dist/meta/finetune/failure.js +26 -0
  62. package/dist/meta/finetune/feedback.d.ts +57 -0
  63. package/dist/meta/finetune/feedback.js +104 -0
  64. package/dist/meta/finetune/guard.d.ts +29 -0
  65. package/dist/meta/finetune/guard.js +104 -0
  66. package/dist/meta/finetune/improvements.d.ts +98 -0
  67. package/dist/meta/finetune/improvements.js +501 -0
  68. package/dist/meta/finetune/index.d.ts +2 -0
  69. package/dist/meta/finetune/index.js +2 -0
  70. package/dist/meta/finetune/journal.d.ts +35 -0
  71. package/dist/meta/finetune/journal.js +135 -0
  72. package/dist/meta/finetune/live.d.ts +84 -0
  73. package/dist/meta/finetune/live.js +272 -0
  74. package/dist/meta/finetune/loop.d.ts +3 -0
  75. package/dist/meta/finetune/loop.js +163 -0
  76. package/dist/meta/finetune/memories.d.ts +47 -0
  77. package/dist/meta/finetune/memories.js +154 -0
  78. package/dist/meta/finetune/recheck.d.ts +58 -0
  79. package/dist/meta/finetune/recheck.js +311 -0
  80. package/dist/meta/finetune/report.d.ts +40 -0
  81. package/dist/meta/finetune/report.js +1397 -0
  82. package/dist/meta/finetune/retry.d.ts +24 -0
  83. package/dist/meta/finetune/retry.js +96 -0
  84. package/dist/meta/finetune/sampler.d.ts +18 -0
  85. package/dist/meta/finetune/sampler.js +47 -0
  86. package/dist/meta/finetune/system.d.ts +27 -0
  87. package/dist/meta/finetune/system.js +132 -0
  88. package/dist/meta/finetune/train.d.ts +43 -0
  89. package/dist/meta/finetune/train.js +425 -0
  90. package/dist/meta/finetune/tui.d.ts +16 -0
  91. package/dist/meta/finetune/tui.js +316 -0
  92. package/dist/meta/finetune/tuning.d.ts +193 -0
  93. package/dist/meta/finetune/tuning.js +308 -0
  94. package/dist/meta/finetune/usage.d.ts +66 -0
  95. package/dist/meta/finetune/usage.js +260 -0
  96. package/dist/meta/finetune/view.d.ts +9 -0
  97. package/dist/meta/finetune/view.js +27 -0
  98. package/dist/meta/host.d.ts +15 -0
  99. package/dist/meta/host.js +21 -0
  100. package/dist/meta/index.d.ts +11 -0
  101. package/dist/meta/index.js +11 -0
  102. package/dist/meta/launch.d.ts +81 -0
  103. package/dist/meta/launch.js +286 -0
  104. package/dist/meta/model.d.ts +44 -0
  105. package/dist/meta/model.js +119 -0
  106. package/dist/meta/prompts.d.ts +18 -0
  107. package/dist/meta/prompts.js +61 -0
  108. package/dist/meta/relay.d.ts +20 -0
  109. package/dist/meta/relay.js +123 -0
  110. package/dist/meta/tokens.d.ts +52 -0
  111. package/dist/meta/tokens.js +226 -0
  112. package/dist/meta/window.d.ts +61 -0
  113. package/dist/meta/window.js +198 -0
  114. package/dist/meta/wire.d.ts +42 -0
  115. package/dist/meta/wire.js +129 -0
  116. package/dist/meta.d.ts +71 -1
  117. package/dist/meta.js +303 -6
  118. package/dist/modelref.d.ts +16 -0
  119. package/dist/modelref.js +41 -0
  120. package/dist/narrate.js +5 -2
  121. package/dist/podman.d.ts +26 -0
  122. package/dist/podman.js +83 -3
  123. package/dist/projects.d.ts +0 -5
  124. package/dist/projects.js +2 -15
  125. package/dist/resources.d.ts +215 -0
  126. package/dist/resources.js +856 -0
  127. package/dist/sandbox.d.ts +2 -0
  128. package/dist/sandbox.js +10 -3
  129. package/dist/scaffold.d.ts +15 -7
  130. package/dist/scaffold.js +24 -67
  131. package/dist/session.js +4 -29
  132. package/dist/term.d.ts +2 -0
  133. package/dist/term.js +11 -2
  134. package/dist/trace.d.ts +5 -0
  135. package/dist/trace.js +107 -7
  136. package/dist/tui/app.js +19 -5
  137. package/dist/tui/links.d.ts +27 -0
  138. package/dist/tui/links.js +142 -0
  139. package/dist/tui/models.d.ts +35 -0
  140. package/dist/tui/models.js +447 -0
  141. package/dist/tui/sandbox.d.ts +10 -0
  142. package/dist/tui/sandbox.js +131 -0
  143. package/dist/tui/wrap.d.ts +5 -0
  144. package/dist/tui/wrap.js +12 -0
  145. package/dist/usage.d.ts +78 -0
  146. package/dist/usage.js +51 -0
  147. package/package.json +4 -2
  148. package/templates/editor/.github/copilot-instructions.md +60 -145
  149. package/templates/editor/.github/prompts/analyze.prompt.md +33 -0
  150. package/templates/editor/.github/prompts/dataset.prompt.md +108 -0
  151. package/templates/editor/.github/prompts/finetune-apply.prompt.md +270 -0
  152. package/templates/editor/.github/prompts/finetune.prompt.md +691 -198
  153. package/templates/editor/.github/prompts/new-agent.prompt.md +3 -1
  154. package/templates/editor/.github/prompts/new-skill.prompt.md +2 -0
  155. package/templates/editor/.github/prompts/project-review.prompt.md +5 -4
  156. package/templates/editor/.github/skills/zen-analyze-run/SKILL.md +588 -0
  157. package/templates/editor/.github/skills/zen-cli/SKILL.md +9 -1
  158. package/templates/editor/.github/skills/zen-cli/references/dataset.md +34 -0
  159. package/templates/editor/.github/skills/zen-cli/references/finetune.md +137 -0
  160. package/templates/editor/.github/skills/zen-cli/references/inspect.md +63 -11
  161. package/templates/editor/.github/skills/zen-cli/references/memory.md +33 -10
  162. package/templates/editor/.github/skills/zen-cli/references/meta.md +61 -0
  163. package/templates/editor/.github/skills/zen-cli/references/models.md +5 -1
  164. package/templates/editor/.github/skills/zen-cli/references/projects.md +4 -6
  165. package/templates/editor/.github/skills/zen-cli/references/rag.md +24 -6
  166. package/templates/editor/.github/skills/zen-cli/references/sandbox.md +35 -14
  167. package/templates/editor/.github/skills/zen-code-python/SKILL.md +123 -0
  168. package/templates/editor/.github/skills/zen-code-python/references/code_python.md +48 -0
  169. package/templates/editor/.github/skills/zen-dataset/SKILL.md +253 -0
  170. package/templates/editor/.github/skills/zen-finetune/SKILL.md +1492 -660
  171. package/templates/editor/.github/skills/zen-finetune/references/readme-template.md +268 -0
  172. package/templates/editor/.github/skills/zen-finetune/scripts/batch.mjs +390 -0
  173. package/templates/editor/.github/skills/zen-finetune/scripts/difficult.mjs +243 -0
  174. package/templates/editor/.github/skills/zen-finetune/scripts/memory.mjs +280 -0
  175. package/templates/editor/.github/skills/zen-finetune/scripts/next.mjs +398 -0
  176. package/templates/editor/.github/skills/zen-finetune/scripts/report.mjs +797 -0
  177. package/templates/editor/.github/skills/zen-finetune/scripts/select.mjs +354 -0
  178. package/templates/editor/.github/skills/zen-finetune/scripts/usage.mjs +722 -0
  179. package/templates/editor/.github/skills/zen-inspect/SKILL.md +74 -26
  180. package/templates/editor/.github/skills/zen-inspect-ask/SKILL.md +271 -0
  181. package/templates/editor/.github/skills/zen-instructions/SKILL.md +4 -12
  182. package/templates/editor/.github/skills/zen-memory/SKILL.md +82 -53
  183. package/templates/editor/.github/skills/zen-rag-docs/SKILL.md +20 -8
  184. package/templates/editor/.github/skills/zen-review/SKILL.md +8 -31
  185. package/templates/editor/.github/skills/zen-review/scripts/check-paths.sh +5 -5
  186. package/templates/editor/.github/skills/zen-review/scripts/review.sh +3 -12
  187. package/templates/editor/.github/skills/zen-sandbox/SKILL.md +9 -4
  188. package/templates/editor/.github/skills/zen-sandbox-capacity/SKILL.md +2 -2
  189. package/templates/editor/.github/skills/zen-spec-sync/SKILL.md +2 -4
  190. package/templates/editor/.github/skills/zen-topology/SKILL.md +283 -0
  191. package/templates/editor/.vscode/settings.json +1 -0
  192. package/templates/project/agents/files-instructions.md +29 -20
  193. package/templates/project/agents/fork-instructions.md +78 -28
  194. package/templates/project/agents/memory-instructions.md +222 -494
  195. package/templates/project/agents/tools-instructions.md +14 -4
  196. package/templates/project/gitignore +2 -0
  197. package/templates/editor/.github/skills/zen-finetune/scripts/collect.sh +0 -432
  198. package/templates/editor/.github/skills/zen-finetune/scripts/sample.sh +0 -199
  199. package/templates/editor/.github/skills/zen-memory/scripts/check-instructions.sh +0 -116
@@ -1,114 +1,313 @@
1
1
  ---
2
- description: Fine-tune this project against a training set in two stages - first without memory, tuning the prose; then with memory, tuning recall for speed.
2
+ description: Fine-tune this project against a training set, a batch at a time - every batch first with no memory, fixing the instructions, then with the memory it just built, making the same work cheaper - starting from a 3-case smoke test and doubling each smooth batch, until every case is used and no difficult case is open.
3
3
  ---
4
4
 
5
- Tune this project against real queries. Nothing here touches model weights: the
6
- parameters are the agent prompts, the skills and the house rules under `agents/`,
7
- and the evidence is the trajectories the queries produce.
8
-
9
- **Load the `zen-finetune` skill before anything else**, and load `zen-inspect`
10
- before you grade, `zen-instructions` before you write a policy file, and
11
- `zen-sandbox-capacity` if a batch dies at 137 or 124. This prompt is the order of
12
- the work; the skill is how each step is done, and it holds the rules that are
13
- easy to get wrong.
14
-
15
- This prompt is **reentrant**. Tuning is long and gets interrupted - a batch dies,
16
- a session ends, I stop you mid-round. Every step leaves its result on disk under
17
- `.finetune/`, so a second invocation does not start over: it reads that state,
18
- works out which stage and which step the work stopped at, says so, and carries
19
- on from there unless I tell you otherwise.
20
-
21
- ## The two stages
22
-
23
- This is two stages, run in order, never interleaved. Say which stage you are in
24
- at the top of every report.
25
-
26
- **Stage 1 - without memory.** Every batch runs against an empty memory, so every
27
- run starts from nothing and the result is attributable to the prose. Here you
28
- tune agent prompts, `agents/instructions.md`, and the project's skills - creating
29
- or updating them. The runs still _write_ memory, into their own throwaway graphs,
30
- and what they write is graded too: the commits have to be generalisable, durable
31
- facts, not cached live readings. Stage 1 optimises **correctness**.
32
-
33
- **Stage 2 - with memory.** Only once stage 1 is stable and well behaved. The
34
- prompts, skills and house rules are now frozen; the merged memory from the last
35
- good cold batch is copied to every item, which reads it and writes into its own
36
- copy, and the only file you edit is `agents/memory-policy-instructions.md`.
37
- Stage 2 optimises **memory use and memory correctness**, and the expected result
38
- is a dramatic drop in tokens and wall clock through reuse, with no verdict
39
- changed.
40
-
41
- Do not start stage 2 while any sample still fails cold. Memory will hide the
42
- defect and it will come back the day the graph is rebuilt.
5
+ Tune this project against real queries. Nothing here touches model weights: what
6
+ changes is the prose the project is made of - the agent prompts, the skills and
7
+ the house rules under `agents/` - and the evidence is the trajectories the
8
+ queries produce.
9
+
10
+ **Load the `zen-finetune` skill before anything else**, and load `zen-inspect`,
11
+ `zen-analyze-run`, `zen-topology` and `zen-inspect-ask` before you grade, and
12
+ `zen-instructions` before you write a policy file. This prompt is the order of the work; the skill
13
+ is how each step is done, and it holds the rules that are easy to get wrong. If
14
+ loading a skill fails, read `.github/skills/<name>/SKILL.md` with your file tool
15
+ before going on - never carry on without it.
16
+
17
+ **Read runs and memories through commands, never by code.** Do not open
18
+ `output.json`, `batch.json`, `state.json`, `graph.json` or `manifest.json` with
19
+ `node -e`, `python -c`, `jq` or a script of your own - every question has a
20
+ command:
21
+
22
+ | Question | Command |
23
+ | -------------------------------------------- | ----------------------------------------------------------------- |
24
+ | each case's trajectory directory | `report.mjs -d "$RUN/batch" paths [id...]` |
25
+ | what each case answered, under its rubric | `report.mjs -d "$RUN/batch" answers [id...]` |
26
+ | what each case committed to memory | `report.mjs -d "$RUN/batch" commits [id...]` |
27
+ | every recall and memory tool step | `report.mjs -d "$RUN/batch" recalls [id...]` |
28
+ | one memory node in full | `zen memory show <node-id> --dir "$RUN/batch/<id>/memory"` |
29
+ | a whole memory, or what one added to another | `zen memory ls --dir <dir>`, `zen memory diff <base> --dir <dir>` |
30
+
31
+ This prompt is **reentrant**. Tuning is long and gets interrupted - a run dies, a
32
+ session ends, I stop you mid-batch. Every step leaves its result on disk under
33
+ `finetune/`, so a second invocation does not start over: it reads that state,
34
+ works out where the work stopped, says so, and carries on from there unless I
35
+ tell you otherwise.
36
+
37
+ ## Words used here
38
+
39
+ Use these words, and only these, in everything you write - findings, README,
40
+ reports to me. Two words for one thing is how a reader loses the thread.
41
+
42
+ | Word | Means |
43
+ | -------------------- | -------------------------------------------------------------------------------------------- |
44
+ | **case** | one query from the training set, with its rubric if it has one |
45
+ | **dataset** | every case in the training set, `finetune/dataset.json` - never cut down |
46
+ | **limit** | how many cases this tuning uses - the whole dataset, unless I gave a smaller number |
47
+ | **selection** | those cases, evenly by class, rubric and complex first, `finetune/selection.json` |
48
+ | **batch** | the cases worked on together until they pass without memory and then with it |
49
+ | **batch size** | how many new cases a batch takes - 3 for batch 1, doubling after each smooth batch, to a max |
50
+ | **recheck cases** | cases from earlier batches, run again - difficult ones first, then complex ones |
51
+ | **run** | one execution of a batch's cases - a no-memory run or a with-memory run |
52
+ | **no-memory run** | every case starts from an empty memory and writes its own: tunes the instructions |
53
+ | **with-memory run** | every case starts from its own copy of the candidate memory: tunes the memory policy |
54
+ | **proposal** | one improvement a case's grading suggests - merged with the others before anything is edited |
55
+ | **passed** | a run where every case is right AND its review found nothing left to cut |
56
+ | **concurrency** | how many cases execute at the same time inside a run - 16 at most, halved on OOM |
57
+ | **trajectory** | what one case did in one run - its graph |
58
+ | **difficult case** | a case that caused real trouble - listed in `finetune/difficult.json`, retested until fixed |
59
+ | **candidate memory** | the last good memory plus what the batch's passing no-memory run committed |
60
+ | **last good memory** | every passed batch's candidate, in turn; always whole on disk |
61
+
62
+ Run directories are `finetune/runs/batch<NN>-<nomem|mem>-run<N>/` -
63
+ `batch03-nomem-run2` is batch 3's second run without memory, `batch03-mem-run1`
64
+ its first with memory. Each holds a `batch/` folder: that is `zen run batch`'s
65
+ own output for that run, named by the command, not by this method.
66
+
67
+ ## What a batch is
68
+
69
+ The selection is not run all at once. After one big run there is too much to
70
+ read, and after one big edit nobody can say which change helped. So it is
71
+ worked a **batch** at a time, and each batch is decided when it is needed:
72
+
73
+ ```
74
+ size = 3 for batch 1 - a smoke test
75
+ = twice the last batch, up to maxBatch, if it went smoothly
76
+ (at most 2 runs of each kind, no case put on the difficult list)
77
+ = the same as the last batch otherwise
78
+ next batch = that many new cases (not used yet: classes taking turns,
79
+ rubric and complex cases first)
80
+ + up to recheck earlier cases (open difficult first, then fixed
81
+ difficult, then complex, then the rest)
82
+ ```
83
+
84
+ Then every case of the batch goes through the same fifteen steps:
85
+
86
+ **Without memory**
87
+
88
+ 1. **Run** the batch with no memory.
89
+ 2. **Analyze** every trajectory: right? what did it cost? where is it
90
+ suboptimal? `zen inspect ask` the model at each critical point.
91
+ 3. **See what it committed** to memory - durable facts, or live readings?
92
+ 4. **Propose** improvements, per case.
93
+ 5. **Merge** the proposals of every case into patterns.
94
+ 6. **Update** prompts, skills, house rules, policies - create what is missing.
95
+ 7. **Run again**, no memory, the same cases.
96
+ 8. **See** whether the edits worked and fixed everything. If not, back to 2 -
97
+ at most 4 no-memory runs. When every case is right and nothing is left to
98
+ cut, the run **passes**.
99
+
100
+ **With memory**
101
+
102
+ 9. **Build the candidate**: the last good memory plus what every case of the
103
+ passing run committed, merged - what the graph already held is folded, not
104
+ added.
105
+ 10. **Run** the batch with memory: **every case gets its own copy** of the
106
+ candidate.
107
+ 11. **Analyze the memory use** - recall, savings, re-commits - with
108
+ `zen inspect ask` and the other tools.
109
+ 12. **Merge** the proposals.
110
+ 13. **Update** the memory policy.
111
+ 14. **Run again** from the same candidate. Back to 11 until it passes - at most
112
+ 4 with-memory runs.
113
+ 15. **Keep it**: the candidate becomes the last good memory, and the next batch
114
+ is built, with the difficult and complex cases tracked for rechecks.
115
+
116
+ A run whose cases are all correct has **not** passed: that is exactly when the
117
+ cost work starts. Being right is the entry ticket; the tuning is making it
118
+ cheap - first without memory, then with it.
119
+
120
+ The recheck cases are there to fail - a rule written for batch 3 is read on every
121
+ query, and the case it breaks is usually in batch 1.
122
+
123
+ ### Difficult cases, and why the number of batches is not fixed
124
+
125
+ Some cases cause most of the trouble. Put each on the difficult list the moment
126
+ grading shows it - `difficult.mjs add` - and it keeps coming back:
127
+
128
+ - **Open difficult cases come first** in every later batch's rechecks, until
129
+ fixed.
130
+ - **Fixed ones stay ahead of the other rechecks**, because what was hard once
131
+ breaks again first.
132
+ - **A batch that added one does not grow the next.**
133
+ - **When the selection is used up**, a batch is made of open difficult cases
134
+ only. So the tuning takes as many batches as it needs.
135
+ - **Stuck is reported, not looped.** A case still open after `maxRetries`
136
+ batches since it was added (default 3) is `stuck` - the model's ceiling or a
137
+ bad rubric - and stops being picked.
138
+
139
+ The tuning is over when **every case in the selection has been used and no
140
+ difficult case is open** - then comes the final check.
141
+
142
+ ### The memory
143
+
144
+ When a no-memory run passes, `memory.mjs candidate <run>` copies the last good
145
+ memory into a new directory and merges in what each of that run's cases
146
+ committed. Every with-memory run of the batch starts from that candidate, a
147
+ copy per case, so no case's commits reach another case or the candidate. When a
148
+ with-memory run passes, `memory.mjs checkpoint <run>` makes the candidate the
149
+ last good memory - switching only when it is complete. So whenever the tuning
150
+ stops - finished, or killed mid-run - `memory.mjs path` names a whole graph
151
+ holding everything the passed batches learned, ready to promote into `memory/`.
152
+
153
+ ## No memory first, then memory - in every batch
154
+
155
+ Never run them together, and never run a batch with memory before it passed
156
+ without. Say which kind of run you are in at the top of every report.
157
+
158
+ **No-memory runs.** Every case starts from an empty memory, so a right answer is
159
+ the instructions' doing and not something remembered. Here you tune agent
160
+ prompts, `agents/instructions.md`, the project's skills and policy files -
161
+ creating or updating them. The runs still _write_ memory, each into its own
162
+ throwaway graph, and what they write is graded too: commits must be durable,
163
+ generalisable facts, not cached live readings - because the candidate is built
164
+ from them. These runs optimise **correctness** first, then the cost of getting
165
+ there - fewer llm calls, then better fan-out with forks, then fewer things
166
+ discovered that could have been known.
167
+
168
+ **With-memory runs.** The same cases, from the candidate. They optimise **memory
169
+ use and memory correctness**, and the expected result is a dramatic drop in
170
+ tokens and wall clock with no verdict changed - and **no commit of anything the
171
+ memory already held**: every such commit is a call spent on nothing. The only
172
+ prose they edit is the memory policy - `agents/memory-policy-instructions.md`,
173
+ or the memory paragraph a `why` line names. A finding that needs any other
174
+ prose is an open problem whose cases go on the difficult list; the next batch's
175
+ no-memory runs fix it.
176
+
177
+ ## The README
178
+
179
+ `finetune/README.md` is the page I read to know what is going on. **You write
180
+ it**, by hand, for a person who has never seen this project - not a log dump.
181
+ Start from `.github/skills/zen-finetune/references/readme-template.md`.
182
+
183
+ - **Before the first run**, write it whole: what is being tuned and why, the
184
+ dataset by class, the limit and how the cases were chosen, what a batch is and
185
+ how it grows, its two halves, the settings - and a mermaid **map** from the
186
+ dataset to the final check, with the batches the growth rule gives if every
187
+ batch is smooth drawn ahead, each with its no-memory and with-memory half.
188
+ - **After every step** - a run finishing, a run graded, edits applied, a
189
+ candidate built, a batch passing - patch it: "Where we are", the map (move
190
+ nodes between the `done` / `now` / `ahead` / `failed` classes, update their
191
+ labels, add a node when a batch the plan did not foresee is built), the
192
+ batch's row, "Difficult cases", a log entry, "Results so far" when a batch
193
+ passes, and "Next".
194
+
195
+ The map is the first thing I look at: it must always show exactly one `now` node,
196
+ everything behind it as done, and everything ahead of it as ahead, with a line
197
+ saying what is left - unused cases and open difficult ones. Link me to the README
198
+ in your reports instead of re-typing it.
43
199
 
44
200
  ## Where you are - work this out before anything else
45
201
 
46
- Do this every time this prompt runs, including the first - on a project that has
47
- never been tuned it costs one `ls` and tells you to start at section 0.
202
+ Do this every time this prompt runs, including the first:
48
203
 
49
204
  ```sh
50
- ls .finetune/dataset.json .finetune/rounds 2>/dev/null
51
- for r in .finetune/rounds/*/; do
52
- printf '%s ' "$r"
53
- for f in cases.json batch/batch.json findings.md changes.md; do
54
- [ -e "$r$f" ] && printf '%s ' "$f"
55
- done
56
- echo
57
- done
205
+ .github/skills/zen-finetune/scripts/next.mjs
206
+ .github/skills/zen-finetune/scripts/batch.mjs # progress: used, left, next size, difficult
207
+ .github/skills/zen-finetune/scripts/memory.mjs # the candidate and the last good memory
58
208
  ```
59
209
 
60
- The rounds are ordered by name, so the last one is the live one. Read its
61
- `findings.md` heading for the stage - it says `COLD` or `WARM` - and its
62
- `changes.md` for what was already applied. Then resume by the artefacts the round
63
- is missing:
64
-
65
- | State of the last round | Where the work stopped | Resume at |
66
- | --------------------------------------- | ----------------------------------- | -------------------------------------------------------------------------- |
67
- | no `.finetune/` at all | nothing has been done | section 0, then 1 |
68
- | `dataset.json`, no `rounds/` | dataset built, never sampled | section 2 (stage 1) |
69
- | `cases.json` only | sampled, batch never started | preflight, then run the batch into this same round |
70
- | `batch/` present, no `batch/batch.json` | the batch was interrupted or killed | the round is **void** - see below, then re-run it |
71
- | `batch/batch.json`, no `findings.md` | the batch finished, nothing graded | section 3 - `collect.sh oom` first |
72
- | `findings.md`, no `changes.md` | graded, nothing applied yet | section 4 |
73
- | `findings.md` and `changes.md` | edits applied, never measured | re-run the same cases into a **new** round: section 5 cold, section 6 warm |
74
- | the last round's heading says `WARM` | stage 2 is in progress | the same rows, read against section 6 |
75
-
76
- Two things the table does not see, so check them by hand:
210
+ `next.mjs` reads the whole `finetune/` tree and prints the next step. Do not
211
+ re-derive the state from an `ls`. Map what it says onto this prompt:
212
+
213
+ | What `next.mjs` says | Go to |
214
+ | ---------------------------------------------------------------- | --------------------------------------------------------------------------------- |
215
+ | `first: patch finetune/README.md ...` | patch the README, then act on the next line |
216
+ | `note: limit N of T cases ...` | unless I asked for a cut: section 2, raise the limit and re-select, then carry on |
217
+ | `build finetune/dataset.json ...` | section 0, then 1 |
218
+ | `write finetune/config.json ...` | section 2 |
219
+ | `choose the cases: select.mjs ...` | section 2 |
220
+ | `write finetune/README.md: the plan ...` | section 2, the README step |
221
+ | `build batch 1: batch.mjs next ...` | section 3 |
222
+ | `... not run yet - zen run batch ...` | run exactly that, then section 4 (no memory) or 7 (with memory) |
223
+ | `... the run never finished ...` | the run is **void** - see below, then re-run |
224
+ | `batchNN-nomem-runN: not graded yet - report.mjs ... oom ...` | section 4 |
225
+ | `batchNN-mem-runN: not graded yet - report.mjs ... oom ...` | section 7, analyze |
226
+ | `... void - out of memory at concurrency C: set ...` | do exactly that - halve it, re-run the cases |
227
+ | `... void - ... the floor ...` | stop and tell me - the only machine stop |
228
+ | `... says PASSED but has no "## Cost review" ...` | section 4, the cost review |
229
+ | `... says PASSED but has no "## Memory review" ...` | section 7, the memory review |
230
+ | `batchNN-nomem-runN: graded, nothing applied ...` | section 5 |
231
+ | `batchNN-mem-runN: graded, nothing applied ...` | section 7, merge and update |
232
+ | `edits applied - confirm them: ... zen run batch ...` | run exactly that, then section 6 (no memory) or 7 (with memory) |
233
+ | `... says PASSED but applied edits ...` | confirm them with another run of the same kind |
234
+ | `batch N has had 4 ... runs - stop tuning it ...` | section 6 or 7, the run cap |
235
+ | `... passed without memory - build the candidate memory: ...` | section 6, the candidate |
236
+ | `batch N passed without memory and the candidate is built - ...` | section 7 |
237
+ | `batch N passed with memory - keep its memory first: ...` | section 7, the checkpoint |
238
+ | `batch N passed (...) - build batch N+1: batch.mjs next ...` | section 3 for the new batch |
239
+ | `every case used, none open ... run the final check ...` | section 8 |
240
+ | `done - ...` | nothing left to tune - report and stop |
241
+
242
+ Read the latest run's `findings.md` for the verdict and its `changes.md` for what
243
+ was already applied. Two things `next.mjs` cannot see, so check them by hand:
77
244
 
78
245
  - **Uncommitted edits under `agents/` with no `changes.md` to explain them.** An
79
246
  apply was interrupted part-way. Read `git status --short agents/` and the diff,
80
247
  finish writing `changes.md` from what is actually on disk, then re-run - do not
81
248
  layer new edits on top of edits you have not recorded.
82
249
  - **`zen check` before you resume anything.** A project left broken mid-edit
83
- fails every item identically and looks like a catastrophic regression.
84
-
85
- A round whose batch never wrote `batch.json` produced no evidence. Move it aside
86
- rather than grading it or deleting it - `mv .finetune/rounds/rN/batch
87
- .finetune/rounds/rN/batch.partial` - and re-run that round from the same
88
- `cases.json`. The same applies to a round `collect.sh oom` calls void.
89
-
90
- Resuming never re-samples and never re-merges. Re-use the first round's
91
- `cases.json` and, in stage 2, the merged graph already sitting in the stage's
92
- round directory - an interruption is not a reason to change the thing every
93
- earlier round was measured against. Re-derive nothing from memory of the last
94
- session either: `findings.md` and `changes.md` are the record, so read them.
95
-
96
- Then tell me, in three lines before you do any work: which stage, which round and
97
- step you are resuming at, and what you are about to run. Continue without waiting
98
- for me up to the next natural checkpoint - the end of a round's report - because
99
- that is where this prompt already stops and asks. If I named a stage or a round,
100
- do that instead of what the state says.
101
-
102
- **If nothing is left to do** - the last round is WARM, it met the stage-2 exit
103
- criteria, and no prose or policy edit has landed since - do not start another
104
- tuning round. Run one confirmation round instead: the same `cases.json`, the same
105
- merged graph, the same flags, the same machine, into
106
- `.finetune/rounds/rN-verify/`. Grade it, write `findings.md` with the heading
107
- `VERIFIED` if no verdict regressed and the saving still holds, and change nothing.
108
- If something did regress, that is a real finding: report it and re-open the stage
109
- it belongs to.
110
-
111
- ## 0. Before the first round
250
+ fails every case identically and looks like a catastrophic regression.
251
+
252
+ A run whose `batch/` never got a `batch.json` produced no evidence. Move it aside
253
+ rather than grading or deleting it - `mv finetune/runs/<run>/batch
254
+ finetune/runs/<run>/batch.partial` - and re-run from the same `cases.json`.
255
+
256
+ A run `report.mjs oom` calls void (any exit 137) is out of memory. Write
257
+ `## VOID` in its `findings.md`, halve `concurrency` in `finetune/config.json` -
258
+ never below 4 - and run a copy of its `cases.json` as the next run. A void run is
259
+ not graded and does not count toward the run cap. Void at concurrency 4 means
260
+ the machine cannot run the batch: stop and tell me.
261
+
262
+ Resuming never re-selects and never rebuilds a batch. `minBatch`, `maxBatch`,
263
+ `recheck` and `seed` in `finetune/config.json` are frozen once batch 1 has run.
264
+ Every later run of a batch - no memory or with memory - re-uses **its first
265
+ run's `cases.json`, copied** - never rebuilt, because the rechecks depend on the
266
+ difficult list, which moves. `findings.md`, `changes.md`, `difficult.json` and
267
+ the README are the record; read them rather than remembering.
268
+
269
+ Then tell me, in three lines before you do any work: which batch and which half
270
+ (no memory or with memory) you are resuming at and the limit as
271
+ `limit N of T cases`, which step of the fifteen, and what you are about to run -
272
+ and then **keep going**. If I named a batch or a run, do that instead of what
273
+ the state says.
274
+
275
+ ### Run to the end
276
+
277
+ Once started, run the whole tuning - every batch, both halves, the final check -
278
+ **without stopping to ask**. Between runs, between halves and between batches,
279
+ report and carry on. `next.mjs` printing `done - ...` is the only normal end.
280
+
281
+ **Never end your turn to wait for a command.** A `zen run batch` outlasts the
282
+ wait of the tool that started it. When a tool says the command is still running,
283
+ read its output again in your next call (`read_bash` with a delay, or your
284
+ terminal's equivalent), and repeat until it has completed.
285
+
286
+ Stop and wait for me only when:
287
+
288
+ - **The machine cannot run the batch** - it ran out of memory at concurrency 4.
289
+ - **I asked you to pause** - after each batch, or anywhere else.
290
+
291
+ Everything that used to be a question is now a note that does not stop the work:
292
+
293
+ - **A structural finding** - the fix needs `agents.yaml`. Do not edit it; write
294
+ the finding under "Open problems" in the README, put the cases it blocks on the
295
+ difficult list, and carry on. It is in the final report.
296
+ - **A with-memory finding that needs other prose.** Note it under "Open
297
+ problems", put its cases on the difficult list, finish the batch; the next
298
+ batch's no-memory runs fix it.
299
+
300
+ When a batch passes, post a short note - batch, size, runs of each kind, cases
301
+ right, llm calls and tokens without memory first run to last and with memory
302
+ against without, re-commits, what changed, what is left - patch the README, and
303
+ build the next batch straight away. Never end your turn with "proposed next
304
+ step" or "ready to proceed upon your confirmation"; take the step.
305
+
306
+ `finetune/USAGE.md` is the token bill - project agents, you, and every
307
+ `zen inspect ask` - by model, batch, run and stage. `next.mjs` rewrites
308
+ it; never edit it, and link it rather than copying its numbers.
309
+
310
+ ## 0. Before the first run
112
311
 
113
312
  Name the training set. If I have not given you one, ask - a specification
114
313
  section, a file of examples, a transcript, a CSV. Fewer than about eight queries
@@ -122,60 +321,236 @@ zen check
122
321
  .github/skills/zen-review/scripts/check-paths.sh
123
322
  ```
124
323
 
125
- A project that does not load fails every item for one reason. Do not start a
126
- round until `zen check` passes.
324
+ A project that does not load fails every case for one reason. Do not start until
325
+ `zen check` passes.
326
+
327
+ ## 1. Dataset - all of it
328
+
329
+ Build `finetune/dataset.json` if it is not there, and read it if it is. **Read
330
+ the whole training set and write every case into it** - no limit, no sampling,
331
+ no "the first twenty". The dataset is the record of what the project is meant to
332
+ handle; the limit is applied later, when cases are selected, and only there.
333
+
334
+ Read the source yourself and write the structure out - do not write a parser.
335
+ Rubrics are copied **verbatim**, one entry per bullet. Classify only the
336
+ distinctions the source already makes, and rate complexity by the trajectory the
337
+ query demands, not by the length of the sentence. Keep ids stable across
338
+ re-extractions. Report the count per class when you are done.
339
+
340
+ ## 2. Settings, selection, README
341
+
342
+ **Settings.** You now know the dataset size. Set the limit, propose the batch
343
+ sizes, recheck and concurrency, say what each costs, and write them to
344
+ `finetune/config.json` - every script reads its defaults from there:
345
+
346
+ ```json
347
+ {
348
+ "datasetSize": 64,
349
+ "limit": 64,
350
+ "minBatch": 3,
351
+ "maxBatch": 10,
352
+ "recheck": 3,
353
+ "concurrency": 13,
354
+ "seed": 1,
355
+ "maxRetries": 3
356
+ }
357
+ ```
358
+
359
+ - **limit** - **equal to `datasetSize`**: every case in the dataset is tuned
360
+ against. Write a smaller number only when I gave you one. Never cut it on your
361
+ own to save cost or time - a limit below the dataset silently drops cases the
362
+ project is meant to handle.
363
+ - **minBatch** - new cases in batch 1, the smoke test. 3.
364
+ - **maxBatch** - the most new cases a batch grows to. Small enough that you will
365
+ actually read every trajectory; 8-12 is the usable range. `batch.mjs` doubles
366
+ from `minBatch` towards it after each smooth batch - you do not size batches
367
+ by hand. **Not the same as concurrency.**
368
+ - **recheck** - earlier cases re-run in each batch. 2-4. Zero means no
369
+ regression guard and no retesting of difficult cases.
370
+ - **concurrency** - the most cases running at once: `min(maxBatch + recheck, 16)`.
371
+ Do not size it any other way. On an out-of-memory run (exit 137) halve it,
372
+ never below 4, and re-run the same cases. The only setting that changes
373
+ mid-way.
374
+ - **maxRetries** - batches a difficult case gets after it is added before it is
375
+ `stuck`. 3.
376
+
377
+ **Selection.** Choose the cases:
378
+
379
+ ```sh
380
+ .github/skills/zen-finetune/scripts/select.mjs -o finetune/selection.json
381
+ ```
127
382
 
128
- ## 1. Dataset
383
+ It takes the limit from the **whole** dataset, evenly by class - one case from
384
+ each class in turn; inside a class, cases with a rubric first, and the most
385
+ complex first - and that order is the order batches take new cases in, so the
386
+ first small batches hold the hard, graded cases. It prints chosen-versus-available
387
+ per class; report that table to me. A class with fewer cases than its share gives
388
+ all it has.
129
389
 
130
- Build `.finetune/dataset.json` if it is not there, and read it if it is. Read the
131
- source yourself and write the structure out - do not write a parser. Rubrics are
132
- copied **verbatim**, one entry per bullet. Classify only the distinctions the
133
- source already makes, and rate complexity by the trajectory the query demands,
134
- not by the length of the sentence. Keep ids stable across re-extractions.
390
+ **README.** Write `finetune/README.md` now, before anything runs - see "The
391
+ README" above.
135
392
 
136
- ## 2. Stage 1: sample, preflight, run cold
393
+ ## 3. Build a batch, run it without memory (step 1)
137
394
 
138
395
  ```sh
139
- .github/skills/zen-finetune/scripts/sample.sh -n 16 --seed 1 -o .finetune/rounds/r1/cases.json
140
- .github/skills/zen-sandbox-capacity/scripts/preflight_sandbox.sh --concurrency 8
141
- zen run batch --input .finetune/rounds/r1/cases.json \
142
- --batch-dir .finetune/rounds/r1/batch --memory .finetune/empty --concurrency 8
396
+ RUN=finetune/runs/batch01-nomem-run1
397
+ C=$(node -p 'require("./finetune/config.json").concurrency')
398
+ .github/skills/zen-finetune/scripts/batch.mjs next -o "$RUN/cases.json"
399
+ zen run batch --input "$RUN/cases.json" \
400
+ --batch-dir "$RUN/batch" --memory finetune/empty --concurrency "$C"
143
401
  ```
144
402
 
145
- Every stage-1 round is cold and every round re-uses the first round's
146
- `cases.json`. A warm graph lets a run succeed by recall instead of by
147
- instruction, and a re-sampled batch means a moved score has two causes and you
148
- cannot tell which.
403
+ `--concurrency` is always the value in `finetune/config.json` - read it fresh
404
+ for every run, since an out-of-memory run halves it.
405
+
406
+ `batch.mjs next` prints the size and why, which cases are new and which are
407
+ rechecks, and writes the same into `plan.json` beside `cases.json` - put that in
408
+ the README's batch row. Then patch the README: the map's `now` node, the row, a
409
+ log line.
410
+
411
+ Batch 1 is a smoke test. If its cases crash - the project, the sandbox, a key -
412
+ that is not the prose: fix it and run the three again before grading anything.
413
+
414
+ Every no-memory run has no memory. A graph with content lets a run succeed by
415
+ recall instead of by instruction. `finetune/empty` is never created - naming a
416
+ directory that does not exist is what gives each case an empty memory.
149
417
 
150
- ## 3. Stage 1: grade
418
+ Never hand-write a `cases.json`, and never re-select mid-way. Build a batch once,
419
+ for its first no-memory run, with `batch.mjs next`; every later run of that
420
+ batch - no memory or with memory - gets a **copy** of that file, which is what
421
+ makes one run comparable to the next.
151
422
 
152
- `collect.sh oom` first - a round with any exit 137 is void, is not graded, and
153
- its tokens are compared to nothing. Then read the graphs before the numbers.
154
- Grade every item against all eight criteria in the skill: rubric compliance,
423
+ ## 4. Grade without memory (steps 2-4)
424
+
425
+ `report.mjs oom` first - a run with any exit 137 is void, is not graded, and its
426
+ tokens are compared to nothing. Then read the answers (`report.mjs answers`) and
427
+ the graphs before the numbers. Grade
428
+ every case against all eight criteria in the skill: rubric compliance,
155
429
  optimality, memory hygiene, fork, delegation, tools and skills, grounding,
156
430
  failure handling. **Cite a node id for every finding.** A finding you cannot
157
431
  point at in the graph did not happen.
158
432
 
159
- Grade the memory each item _wrote_ as seriously as the answer it gave. Read
160
- `<id>/memory/` across the batch: every commit should be an operation, a pointer,
161
- a shape or a rule that is still true next week. A cached live reading poisons
162
- every future run, and committing nothing after learning something durable throws
163
- the round away. Commit policy is fixed here, cold, while the cause is still
164
- attributable - not in stage 2.
165
-
166
- Write `.finetune/rounds/rN/findings.md`: the verdict block first - `PROGRESS`,
167
- `NO CHANGE`, `REGRESSION`, `MIXED` or `VOID`, the `memory:` and `machine:` lines
168
- as printed, and the `compare` table - then the per-sample grading. The heading
169
- says `COLD` or `WARM`.
433
+ Where a node shows **what** went wrong but not **why** - memory not searched,
434
+ lookups not forked, a skill not followed, a tool misused - ask the model at the
435
+ call that made the choice, once per pattern: `zen inspect ask`, worded as
436
+ **zen-inspect-ask** says. Load that skill before the first ask of the run, even
437
+ if you loaded it in an earlier run. A rule written in section 5 without the
438
+ sentence an ask named is a guess.
439
+
440
+ Grade in this order, because they are not equally urgent:
441
+
442
+ 1. **The recheck cases.** A recheck that passed before and fails now means a
443
+ rule written since broke it, and that outranks every other failure in the
444
+ run - nothing else is worth grading until it is resolved.
445
+ 2. **Correctness of the new cases.** Rubric compliance gates everything.
446
+ 3. **The cost of getting there** - as soon as correctness holds, and in the same
447
+ run. llm calls first - read straight off the graph header - then fan-out, then
448
+ discovery the run should not have needed because something authoritative
449
+ could have told it.
450
+
451
+ **The cost review is not optional.** Every `findings.md` of a run where all cases
452
+ are right has a `## Cost review` section - `next.mjs` refuses a `PASSED` without
453
+ one. It contains:
454
+
455
+ - the llm calls per case, the batch's median, and every case above 1.5x the
456
+ median with the reason it cost that much, citing nodes;
457
+ - **repeated work** - the same call with the same arguments twice, a loop, a
458
+ search made as four calls that could have been one: the pattern, or `none`;
459
+ - **missed forks** - independent calls or sub-tasks run one after another: the
460
+ pattern, or `none`;
461
+ - **unneeded discovery** - listing, probing or reading to learn something a
462
+ prompt or skill could simply have stated: the pattern, or `none`;
463
+ - **excess prose** - every paragraph, example, table row, skill and skill
464
+ binding the agents carried this run that no trajectory needed: a rule nothing
465
+ in the run tested, a second wording of a rule stated elsewhere, a rationale
466
+ the model does not act on, a preloaded skill no case used, a skill bound to an
467
+ agent that never needs it. Grep the assembled prefix - prompt, house rules,
468
+ every preloaded and loaded skill - and list each with its file and lines, or
469
+ `none`. Every token of it is paid on every call of every case.
470
+
471
+ `none` needs the evidence for it - the trajectory you checked, not the absence
472
+ of a look. A case averaging 40+ llm calls almost never has `none` on all three.
473
+
474
+ **See what each case committed** (step 3), as seriously as the answer it gave.
475
+ Start from `report.mjs -d "$RUN/batch" memory` for the counts, then
476
+ `report.mjs -d "$RUN/batch" commits` for every node each case committed: every
477
+ commit should be an operation, a pointer, a shape or a
478
+ rule that is still true next week. A cached live reading poisons every future
479
+ run, and committing nothing after learning something durable throws the run
480
+ away. The candidate memory is built from exactly these commits, so commit
481
+ policy is fixed here, without memory, while the cause is still attributable.
482
+
483
+ **End every case with its proposals** (step 4): what that case alone says should
484
+ change - criterion, file, the sentence its `why` line named, the edit. They are
485
+ not edits yet; section 5 merges them.
486
+
487
+ Write `$RUN/findings.md`: the verdict heading first - `PROGRESS`, `NO CHANGE`,
488
+ `REGRESSION`, `MIXED`, `CORRECT`, `PASSED` or `VOID` - the `memory:` and
489
+ `machine:` lines as printed, and the `report.mjs compare` table with the recheck
490
+ cases marked; then the cost review; then the per-case grading, each case ending
491
+ in its proposals. The title names the run and says `NO MEMORY` or
492
+ `WITH MEMORY`. Then patch the README.
493
+
494
+ `CORRECT` means every case is right but the cost review found something worth
495
+ changing - apply it and run again. `PASSED` means every case is right **and** the
496
+ cost review found nothing worth changing, so this run applies no edits. It is
497
+ load-bearing: `next.mjs` reads it to move on, and refuses it on a run with a
498
+ `changes.md` or without a cost review. Never write it on a run that merely
499
+ improved, and never on a run you are about to edit after.
500
+
501
+ **Then update the difficult list** - mechanically, every graded run:
170
502
 
171
- ## 4. Stage 1: generalise, then apply
503
+ ```sh
504
+ D=.github/skills/zen-finetune/scripts/difficult.mjs
505
+ $D add <id> --why wrong --run <run> --note "<what goes wrong>" # wrong after a run that tried to fix it
506
+ $D add <id> --why regressed --run <run> # a recheck case that failed
507
+ $D add <id> --why flaky --run <run> # right before, wrong now, no rule aimed at it
508
+ $D add <id> --why memory --run <run> --note "<what goes wrong>" # with memory: wrong, no cheaper, or re-commits, after a fix
509
+ $D add <id> --why costly --run <run> # over 2x the median llm calls in a passing run
510
+ $D fix <id> --run <run> # a difficult case right, in a PASSED run
511
+ $D # the list
512
+ ```
172
513
 
173
- Collect every finding into one table before writing a word of instruction. The
174
- unit of change is a pattern across findings, not a finding: three items missing
175
- the same beat is a rule, one item is a note in `findings.md`. Write the rule one
176
- level up from the sample that produced it - a rule that names the dataset's own
177
- endpoints makes the eval pass and the product fail. Prefer mechanical wording,
178
- and remember that a prohibition with a self-judged exemption is a permission.
514
+ A case wrong on run 1 is not difficult yet - that is what the batch is for. It
515
+ is difficult when it is **still** wrong after a run that tried to fix it.
516
+
517
+ ## 5. Merge the proposals, then apply (steps 5-6)
518
+
519
+ If a recheck case failed, stop here and revert or narrow the rule that broke it.
520
+ Find it in the earlier batches' `changes.md`, undo or qualify it, record the
521
+ over-reach, and go straight to the next run. Adding a rule on top of a rule that
522
+ over-reached is how a tuning ends with prose nobody can unpick.
523
+
524
+ Otherwise: collect every case's proposals into one table before writing a word
525
+ of instruction - wrong answers and cost findings alike - and write it into
526
+ `findings.md` under `## Proposals, merged`, one row per pattern with the cases
527
+ and nodes behind it. Two proposals saying the same thing in different words are
528
+ one pattern; two proposing opposite edits to one sentence are a conflict, and
529
+ the conflict is the finding. A cost pattern is a rule
530
+ just as a correctness pattern is: "these three cases each listed the directory
531
+ to find a file the skill could name" becomes a line in that skill; "these ran
532
+ four independent lookups in sequence" becomes a fan-out rule in the prompt. The
533
+ unit of change is a pattern across proposals, not a proposal: three cases missing
534
+ the same beat is a rule, one case is a note in `findings.md`. The three do not
535
+ have to be in this batch - grep the earlier runs' findings before calling
536
+ something an anecdote. Each cost or behaviour pattern carries the sentence its
537
+ `zen inspect ask` named - verified by grep in the recorded request - or
538
+ `no instruction`: that sentence is what the rule edits. For a difficult case, read its notes (`difficult.mjs`)
539
+ and every earlier `findings.md` that names it first: the rules already tried and
540
+ failed are the most useful thing you know about it. Write the rule one level up
541
+ from the case that produced it; a rule that names the dataset's own endpoints
542
+ makes the eval pass and the product fail. Prefer mechanical wording, and
543
+ remember that a prohibition with a self-judged exemption is a permission.
544
+
545
+ **Cut as well as add.** Every apply also removes what the excess-prose review
546
+ found: delete duplicated rules (keep one copy, in the place the routing below
547
+ names), collapse paragraphs to the sentence the model acts on, drop examples
548
+ and rationale no trajectory used, and delete skill sections no case needed. A
549
+ skill binding or preload no case needed lives in `agents.yaml` - record it as
550
+ an open problem, do not edit it. A rule written in an earlier run that no case has tested since is a
551
+ candidate too. An apply that only adds is suspect: say in `changes.md` what was
552
+ cut and how many lines each file lost or gained. Cuts are confirmed like any
553
+ edit - a cut that makes a case wrong is restored, nothing else.
179
554
 
180
555
  Route each rule by what kind of thing it is: one agent's behaviour to its prompt,
181
556
  a fact or a procedure to a skill - update an existing one or create a new one, a
@@ -185,67 +560,185 @@ standing rule to `agents/instructions.md`, a rule about one capability to a new
185
560
  You may edit prompts, skills, house rules and new policy files. You may not edit
186
561
  `agents/memory-instructions.md`, `agents/fork-instructions.md`,
187
562
  `agents/tools-instructions.md` or `agents/files-instructions.md` - `zen check
188
- --fix` overwrites them. You may not change a rubric to make a run pass. Tell me
189
- before you change `agents.yaml`; that finding is structural.
190
-
191
- Record every edit and the finding numbers behind it in
192
- `.finetune/rounds/rN/changes.md`, then run `zen check` again.
193
-
194
- ## 5. Stage 1: loop, and leave the stage
195
-
196
- Re-run the same cases into a new round directory and compare per sample and on
197
- the aggregate. The verdict column decides, not the token column. A sample that
198
- passed and now fails is the most valuable result in the round and is almost
199
- always the new instruction. If the instruction files grew by a third and the
200
- score moved by one sample, revert and say so.
201
-
202
- Report to me after each round and wait: the verdict block, what you changed, and
203
- what you propose to change next. Stop when a round produces no generalisable
204
- pattern - that is the remaining failure being the model's rather than the prose's.
205
-
206
- Stage 1 ends when, on one cold round, **every sample holds** and **what the runs
207
- wrote to memory is worth keeping**. Tell me both before proposing stage 2.
208
-
209
- ## 6. Stage 2: memory
210
-
211
- Freeze the prompts, the skills and the house rules. Merge the last good cold
212
- round's memory into one graph, read `zen memory stats`, and run the same cases
213
- against it with the same flags the cold round used - `--memory <merged graph>`
214
- and nothing else. Each item copies that graph, recalls from the copy and writes
215
- back into the copy, so every item starts from the same memory and the source is
216
- left untouched. Do not use `--memory-read-only`: it removes the commit tool and
217
- changes the memory prose, which would make the warm round differ from its cold
218
- baseline in two ways instead of one.
219
-
220
- Compare against the cold round the memory came from.
221
-
222
- The target is reuse: recall should replace the discovery the cold runs repeated
223
- every time, so the warm batch is dramatically cheaper in tokens, calls and wall
224
- clock with no verdict changed. A warm round that costs about the same, or more,
225
- means memory was carried and not used - that is a recall-timing finding, not a
226
- reason for more memory. A warm round that is much cheaper still needs one
227
- trajectory opened to confirm the saving is recall replacing _discovery_ and not
228
- recall replacing a _live reading_.
229
-
230
- Grade what the warm items committed as well as what they recalled - this is the
231
- one place it can be seen. A node that duplicates something the same run just
232
- recalled is waste, and a correction committed without `SUPERSEDES` beside the
233
- stale node it contradicts is worse than waste.
234
-
235
- In stage 2 the only file you edit is `agents/memory-policy-instructions.md`. Keep
236
- the merged graph byte-identical for the whole stage: do not re-merge between
237
- rounds, and never merge a warm round's own copies back into it - either changes
238
- the memory and the prose at once. If a finding cannot be written as a recall or
239
- re-commit rule, it is a stage-1 finding that escaped - tell me, and we re-open
240
- stage 1.
241
-
242
- Stage 2 ends when no verdict regressed, the saving is obvious next to the noise
243
- floor, every large saving is explained, and nothing was committed that memory
244
- already held.
245
-
246
- Commit `dataset.json`, `cases.json`, `findings.md` and `changes.md` - they are
247
- the eval history, and they are what a resumed session reads to find out where it
248
- got to. Write each of them as its step ends rather than at the end of the round,
249
- so an interruption never costs more than the step it lands in. Add
250
- `.finetune/rounds/*/batch/` and `.finetune/empty/` to `.gitignore` - the batch
251
- directories are large and may hold live API responses.
563
+ --fix` overwrites them. You may not change a rubric to make a run pass. Do not
564
+ change `agents.yaml`: a finding that needs it is structural - record it as an
565
+ open problem and carry on.
566
+
567
+ Record every edit and the merged pattern behind it in `$RUN/changes.md`, then
568
+ run `zen check` and patch the README.
569
+
570
+ ## 6. Confirm without memory, build the candidate (steps 7-9)
571
+
572
+ An edit is a guess until the same cases have run against it. Run the same batch
573
+ again as the next run - a copy of its cases, new prose, still no memory:
574
+
575
+ ```sh
576
+ PREV=finetune/runs/batch01-nomem-run1
577
+ RUN=finetune/runs/batch01-nomem-run2
578
+ mkdir -p "$RUN" && cp finetune/runs/batch01-nomem-run1/cases.json "$RUN/cases.json"
579
+ C=$(node -p 'require("./finetune/config.json").concurrency')
580
+ zen run batch --input "$RUN/cases.json" \
581
+ --batch-dir "$RUN/batch" --memory finetune/empty --concurrency "$C"
582
+ .github/skills/zen-finetune/scripts/report.mjs -d "$RUN/batch" -p "$PREV/batch" compare
583
+ ```
584
+
585
+ Same queries, same machine, same memory, exactly one thing changed. The verdict
586
+ column decides first: a cost edit that makes any case wrong is reverted, whatever
587
+ it saved. Then the cost columns: a cost edit that did not cut llm calls or tokens
588
+ in the cases it was aimed at by more than the noise floor is reverted too - prose
589
+ that does not pay is a cost on every future run. Read the recheck cases first,
590
+ then the cases the rule was written for, then the ones it was not about. Cost
591
+ growth concentrated in the cases the rule touches is its price; growth spread
592
+ evenly across all of them is prose leaking into every agent, and that is a
593
+ regression even when every case still passes. If the instruction files grew by a
594
+ third and one case moved, revert and say so.
595
+
596
+ Then grade this run exactly as in section 4 - cost review, commits, proposals
597
+ and difficult list included. If every case is right and the cost review finds
598
+ nothing more worth changing, write `PASSED`, then **build the candidate memory**
599
+ from it before anything else (step 9):
600
+
601
+ ```sh
602
+ M=.github/skills/zen-finetune/scripts/memory.mjs
603
+ $M candidate batch01-nomem-run2
604
+ zen memory stats --dir "$($M path --candidate)"
605
+ ```
606
+
607
+ It copies the last good memory and merges in what each case of this run
608
+ committed - what the graph already held is folded, not added. A candidate of 0
609
+ nodes means nothing was committed: a commit-policy finding, and the with-memory
610
+ runs will measure nothing. Then patch the README and go straight to section 7.
611
+ If the review finds more, it is `CORRECT`: back to section 5.
612
+
613
+ The no-memory runs get **at most 4** per batch. After the 4th, stop tuning:
614
+ revert whatever it did not confirm, add every case still wrong to the difficult
615
+ list, and write `PASSED` on it. If a case is still wrong, the map shows the
616
+ batch `failed`, not `done` - the case comes back first in the next batches.
617
+
618
+ **Never compare two different batches.** They hold different cases, so the
619
+ difference between them is the cases and not the prose. Compare run to run
620
+ within one batch and kind, or a with-memory run against the no-memory run that
621
+ passed the same batch.
622
+
623
+ ## 7. With memory (steps 10-15)
624
+
625
+ Run the **same cases** from the candidate - every case gets **its own copy** of
626
+ it, recalls from the copy and commits into the copy, so nothing one case writes
627
+ reaches another case or the candidate:
628
+
629
+ ```sh
630
+ M=.github/skills/zen-finetune/scripts/memory.mjs
631
+ RUN=finetune/runs/batch01-mem-run1
632
+ mkdir -p "$RUN" && cp finetune/runs/batch01-nomem-run1/cases.json "$RUN/cases.json"
633
+ C=$(node -p 'require("./finetune/config.json").concurrency')
634
+ zen run batch --input "$RUN/cases.json" \
635
+ --batch-dir "$RUN/batch" --memory "$($M path --candidate)" --concurrency "$C"
636
+ ```
637
+
638
+ Do not use `--memory-read-only`: it removes the commit tool and changes the
639
+ memory prose, which makes a with-memory run differ from its no-memory baseline
640
+ in two ways instead of one.
641
+
642
+ **Analyze the memory use** (step 11). `report.mjs oom` first, then
643
+ `report.mjs memory` - recalls, memory reads, commits and `known` per case - then
644
+ `report.mjs recalls` - what each recall put in front of the model and every
645
+ memory tool step - and `report.mjs commits` - each case against the candidate:
646
+ what it committed, which of those the candidate already held, and which nodes
647
+ it loaded - then
648
+ `compare` against **the no-memory run that passed the same batch**, and from
649
+ run 2 on also against the previous with-memory run. Read the trajectories, and
650
+ `zen inspect ask` at the critical points - the first research call that ignored
651
+ a recall, the call right after a recall that redid the work, the call that
652
+ committed what it had just recalled - once per pattern, worded as
653
+ **zen-inspect-ask** says.
654
+
655
+ The target is reuse: recall should replace the discovery the no-memory runs
656
+ repeated every time, so the run is dramatically cheaper in tokens, calls and
657
+ wall clock with no verdict changed. A run that costs about the same, or more,
658
+ means memory was carried and not used - a recall-timing finding, not a reason
659
+ for more memory. A run that is much cheaper still needs one trajectory opened
660
+ to confirm the saving is recall replacing _discovery_ and not recall replacing a
661
+ _live reading_. The recheck cases, recalling what earlier batches committed,
662
+ are where that shows up first.
663
+
664
+ **Do not commit what memory already holds.** Every commit of a fact the graph
665
+ had - `known` above 0 in `report.mjs memory`, `ALREADY HELD` in
666
+ `report.mjs commits`, or a near-copy of a node it just
667
+ recalled - is an llm turn and a tool call that bought nothing, in every future
668
+ run. The target is `known` 0, and commits only of what the run genuinely
669
+ learned. A correction committed without `SUPERSEDES` beside the stale node it
670
+ contradicts is worse than a duplicate.
671
+
672
+ Write `findings.md` titled `WITH MEMORY`, with a `## Memory review` straight
673
+ after the verdict block - `next.mjs` refuses a `PASSED` without one: the saving
674
+ against the no-memory run, per case and in total; whether recall replaced
675
+ discovery, citing nodes; any live reading replaced by a recall; re-commits; and
676
+ corrections - each the pattern or `none` with the evidence. Then the per-case
677
+ grading, each case ending in its proposals.
678
+
679
+ **Merge and update** (steps 12-13) exactly as in section 5, into
680
+ `agents/memory-policy-instructions.md` - or the memory paragraph of a prompt or
681
+ skill where a `why` line names one: when to recall, when to trust a recalled
682
+ fact, when to go and look anyway, when **not** to commit because memory already
683
+ holds it. A pattern that cannot be written as one of those is a no-memory
684
+ finding that escaped: note it under "Open problems", put its cases on the
685
+ difficult list, do not edit the prose - the next batch's no-memory runs fix it.
686
+ A live reading sitting in the candidate is a commit-policy rule, and its nodes
687
+ may be removed with `zen memory forget --dir "$($M path --candidate)" <id>...`,
688
+ recorded in `changes.md`. Never edit the candidate otherwise, and never merge a
689
+ with-memory run's copies back into it.
690
+
691
+ **Run again** (step 14) from the **same candidate** - `batch01-mem-run2`, a copy
692
+ of the same cases. At most 4 with-memory runs. A case memory makes wrong or no
693
+ cheaper, still, after a run that tried to fix it, goes on the difficult list
694
+ `--why memory`.
695
+
696
+ **Keep it and move on** (step 15). When no verdict regressed, the saving is
697
+ obvious next to the noise floor, every large saving is explained, and nothing
698
+ was committed that memory already held, write `PASSED` and keep the candidate
699
+ before anything else:
700
+
701
+ ```sh
702
+ .github/skills/zen-finetune/scripts/memory.mjs checkpoint batch01-mem-run1
703
+ ```
704
+
705
+ Then mark difficult cases right in both passing runs `fixed`, patch the README
706
+ (the batch turns `done` on the map, the next one `now`), and build the next
707
+ batch as `next.mjs` says - section 3 - straight away. It grows on its own if
708
+ this one went smoothly.
709
+
710
+ ## 8. The final check
711
+
712
+ When every case in the selection has been used and no difficult case is open -
713
+ `next.mjs` says so - run the whole selection as one run - the last good memory,
714
+ the same flags, the same machine - into `finetune/runs/final-check/`. It is the
715
+ only run that measures every case under the final prose and the final memory at
716
+ once. Grade it, and
717
+ write `findings.md` with the heading `VERIFIED` if no verdict regressed and the
718
+ saving holds. Change nothing. If something did regress, write it up as a finding
719
+ and under "Open problems" - which batch's rules are the suspect and why. Patch the README
720
+ to its final state - every node `done` or `failed`, "Results so far" as the
721
+ summary of the whole tuning, and every `stuck` difficult case and open problem
722
+ listed with what was tried.
723
+
724
+ Then, and only then, report to me: the final summary, the open problems, and a
725
+ link to the README. That is the end of the tuning.
726
+
727
+ ## If the tuning stops for good
728
+
729
+ Whenever it stops - finished, abandoned, or killed mid-run - the prose under
730
+ `agents/` is whatever the last confirmed run left, and the memory worth keeping
731
+ is `memory.mjs path`: every passed batch's candidate, whole on disk, never
732
+ the graph a half-finished merge was writing. If the project will ship with a
733
+ memory, that is the one to promote into `memory/` - read `zen-memory-warmup`
734
+ first.
735
+
736
+ ## Housekeeping
737
+
738
+ Commit `dataset.json`, `config.json`, `selection.json`, `difficult.json`,
739
+ `README.md`, `USAGE.md`, `usage.json`, every `cases.json`, `plan.json`, `findings.md` and `changes.md` - they are the
740
+ eval history, and they are what a resumed session reads to find its place. Write
741
+ each as its step ends rather than at the end of the batch, so an interruption
742
+ never costs more than the step it lands in. Add `finetune/runs/*/batch/`,
743
+ `finetune/memory/`, `finetune/empty/` and `finetune/usage/` to `.gitignore` - `batch/` folders are
744
+ large and may hold live API responses, and the memory graphs are built from them.