@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.
- package/README.md +65 -0
- package/dist/ask.js +28 -5
- package/dist/banner.js +2 -2
- package/dist/batch.d.ts +6 -0
- package/dist/batch.js +76 -1
- package/dist/catalog.d.ts +31 -1
- package/dist/catalog.js +140 -7
- package/dist/commands/index.js +2 -0
- package/dist/commands/inspect.d.ts +52 -2
- package/dist/commands/inspect.js +220 -31
- package/dist/commands/memory.js +156 -32
- package/dist/commands/meta.js +333 -24
- package/dist/commands/models.js +97 -12
- package/dist/commands/open.js +4 -4
- package/dist/commands/run.js +38 -4
- package/dist/commands/sandbox.js +148 -16
- package/dist/commands/view.d.ts +8 -0
- package/dist/commands/view.js +114 -0
- package/dist/engine.d.ts +8 -1
- package/dist/engine.js +7 -0
- package/dist/eventlog.d.ts +64 -0
- package/dist/eventlog.js +170 -0
- package/dist/liveness.d.ts +7 -0
- package/dist/liveness.js +35 -3
- package/dist/main.js +0 -0
- package/dist/meta/editor.d.ts +9 -0
- package/dist/meta/editor.js +43 -0
- package/dist/meta/finetune/analyze-brief.d.ts +26 -0
- package/dist/meta/finetune/analyze-brief.js +131 -0
- package/dist/meta/finetune/apply-brief.d.ts +23 -0
- package/dist/meta/finetune/apply-brief.js +247 -0
- package/dist/meta/finetune/brief.d.ts +45 -0
- package/dist/meta/finetune/brief.js +158 -0
- package/dist/meta/finetune/children.d.ts +8 -0
- package/dist/meta/finetune/children.js +76 -0
- package/dist/meta/finetune/command.d.ts +30 -0
- package/dist/meta/finetune/command.js +516 -0
- package/dist/meta/finetune/cost.d.ts +46 -0
- package/dist/meta/finetune/cost.js +105 -0
- package/dist/meta/finetune/dataset/anchors.d.ts +41 -0
- package/dist/meta/finetune/dataset/anchors.js +239 -0
- package/dist/meta/finetune/dataset/changes.d.ts +36 -0
- package/dist/meta/finetune/dataset/changes.js +319 -0
- package/dist/meta/finetune/dataset/command.d.ts +17 -0
- package/dist/meta/finetune/dataset/command.js +1011 -0
- package/dist/meta/finetune/dataset/drift.d.ts +33 -0
- package/dist/meta/finetune/dataset/drift.js +71 -0
- package/dist/meta/finetune/dataset/export.d.ts +26 -0
- package/dist/meta/finetune/dataset/export.js +51 -0
- package/dist/meta/finetune/dataset/index.d.ts +2 -0
- package/dist/meta/finetune/dataset/index.js +2 -0
- package/dist/meta/finetune/dataset/sample.d.ts +50 -0
- package/dist/meta/finetune/dataset/sample.js +197 -0
- package/dist/meta/finetune/dataset/schema.d.ts +33 -0
- package/dist/meta/finetune/dataset/schema.js +216 -0
- package/dist/meta/finetune/dataset/store.d.ts +35 -0
- package/dist/meta/finetune/dataset/store.js +177 -0
- package/dist/meta/finetune/dataset/types.d.ts +127 -0
- package/dist/meta/finetune/dataset/types.js +39 -0
- package/dist/meta/finetune/failure.d.ts +15 -0
- package/dist/meta/finetune/failure.js +26 -0
- package/dist/meta/finetune/feedback.d.ts +57 -0
- package/dist/meta/finetune/feedback.js +104 -0
- package/dist/meta/finetune/guard.d.ts +29 -0
- package/dist/meta/finetune/guard.js +104 -0
- package/dist/meta/finetune/improvements.d.ts +98 -0
- package/dist/meta/finetune/improvements.js +501 -0
- package/dist/meta/finetune/index.d.ts +2 -0
- package/dist/meta/finetune/index.js +2 -0
- package/dist/meta/finetune/journal.d.ts +35 -0
- package/dist/meta/finetune/journal.js +135 -0
- package/dist/meta/finetune/live.d.ts +84 -0
- package/dist/meta/finetune/live.js +272 -0
- package/dist/meta/finetune/loop.d.ts +3 -0
- package/dist/meta/finetune/loop.js +163 -0
- package/dist/meta/finetune/memories.d.ts +47 -0
- package/dist/meta/finetune/memories.js +154 -0
- package/dist/meta/finetune/recheck.d.ts +58 -0
- package/dist/meta/finetune/recheck.js +311 -0
- package/dist/meta/finetune/report.d.ts +40 -0
- package/dist/meta/finetune/report.js +1397 -0
- package/dist/meta/finetune/retry.d.ts +24 -0
- package/dist/meta/finetune/retry.js +96 -0
- package/dist/meta/finetune/sampler.d.ts +18 -0
- package/dist/meta/finetune/sampler.js +47 -0
- package/dist/meta/finetune/system.d.ts +27 -0
- package/dist/meta/finetune/system.js +132 -0
- package/dist/meta/finetune/train.d.ts +43 -0
- package/dist/meta/finetune/train.js +425 -0
- package/dist/meta/finetune/tui.d.ts +16 -0
- package/dist/meta/finetune/tui.js +316 -0
- package/dist/meta/finetune/tuning.d.ts +193 -0
- package/dist/meta/finetune/tuning.js +308 -0
- package/dist/meta/finetune/usage.d.ts +66 -0
- package/dist/meta/finetune/usage.js +260 -0
- package/dist/meta/finetune/view.d.ts +9 -0
- package/dist/meta/finetune/view.js +27 -0
- package/dist/meta/host.d.ts +15 -0
- package/dist/meta/host.js +21 -0
- package/dist/meta/index.d.ts +11 -0
- package/dist/meta/index.js +11 -0
- package/dist/meta/launch.d.ts +81 -0
- package/dist/meta/launch.js +286 -0
- package/dist/meta/model.d.ts +44 -0
- package/dist/meta/model.js +119 -0
- package/dist/meta/prompts.d.ts +18 -0
- package/dist/meta/prompts.js +61 -0
- package/dist/meta/relay.d.ts +20 -0
- package/dist/meta/relay.js +123 -0
- package/dist/meta/tokens.d.ts +52 -0
- package/dist/meta/tokens.js +226 -0
- package/dist/meta/window.d.ts +61 -0
- package/dist/meta/window.js +198 -0
- package/dist/meta/wire.d.ts +42 -0
- package/dist/meta/wire.js +129 -0
- package/dist/meta.d.ts +71 -1
- package/dist/meta.js +303 -6
- package/dist/modelref.d.ts +16 -0
- package/dist/modelref.js +41 -0
- package/dist/narrate.js +5 -2
- package/dist/podman.d.ts +26 -0
- package/dist/podman.js +83 -3
- package/dist/projects.d.ts +0 -5
- package/dist/projects.js +2 -15
- package/dist/resources.d.ts +215 -0
- package/dist/resources.js +856 -0
- package/dist/sandbox.d.ts +2 -0
- package/dist/sandbox.js +10 -3
- package/dist/scaffold.d.ts +15 -7
- package/dist/scaffold.js +24 -67
- package/dist/session.js +4 -29
- package/dist/term.d.ts +2 -0
- package/dist/term.js +11 -2
- package/dist/trace.d.ts +5 -0
- package/dist/trace.js +107 -7
- package/dist/tui/app.js +19 -5
- package/dist/tui/links.d.ts +27 -0
- package/dist/tui/links.js +142 -0
- package/dist/tui/models.d.ts +35 -0
- package/dist/tui/models.js +447 -0
- package/dist/tui/sandbox.d.ts +10 -0
- package/dist/tui/sandbox.js +131 -0
- package/dist/tui/wrap.d.ts +5 -0
- package/dist/tui/wrap.js +12 -0
- package/dist/usage.d.ts +78 -0
- package/dist/usage.js +51 -0
- package/package.json +4 -2
- package/templates/editor/.github/copilot-instructions.md +60 -145
- package/templates/editor/.github/prompts/analyze.prompt.md +33 -0
- package/templates/editor/.github/prompts/dataset.prompt.md +108 -0
- package/templates/editor/.github/prompts/finetune-apply.prompt.md +270 -0
- package/templates/editor/.github/prompts/finetune.prompt.md +691 -198
- package/templates/editor/.github/prompts/new-agent.prompt.md +3 -1
- package/templates/editor/.github/prompts/new-skill.prompt.md +2 -0
- package/templates/editor/.github/prompts/project-review.prompt.md +5 -4
- package/templates/editor/.github/skills/zen-analyze-run/SKILL.md +588 -0
- package/templates/editor/.github/skills/zen-cli/SKILL.md +9 -1
- package/templates/editor/.github/skills/zen-cli/references/dataset.md +34 -0
- package/templates/editor/.github/skills/zen-cli/references/finetune.md +137 -0
- package/templates/editor/.github/skills/zen-cli/references/inspect.md +63 -11
- package/templates/editor/.github/skills/zen-cli/references/memory.md +33 -10
- package/templates/editor/.github/skills/zen-cli/references/meta.md +61 -0
- package/templates/editor/.github/skills/zen-cli/references/models.md +5 -1
- package/templates/editor/.github/skills/zen-cli/references/projects.md +4 -6
- package/templates/editor/.github/skills/zen-cli/references/rag.md +24 -6
- package/templates/editor/.github/skills/zen-cli/references/sandbox.md +35 -14
- package/templates/editor/.github/skills/zen-code-python/SKILL.md +123 -0
- package/templates/editor/.github/skills/zen-code-python/references/code_python.md +48 -0
- package/templates/editor/.github/skills/zen-dataset/SKILL.md +253 -0
- package/templates/editor/.github/skills/zen-finetune/SKILL.md +1492 -660
- package/templates/editor/.github/skills/zen-finetune/references/readme-template.md +268 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/batch.mjs +390 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/difficult.mjs +243 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/memory.mjs +280 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/next.mjs +398 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/report.mjs +797 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/select.mjs +354 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/usage.mjs +722 -0
- package/templates/editor/.github/skills/zen-inspect/SKILL.md +74 -26
- package/templates/editor/.github/skills/zen-inspect-ask/SKILL.md +271 -0
- package/templates/editor/.github/skills/zen-instructions/SKILL.md +4 -12
- package/templates/editor/.github/skills/zen-memory/SKILL.md +82 -53
- package/templates/editor/.github/skills/zen-rag-docs/SKILL.md +20 -8
- package/templates/editor/.github/skills/zen-review/SKILL.md +8 -31
- package/templates/editor/.github/skills/zen-review/scripts/check-paths.sh +5 -5
- package/templates/editor/.github/skills/zen-review/scripts/review.sh +3 -12
- package/templates/editor/.github/skills/zen-sandbox/SKILL.md +9 -4
- package/templates/editor/.github/skills/zen-sandbox-capacity/SKILL.md +2 -2
- package/templates/editor/.github/skills/zen-spec-sync/SKILL.md +2 -4
- package/templates/editor/.github/skills/zen-topology/SKILL.md +283 -0
- package/templates/editor/.vscode/settings.json +1 -0
- package/templates/project/agents/files-instructions.md +29 -20
- package/templates/project/agents/fork-instructions.md +78 -28
- package/templates/project/agents/memory-instructions.md +222 -494
- package/templates/project/agents/tools-instructions.md +14 -4
- package/templates/project/gitignore +2 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/collect.sh +0 -432
- package/templates/editor/.github/skills/zen-finetune/scripts/sample.sh +0 -199
- package/templates/editor/.github/skills/zen-memory/scripts/check-instructions.sh +0 -116
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @ts-check
|
|
3
|
+
//
|
|
4
|
+
// Say what to do next in the fine-tuning, read off finetune/.
|
|
5
|
+
//
|
|
6
|
+
// This is how an interrupted session finds its place. Everything it reads is on
|
|
7
|
+
// disk - the dataset, the config, the selection, the README, the difficult list,
|
|
8
|
+
// the memory pointers, every run directory and the verdict heading of every
|
|
9
|
+
// findings.md - so it never guesses.
|
|
10
|
+
//
|
|
11
|
+
// Every batch goes the same way:
|
|
12
|
+
//
|
|
13
|
+
// no-memory runs run, grade, fix the prose, run again - until one PASSES
|
|
14
|
+
// memory.mjs candidate last good memory + what that run's cases committed
|
|
15
|
+
// with-memory runs every case from its own copy of the candidate; grade the
|
|
16
|
+
// memory use, fix the memory policy, run again - until one PASSES
|
|
17
|
+
// memory.mjs checkpoint the candidate becomes the last good memory
|
|
18
|
+
// the next batch
|
|
19
|
+
//
|
|
20
|
+
// A PASSED is only accepted from a run whose findings.md has the review its kind
|
|
21
|
+
// needs - `## Cost review` without memory, `## Memory review` with it - and which
|
|
22
|
+
// applied no edits of its own (no changes.md): edits are a guess until the next
|
|
23
|
+
// run confirms them. Each kind gets at most MAX_RUNS runs per batch.
|
|
24
|
+
//
|
|
25
|
+
// The number of batches is not fixed: the tuning goes on while the selection has
|
|
26
|
+
// unused cases or the difficult list has open ones.
|
|
27
|
+
//
|
|
28
|
+
// When finetune/README.md is older than the newest thing a run wrote, it says
|
|
29
|
+
// so first: the README is written by hand and is only useful if it is current.
|
|
30
|
+
//
|
|
31
|
+
// Run directories are `batch<NN>-<nomem|mem>-run<N>`; anything else is ignored.
|
|
32
|
+
// Run it from anywhere; it finds the project root from its own location.
|
|
33
|
+
|
|
34
|
+
import { spawnSync } from 'node:child_process';
|
|
35
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
36
|
+
import { dirname, join, resolve } from 'node:path';
|
|
37
|
+
import { fileURLToPath } from 'node:url';
|
|
38
|
+
|
|
39
|
+
const NAME = 'next.mjs';
|
|
40
|
+
|
|
41
|
+
process.chdir(resolve(dirname(fileURLToPath(import.meta.url)), '../../../..'));
|
|
42
|
+
|
|
43
|
+
for (const arg of process.argv.slice(2)) {
|
|
44
|
+
if (arg === '-h' || arg === '--help') {
|
|
45
|
+
console.log(`Say what to do next in the fine-tuning, read off finetune/.\n\n ${NAME}`);
|
|
46
|
+
process.exit(0);
|
|
47
|
+
}
|
|
48
|
+
console.error(`${NAME}: unknown argument ${arg}`);
|
|
49
|
+
process.exit(2);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const DATASET = 'finetune/dataset.json';
|
|
53
|
+
const CONFIG = 'finetune/config.json';
|
|
54
|
+
const SELECTION = 'finetune/selection.json';
|
|
55
|
+
const README = 'finetune/README.md';
|
|
56
|
+
const DIFFICULT = 'finetune/difficult.json';
|
|
57
|
+
const LAST_GOOD = 'finetune/memory/last-good.json';
|
|
58
|
+
const CANDIDATE = 'finetune/memory/candidate.json';
|
|
59
|
+
const RUNS = 'finetune/runs';
|
|
60
|
+
const FINAL = `${RUNS}/final-check`;
|
|
61
|
+
const SCRIPTS = '.github/skills/zen-finetune/scripts';
|
|
62
|
+
const MAX_RUNS = 4;
|
|
63
|
+
const RUN_NAME = /^batch(\d{2})-(nomem|mem)-run(\d+)$/;
|
|
64
|
+
|
|
65
|
+
/** @param {string} path @returns {any} */
|
|
66
|
+
function json(path) {
|
|
67
|
+
try {
|
|
68
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
69
|
+
} catch {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** @param {string} path @returns {number} */
|
|
75
|
+
const mtime = (path) => (existsSync(path) ? statSync(path).mtimeMs : 0);
|
|
76
|
+
|
|
77
|
+
const datasetDoc = json(DATASET);
|
|
78
|
+
const datasetSize = (datasetDoc?.cases ?? datasetDoc?.samples ?? []).length;
|
|
79
|
+
const config = json(CONFIG);
|
|
80
|
+
/** @type {string[]} */
|
|
81
|
+
const selection = (json(SELECTION)?.batch ?? []).map((/** @type {{ id: string }} */ c) => c.id);
|
|
82
|
+
const maxRetries = Number(config?.maxRetries) || 3;
|
|
83
|
+
const concurrency = Number(config?.concurrency) || 16;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The verdict heading of a run's findings.md, or '' if it has none.
|
|
87
|
+
*
|
|
88
|
+
* @param {string} dir
|
|
89
|
+
* @returns {string}
|
|
90
|
+
*/
|
|
91
|
+
function verdict(dir) {
|
|
92
|
+
const path = join(dir, 'findings.md');
|
|
93
|
+
if (!existsSync(path)) {
|
|
94
|
+
return '';
|
|
95
|
+
}
|
|
96
|
+
const match = /^## ([A-Z][A-Z ]*[A-Z])/m.exec(readFileSync(path, 'utf8'));
|
|
97
|
+
return match ? match[1].trimEnd() : '';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** @typedef {{ name: string, batch: number, kind: 'nomem' | 'mem', run: number }} Run */
|
|
101
|
+
|
|
102
|
+
/** Every run directory whose name parses, in the order they ran. @type {Run[]} */
|
|
103
|
+
const runs = (() => {
|
|
104
|
+
try {
|
|
105
|
+
return readdirSync(RUNS, { withFileTypes: true })
|
|
106
|
+
.filter((e) => e.isDirectory() && RUN_NAME.test(e.name))
|
|
107
|
+
.map((e) => {
|
|
108
|
+
const m = /** @type {RegExpExecArray} */ (RUN_NAME.exec(e.name));
|
|
109
|
+
return {
|
|
110
|
+
name: e.name,
|
|
111
|
+
batch: +m[1],
|
|
112
|
+
kind: /** @type {'nomem' | 'mem'} */ (m[2]),
|
|
113
|
+
run: +m[3],
|
|
114
|
+
};
|
|
115
|
+
})
|
|
116
|
+
.sort(
|
|
117
|
+
(a, b) =>
|
|
118
|
+
a.batch - b.batch ||
|
|
119
|
+
Number(a.kind === 'mem') - Number(b.kind === 'mem') ||
|
|
120
|
+
a.run - b.run,
|
|
121
|
+
);
|
|
122
|
+
} catch {
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
})();
|
|
126
|
+
|
|
127
|
+
const pad = (/** @type {number} */ n) => String(n).padStart(2, '0');
|
|
128
|
+
/** @param {number} batch @param {'nomem' | 'mem'} kind @param {number} run */
|
|
129
|
+
const runDir = (batch, kind, run) => `${RUNS}/batch${pad(batch)}-${kind}-run${run}`;
|
|
130
|
+
/** @param {'nomem' | 'mem'} kind */
|
|
131
|
+
const memoryFlag = (kind) =>
|
|
132
|
+
kind === 'nomem'
|
|
133
|
+
? '--memory finetune/empty'
|
|
134
|
+
: `--memory "$(${SCRIPTS}/memory.mjs path --candidate)"`;
|
|
135
|
+
/** @param {number} batch @param {'nomem' | 'mem'} kind @param {number} run @param {number} [c] */
|
|
136
|
+
const runIt = (batch, kind, run, c = concurrency) => {
|
|
137
|
+
const dir = runDir(batch, kind, run);
|
|
138
|
+
const copy =
|
|
139
|
+
kind === 'nomem' && run === 1
|
|
140
|
+
? ''
|
|
141
|
+
: `mkdir -p ${dir} && cp ${runDir(batch, 'nomem', 1)}/cases.json ${dir}/ && `;
|
|
142
|
+
return (
|
|
143
|
+
`${copy}zen run batch --input ${dir}/cases.json --batch-dir ${dir}/batch ` +
|
|
144
|
+
`${memoryFlag(kind)} --concurrency ${c}`
|
|
145
|
+
);
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/** @param {Run} r @returns {number} runs of r's batch and kind up to r, not counting void ones */
|
|
149
|
+
const tuned = (r) =>
|
|
150
|
+
runs.filter(
|
|
151
|
+
(x) =>
|
|
152
|
+
x.batch === r.batch &&
|
|
153
|
+
x.kind === r.kind &&
|
|
154
|
+
x.run <= r.run &&
|
|
155
|
+
verdict(join(RUNS, x.name)) !== 'VOID',
|
|
156
|
+
).length;
|
|
157
|
+
|
|
158
|
+
/** @returns {Map<number, string[]>} case ids of every batch, from its first no-memory run */
|
|
159
|
+
function batches() {
|
|
160
|
+
/** @type {Map<number, string[]>} */
|
|
161
|
+
const found = new Map();
|
|
162
|
+
for (const r of runs) {
|
|
163
|
+
if (r.kind === 'nomem' && r.run === 1) {
|
|
164
|
+
found.set(
|
|
165
|
+
r.batch,
|
|
166
|
+
(json(join(RUNS, r.name, 'cases.json'))?.batch ?? []).map(
|
|
167
|
+
(/** @type {{ id: string }} */ c) => c.id,
|
|
168
|
+
),
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return found;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Open and stuck difficult cases. Stuck: `maxRetries` batches have held it
|
|
177
|
+
* since it was added, and it is still not fixed.
|
|
178
|
+
*
|
|
179
|
+
* @returns {{ open: number, stuck: number }}
|
|
180
|
+
*/
|
|
181
|
+
function difficulty() {
|
|
182
|
+
const held = batches();
|
|
183
|
+
const unfixed = (json(DIFFICULT)?.cases ?? []).filter((/** @type {any} */ e) => !e.fixed);
|
|
184
|
+
const stuck = unfixed.filter((/** @type {any} */ e) => {
|
|
185
|
+
const since = Number(RUN_NAME.exec(e.added)?.[1] ?? 0);
|
|
186
|
+
const retries = [...held.entries()].filter(
|
|
187
|
+
([n, ids]) => n > since && ids.includes(e.id),
|
|
188
|
+
).length;
|
|
189
|
+
return retries >= maxRetries;
|
|
190
|
+
}).length;
|
|
191
|
+
return { open: unfixed.length - stuck, stuck };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Why a run that says PASSED may not pass, or '' if it may.
|
|
196
|
+
*
|
|
197
|
+
* @param {Run} r
|
|
198
|
+
* @returns {string}
|
|
199
|
+
*/
|
|
200
|
+
function refusal(r) {
|
|
201
|
+
const dir = join(RUNS, r.name);
|
|
202
|
+
const findings = existsSync(join(dir, 'findings.md'))
|
|
203
|
+
? readFileSync(join(dir, 'findings.md'), 'utf8')
|
|
204
|
+
: '';
|
|
205
|
+
const review = r.kind === 'nomem' ? 'Cost review' : 'Memory review';
|
|
206
|
+
if (!new RegExp(`^## ${review}`, 'm').test(findings)) {
|
|
207
|
+
return r.kind === 'nomem'
|
|
208
|
+
? `${r.name}: says PASSED but has no "## Cost review" - review llm calls, ` +
|
|
209
|
+
'forks and discovery for every case before passing'
|
|
210
|
+
: `${r.name}: says PASSED but has no "## Memory review" - review recall, ` +
|
|
211
|
+
're-commits and savings for every case before passing';
|
|
212
|
+
}
|
|
213
|
+
if (existsSync(join(dir, 'changes.md')) && tuned(r) < MAX_RUNS) {
|
|
214
|
+
return (
|
|
215
|
+
`${r.name}: says PASSED but applied edits, which nothing has confirmed - ` +
|
|
216
|
+
`run it again: ${runIt(r.batch, r.kind, r.run + 1)}`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
return '';
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* After a batch passed with memory: the next batch while there is anything left,
|
|
224
|
+
* else the final check.
|
|
225
|
+
*
|
|
226
|
+
* @param {number} batch
|
|
227
|
+
* @returns {string}
|
|
228
|
+
*/
|
|
229
|
+
function afterPass(batch) {
|
|
230
|
+
const { open, stuck } = difficulty();
|
|
231
|
+
const used = new Set([...batches().values()].flat());
|
|
232
|
+
const unused = selection.filter((id) => !used.has(id)).length;
|
|
233
|
+
const n = batch + 1;
|
|
234
|
+
|
|
235
|
+
if (unused > 0 || open > 0) {
|
|
236
|
+
return (
|
|
237
|
+
`batch ${batch} passed (${unused} unused case(s), ${open} difficult open) - ` +
|
|
238
|
+
`build batch ${n}: ${SCRIPTS}/batch.mjs next -o ${runDir(n, 'nomem', 1)}/cases.json`
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
const stuckNote = stuck > 0 ? ` (${stuck} difficult case(s) stuck - report them)` : '';
|
|
242
|
+
if (!existsSync(FINAL)) {
|
|
243
|
+
return (
|
|
244
|
+
`every case used, none open${stuckNote} - run the final check into ${FINAL}/: ` +
|
|
245
|
+
`the whole selection, with the last good memory`
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
if (!existsSync(join(FINAL, 'batch', 'batch.json'))) {
|
|
249
|
+
return `${FINAL}: the run never finished - move batch/ aside and re-run it`;
|
|
250
|
+
}
|
|
251
|
+
switch (verdict(FINAL)) {
|
|
252
|
+
case 'VERIFIED':
|
|
253
|
+
return 'done - the final check is VERIFIED and nothing is left to tune';
|
|
254
|
+
case '':
|
|
255
|
+
return `${FINAL}: not graded yet - ${SCRIPTS}/report.mjs -d ${FINAL}/batch oom, then grade`;
|
|
256
|
+
default:
|
|
257
|
+
return (
|
|
258
|
+
`${FINAL}: not VERIFIED - report what regressed under "Open problems" and ` +
|
|
259
|
+
'tell the user; the tuning does not re-open itself'
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* The step after `last`, the latest run of the latest batch.
|
|
266
|
+
*
|
|
267
|
+
* @param {Run} last
|
|
268
|
+
* @returns {string}
|
|
269
|
+
*/
|
|
270
|
+
function afterRun(last) {
|
|
271
|
+
const dir = join(RUNS, last.name);
|
|
272
|
+
if (!existsSync(join(dir, 'batch'))) {
|
|
273
|
+
return `${last.name}: not run yet - ${runIt(last.batch, last.kind, last.run)}`;
|
|
274
|
+
}
|
|
275
|
+
if (!existsSync(join(dir, 'batch', 'batch.json'))) {
|
|
276
|
+
return `${last.name}: the run never finished - move batch/ aside and re-run it`;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
switch (verdict(dir)) {
|
|
280
|
+
case '':
|
|
281
|
+
return `${last.name}: not graded yet - ${SCRIPTS}/report.mjs -d ${dir}/batch oom, then grade`;
|
|
282
|
+
case 'VOID': {
|
|
283
|
+
// What the void run actually ran at, not the config: the agent may
|
|
284
|
+
// already have halved that.
|
|
285
|
+
const ran =
|
|
286
|
+
Number(json(join(dir, 'batch', 'batch.json'))?.batch?.concurrency) || concurrency;
|
|
287
|
+
if (ran <= 4) {
|
|
288
|
+
return (
|
|
289
|
+
`${last.name}: void - out of memory at concurrency ${ran}, the floor: ` +
|
|
290
|
+
'the machine cannot run this batch - stop and tell the user'
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
const halved = Math.max(4, Math.floor(ran / 2));
|
|
294
|
+
const setIt =
|
|
295
|
+
concurrency <= halved ? '' : `set "concurrency": ${halved} in ${CONFIG}, then `;
|
|
296
|
+
return (
|
|
297
|
+
`${last.name}: void - out of memory at concurrency ${ran}: ${setIt}` +
|
|
298
|
+
`run the same cases again: ${runIt(last.batch, last.kind, last.run + 1, Math.min(concurrency, halved))}`
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
case 'PASSED': {
|
|
302
|
+
if (refusal(last)) {
|
|
303
|
+
return refusal(last);
|
|
304
|
+
}
|
|
305
|
+
if (last.kind === 'nomem') {
|
|
306
|
+
if (json(CANDIDATE)?.from !== last.name) {
|
|
307
|
+
return (
|
|
308
|
+
`${last.name}: passed without memory - build the candidate memory: ` +
|
|
309
|
+
`${SCRIPTS}/memory.mjs candidate ${last.name}`
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
return (
|
|
313
|
+
`batch ${last.batch} passed without memory and the candidate is built - ` +
|
|
314
|
+
`run it with memory: ${runIt(last.batch, 'mem', 1)}`
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
if (!(json(LAST_GOOD)?.batches ?? []).includes(last.batch)) {
|
|
318
|
+
return (
|
|
319
|
+
`batch ${last.batch} passed with memory - keep its memory first: ` +
|
|
320
|
+
`${SCRIPTS}/memory.mjs checkpoint ${last.name}`
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
return afterPass(last.batch);
|
|
324
|
+
}
|
|
325
|
+
default: {
|
|
326
|
+
const what = last.kind === 'nomem' ? 'no-memory' : 'with-memory';
|
|
327
|
+
if (tuned(last) >= MAX_RUNS) {
|
|
328
|
+
return (
|
|
329
|
+
`batch ${last.batch} has had ${MAX_RUNS} ${what} runs - stop tuning it: revert what ` +
|
|
330
|
+
`${last.name} did not confirm, add what is still wrong to difficult.mjs, ` +
|
|
331
|
+
'and write PASSED'
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
return existsSync(join(dir, 'changes.md'))
|
|
335
|
+
? `edits applied - confirm them: ${runIt(last.batch, last.kind, last.run + 1)}`
|
|
336
|
+
: `${last.name}: graded, nothing applied - merge the proposals and write changes.md`;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/** @returns {string} */
|
|
342
|
+
function nextStep() {
|
|
343
|
+
if (datasetSize === 0) {
|
|
344
|
+
return `build ${DATASET} from the whole training set - every case, no limit`;
|
|
345
|
+
}
|
|
346
|
+
if (!config) {
|
|
347
|
+
return `write ${CONFIG}: limit, minBatch, maxBatch, recheck, concurrency, seed, maxRetries`;
|
|
348
|
+
}
|
|
349
|
+
if (selection.length === 0) {
|
|
350
|
+
return `choose the cases: ${SCRIPTS}/select.mjs -o ${SELECTION}`;
|
|
351
|
+
}
|
|
352
|
+
if (!existsSync(README)) {
|
|
353
|
+
return `write ${README}: the plan - before anything runs`;
|
|
354
|
+
}
|
|
355
|
+
const last = runs.at(-1);
|
|
356
|
+
if (!last) {
|
|
357
|
+
return `build batch 1: ${SCRIPTS}/batch.mjs next -o ${runDir(1, 'nomem', 1)}/cases.json`;
|
|
358
|
+
}
|
|
359
|
+
return afterRun(last);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// The README is patched by hand after every step; say so when it fell behind.
|
|
363
|
+
if (existsSync(README)) {
|
|
364
|
+
const written = mtime(README);
|
|
365
|
+
const newer = [
|
|
366
|
+
CONFIG,
|
|
367
|
+
SELECTION,
|
|
368
|
+
DIFFICULT,
|
|
369
|
+
LAST_GOOD,
|
|
370
|
+
CANDIDATE,
|
|
371
|
+
...[...runs.map((r) => join(RUNS, r.name)), FINAL].flatMap((dir) =>
|
|
372
|
+
['batch/batch.json', 'findings.md', 'changes.md'].map((f) => join(dir, f)),
|
|
373
|
+
),
|
|
374
|
+
].filter((path) => mtime(path) > written);
|
|
375
|
+
if (newer.length > 0) {
|
|
376
|
+
const latest = newer.sort((a, b) => mtime(b) - mtime(a))[0];
|
|
377
|
+
console.log(`first: patch ${README} - ${latest} is newer than it`);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// A cut dataset is only right if the user asked for one; never let it pass unseen.
|
|
382
|
+
if (selection.length > 0 && selection.length < datasetSize) {
|
|
383
|
+
console.log(
|
|
384
|
+
`note: limit ${selection.length} of ${datasetSize} cases - ${datasetSize - selection.length} ` +
|
|
385
|
+
`are never tuned against. Unless the user asked for that, set "limit": ${datasetSize} ` +
|
|
386
|
+
`in ${CONFIG} and re-run select.mjs -o ${SELECTION}`,
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Every step passes through here, so the usage report never lags by more than one.
|
|
391
|
+
if (existsSync(`${SCRIPTS}/usage.mjs`)) {
|
|
392
|
+
spawnSync(process.execPath, [`${SCRIPTS}/usage.mjs`, '-q'], {
|
|
393
|
+
stdio: 'ignore',
|
|
394
|
+
timeout: 30_000,
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
console.log(nextStep());
|