zelari-code 1.29.0 → 1.30.1
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/dist/cli/headless.js +14 -0
- package/dist/cli/headless.js.map +1 -1
- package/dist/cli/hooks/useSlashDispatch.js +11 -0
- package/dist/cli/hooks/useSlashDispatch.js.map +1 -1
- package/dist/cli/kraken/executor.js +63 -1
- package/dist/cli/kraken/executor.js.map +1 -1
- package/dist/cli/kraken/planner.js +87 -2
- package/dist/cli/kraken/planner.js.map +1 -1
- package/dist/cli/kraken/planner.test.js +43 -0
- package/dist/cli/kraken/planner.test.js.map +1 -0
- package/dist/cli/kraken/runtime/compile.js +73 -0
- package/dist/cli/kraken/runtime/compile.js.map +1 -0
- package/dist/cli/kraken/runtime/runScriptPlan.js +195 -0
- package/dist/cli/kraken/runtime/runScriptPlan.js.map +1 -0
- package/dist/cli/kraken/scriptPlanner.js +286 -0
- package/dist/cli/kraken/scriptPlanner.js.map +1 -0
- package/dist/cli/kraken/scriptPlanner.test.js +152 -0
- package/dist/cli/kraken/scriptPlanner.test.js.map +1 -0
- package/dist/cli/kraken/skillSuggest.js +97 -0
- package/dist/cli/kraken/skillSuggest.js.map +1 -0
- package/dist/cli/kraken/skillSuggest.test.js +157 -0
- package/dist/cli/kraken/skillSuggest.test.js.map +1 -0
- package/dist/cli/kraken/weaknessMeter.js +183 -0
- package/dist/cli/kraken/weaknessMeter.js.map +1 -0
- package/dist/cli/kraken/weaknessMeter.test.js +212 -0
- package/dist/cli/kraken/weaknessMeter.test.js.map +1 -0
- package/dist/cli/kraken/workbench.js +296 -0
- package/dist/cli/kraken/workbench.js.map +1 -0
- package/dist/cli/kraken/workbench.test.js +253 -0
- package/dist/cli/kraken/workbench.test.js.map +1 -0
- package/dist/cli/kraken/workbenchView.js +155 -0
- package/dist/cli/kraken/workbenchView.js.map +1 -0
- package/dist/cli/kraken/workbenchView.test.js +130 -0
- package/dist/cli/kraken/workbenchView.test.js.map +1 -0
- package/dist/cli/main.bundled.js +2214 -299
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/runHeadless.js +58 -7
- package/dist/cli/runHeadless.js.map +1 -1
- package/dist/cli/slashCommands.js +16 -0
- package/dist/cli/slashCommands.js.map +1 -1
- package/dist/cli/slashHandlers/krakenFanout.js +200 -0
- package/dist/cli/slashHandlers/krakenFanout.js.map +1 -0
- package/dist/cli/slashHandlers/krakenWorkbench.js +49 -0
- package/dist/cli/slashHandlers/krakenWorkbench.js.map +1 -0
- package/dist/cli/tools/krakenCsvFanout.js +260 -0
- package/dist/cli/tools/krakenCsvFanout.js.map +1 -0
- package/dist/cli/tools/krakenCsvFanout.test.js +200 -0
- package/dist/cli/tools/krakenCsvFanout.test.js.map +1 -0
- package/dist/cli/tools/krakenModel.js +32 -0
- package/dist/cli/tools/krakenModel.js.map +1 -1
- package/dist/cli/tools/krakenRadio.js.map +1 -1
- package/dist/cli/tools/taskTool.js +1 -1
- package/dist/cli/tools/taskTool.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kraken workbench writer (Slice E / Pillar 3, F3.1).
|
|
3
|
+
*
|
|
4
|
+
* Writes a single Markdown file at `.zelari/radio/workbench-<id>.md` that
|
|
5
|
+
* reflects the live state of a running graph. The format is intentionally
|
|
6
|
+
* plain Markdown so a developer can `tail -f` the file (or open it in a
|
|
7
|
+
* Markdown viewer that auto-refreshes) and see what the run is doing
|
|
8
|
+
* without touching the TUI.
|
|
9
|
+
*
|
|
10
|
+
* The file is rewritten atomically on every `snapshot()` (write to `.tmp`,
|
|
11
|
+
* rename) so a `tail` never sees a partial state. Updates are debounced:
|
|
12
|
+
* 500ms after the last `markX` call, the writer flushes the buffer to
|
|
13
|
+
* disk. This keeps the file fresh without thrashing on bursty event
|
|
14
|
+
* streams (e.g. a 100-tentacle parallel wave emits 100 `node_start` events
|
|
15
|
+
* in a single tick).
|
|
16
|
+
*
|
|
17
|
+
* Format (one Markdown file):
|
|
18
|
+
* # Kraken workbench
|
|
19
|
+
* **Goal:** <goal>
|
|
20
|
+
* **Graph id:** <id>
|
|
21
|
+
* **Started:** <iso> · **Elapsed:** <hh:mm:ss>
|
|
22
|
+
*
|
|
23
|
+
* ## Progress: <done>/<total> · <running>↑ · <errored>✗
|
|
24
|
+
*
|
|
25
|
+
* ## Wave
|
|
26
|
+
* | id | label | kind | deps | scope | status | verdict | weakness | model | duration |
|
|
27
|
+
* |----|-------|------|------|-------|--------|---------|----------|-------|----------|
|
|
28
|
+
* | t0001 | map auth | explore | | src/auth | done | pass | 0.83 | grok-3-mini | 4s |
|
|
29
|
+
* | t0003 | auth fix | general | t0001, t0002 | src/auth | done | fail | 0.41 | grok | 3s |
|
|
30
|
+
* ...
|
|
31
|
+
*
|
|
32
|
+
* ## Events (latest 30)
|
|
33
|
+
* - 11:00:01 graph_plan plan=plan.ts (4 nodes)
|
|
34
|
+
* - 11:00:02 node_start t0001 map auth
|
|
35
|
+
* ...
|
|
36
|
+
*
|
|
37
|
+
* **v1.31.x**: each completed verify/spec/conformance node surfaces two
|
|
38
|
+
* extra columns — `verdict` (pass / fail / unknown) and `weakness`
|
|
39
|
+
* (Bennett-style score in `[0, 1]`, where 1.0 = maximally general
|
|
40
|
+
* claim, 0.0 = maximally specific claim). The weakness column is
|
|
41
|
+
* metadata, not a gate: a PASS with weakness 0.92 is "loosely
|
|
42
|
+
* asserted" (a reviewer that said little), a PASS with weakness 0.31
|
|
43
|
+
* is "tightly asserted" (a reviewer that pinned many specifics). The
|
|
44
|
+
* workbench now lets you see which you got, per node, in the same
|
|
45
|
+
* Markdown file you already `tail -f`.
|
|
46
|
+
*
|
|
47
|
+
* **v1.31.x (Slice N+2)**: a `deps` column is now part of the Wave
|
|
48
|
+
* table so the desktop graph visualizer (and any other consumer) can
|
|
49
|
+
* read dependency edges without needing the raw plan JSON. Cells list
|
|
50
|
+
* the upstream node ids comma-separated; empty when the node has no
|
|
51
|
+
* dependencies (root nodes).
|
|
52
|
+
*
|
|
53
|
+
* @since Kraken v1.30.x — workflow script runtime (Pillar 3)
|
|
54
|
+
* @since Kraken v1.31.x — verdict + weakness + deps in workbench
|
|
55
|
+
*/
|
|
56
|
+
import { promises as fs } from 'node:fs';
|
|
57
|
+
import path from 'node:path';
|
|
58
|
+
import { parsePersonaVerdict } from '@zelari/core';
|
|
59
|
+
const DEBOUNCE_DEFAULT_MS = 500;
|
|
60
|
+
const MAX_EVENTS = 30;
|
|
61
|
+
const MAX_NODES_IN_TABLE = 200;
|
|
62
|
+
export function isWorkbenchEnabled(env = process.env) {
|
|
63
|
+
const v = (env.ZELARI_KRAKEN_WORKBENCH ?? '1').trim().toLowerCase();
|
|
64
|
+
if (v === '0' || v === 'false' || v === 'no' || v === 'off')
|
|
65
|
+
return false;
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
function workbenchPath(cwd, graphId) {
|
|
69
|
+
return path.join(cwd, '.zelari', 'radio', `workbench-${graphId}.md`);
|
|
70
|
+
}
|
|
71
|
+
/** A workbench writer for one run. Cheap to construct. */
|
|
72
|
+
export class WorkbenchWriter {
|
|
73
|
+
cwd;
|
|
74
|
+
graphId;
|
|
75
|
+
goal;
|
|
76
|
+
enabled;
|
|
77
|
+
debounceMs;
|
|
78
|
+
startedAt;
|
|
79
|
+
nodesById = new Map();
|
|
80
|
+
events = [];
|
|
81
|
+
dirty = true;
|
|
82
|
+
timer = null;
|
|
83
|
+
lastWrite = null;
|
|
84
|
+
constructor(opts) {
|
|
85
|
+
this.cwd = opts.cwd;
|
|
86
|
+
this.graphId = opts.graphId;
|
|
87
|
+
this.goal = opts.goal;
|
|
88
|
+
this.enabled = opts.enabled ?? isWorkbenchEnabled();
|
|
89
|
+
this.debounceMs = opts.debounceMs ?? DEBOUNCE_DEFAULT_MS;
|
|
90
|
+
this.startedAt = new Date().toISOString();
|
|
91
|
+
}
|
|
92
|
+
/** Seed the writer with the planned nodes. Idempotent: later calls
|
|
93
|
+
* merge — a node's status is not reset to `pending` if it's already
|
|
94
|
+
* `running` / `done` / `error`. */
|
|
95
|
+
setNodes(nodes) {
|
|
96
|
+
if (!this.enabled)
|
|
97
|
+
return;
|
|
98
|
+
for (const n of nodes) {
|
|
99
|
+
const existing = this.nodesById.get(n.id);
|
|
100
|
+
if (!existing)
|
|
101
|
+
this.nodesById.set(n.id, { ...n });
|
|
102
|
+
// Preserve non-pending status.
|
|
103
|
+
else if (existing.status !== 'pending') {
|
|
104
|
+
this.nodesById.set(n.id, { ...existing, ...n, status: existing.status });
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
this.nodesById.set(n.id, { ...n });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
this.markDirty();
|
|
111
|
+
}
|
|
112
|
+
markStart(id, patch = {}) {
|
|
113
|
+
if (!this.enabled)
|
|
114
|
+
return;
|
|
115
|
+
const n = this.nodesById.get(id) ?? { id, kind: 'general', label: id, status: 'pending' };
|
|
116
|
+
this.nodesById.set(id, {
|
|
117
|
+
...n,
|
|
118
|
+
...patch,
|
|
119
|
+
id,
|
|
120
|
+
status: 'running',
|
|
121
|
+
startedAt: patch.startedAt ?? new Date().toISOString(),
|
|
122
|
+
});
|
|
123
|
+
this.events.push({ ts: new Date().toISOString(), text: `node_start ${id} ${patch.kind ?? n.kind}` });
|
|
124
|
+
this.markDirty();
|
|
125
|
+
}
|
|
126
|
+
markEnd(id, patch) {
|
|
127
|
+
if (!this.enabled)
|
|
128
|
+
return;
|
|
129
|
+
const n = this.nodesById.get(id);
|
|
130
|
+
if (!n)
|
|
131
|
+
return;
|
|
132
|
+
// Parse the persona verdict out of `findings` (if any) so the workbench
|
|
133
|
+
// can surface the verdict + Bennett-style weakness score in the Wave
|
|
134
|
+
// table. Cheap: heuristic scan, no LLM, no I/O. Falls back to "no
|
|
135
|
+
// verdict" when findings are empty or non-persona text.
|
|
136
|
+
let verdict;
|
|
137
|
+
let weaknessScore;
|
|
138
|
+
if (patch.findings && patch.findings.length > 0) {
|
|
139
|
+
const parsed = parsePersonaVerdict(patch.findings);
|
|
140
|
+
verdict = parsed.verdict;
|
|
141
|
+
weaknessScore = parsed.weaknessScore;
|
|
142
|
+
}
|
|
143
|
+
this.nodesById.set(id, {
|
|
144
|
+
...n,
|
|
145
|
+
status: patch.status,
|
|
146
|
+
...(patch.durationMs !== undefined ? { durationMs: patch.durationMs } : {}),
|
|
147
|
+
...(patch.error ? { error: patch.error.slice(0, 200) } : {}),
|
|
148
|
+
...(Array.isArray(patch.deps) ? { deps: patch.deps } : {}),
|
|
149
|
+
...(verdict ? { verdict } : {}),
|
|
150
|
+
...(typeof weaknessScore === 'number' ? { weaknessScore } : {}),
|
|
151
|
+
endedAt: new Date().toISOString(),
|
|
152
|
+
});
|
|
153
|
+
this.events.push({
|
|
154
|
+
ts: new Date().toISOString(),
|
|
155
|
+
text: `node_end ${id} ${patch.status}${patch.durationMs !== undefined ? ` (${patch.durationMs}ms)` : ''}${verdict ? ` · ${verdict}${typeof weaknessScore === 'number' ? ` w=${weaknessScore.toFixed(2)}` : ''}` : ''}${patch.error ? ` — ${patch.error.slice(0, 80)}` : ''}`,
|
|
156
|
+
});
|
|
157
|
+
this.markDirty();
|
|
158
|
+
}
|
|
159
|
+
logEvent(text) {
|
|
160
|
+
if (!this.enabled)
|
|
161
|
+
return;
|
|
162
|
+
this.events.push({ ts: new Date().toISOString(), text });
|
|
163
|
+
this.markDirty();
|
|
164
|
+
}
|
|
165
|
+
/** Mark "this set of node ids is running in parallel right now". The
|
|
166
|
+
* workbench surfaces a "wave" line so a `tail -f` reader can see when
|
|
167
|
+
* a new parallel wave starts. */
|
|
168
|
+
markWave(ids) {
|
|
169
|
+
if (!this.enabled)
|
|
170
|
+
return;
|
|
171
|
+
this.events.push({ ts: new Date().toISOString(), text: `wave: ${ids.join(', ')}` });
|
|
172
|
+
this.markDirty();
|
|
173
|
+
}
|
|
174
|
+
/** Force a synchronous-style flush. Returns the path written, or null
|
|
175
|
+
* when the writer is disabled. */
|
|
176
|
+
async flush() {
|
|
177
|
+
if (!this.enabled)
|
|
178
|
+
return null;
|
|
179
|
+
if (this.timer) {
|
|
180
|
+
clearTimeout(this.timer);
|
|
181
|
+
this.timer = null;
|
|
182
|
+
}
|
|
183
|
+
return this.writeNow();
|
|
184
|
+
}
|
|
185
|
+
/** Cancel any pending debounced write. The next `flush()` will write. */
|
|
186
|
+
close() {
|
|
187
|
+
if (this.timer) {
|
|
188
|
+
clearTimeout(this.timer);
|
|
189
|
+
this.timer = null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// --- internals ------------------------------------------------------------
|
|
193
|
+
markDirty() {
|
|
194
|
+
if (!this.enabled)
|
|
195
|
+
return;
|
|
196
|
+
this.dirty = true;
|
|
197
|
+
if (this.timer)
|
|
198
|
+
return;
|
|
199
|
+
this.timer = setTimeout(() => {
|
|
200
|
+
this.timer = null;
|
|
201
|
+
// Fire-and-forget the write; the test suite and live tail should
|
|
202
|
+
// never see this rejection. The atomic rename is the real
|
|
203
|
+
// durability guarantee.
|
|
204
|
+
void this.writeNow().catch(() => {
|
|
205
|
+
/* ignore — best-effort telemetry */
|
|
206
|
+
});
|
|
207
|
+
}, this.debounceMs);
|
|
208
|
+
}
|
|
209
|
+
async writeNow() {
|
|
210
|
+
if (!this.enabled)
|
|
211
|
+
return null;
|
|
212
|
+
if (!this.dirty && this.lastWrite)
|
|
213
|
+
return this.lastWrite;
|
|
214
|
+
const out = workbenchPath(this.cwd, this.graphId);
|
|
215
|
+
await fs.mkdir(path.dirname(out), { recursive: true });
|
|
216
|
+
const body = this.render();
|
|
217
|
+
const tmp = `${out}.${process.pid}.${Date.now()}.tmp`;
|
|
218
|
+
await fs.writeFile(tmp, body, 'utf8');
|
|
219
|
+
await fs.rename(tmp, out);
|
|
220
|
+
this.dirty = false;
|
|
221
|
+
this.lastWrite = Promise.resolve(out);
|
|
222
|
+
return out;
|
|
223
|
+
}
|
|
224
|
+
render() {
|
|
225
|
+
const counts = countByStatus([...this.nodesById.values()]);
|
|
226
|
+
const elapsed = formatElapsed(Date.now() - Date.parse(this.startedAt));
|
|
227
|
+
const lines = [
|
|
228
|
+
`# Kraken workbench`,
|
|
229
|
+
``,
|
|
230
|
+
`**Goal:** ${this.goal}`,
|
|
231
|
+
`**Graph id:** \`${this.graphId}\``,
|
|
232
|
+
`**Started:** ${this.startedAt} · **Elapsed:** ${elapsed}`,
|
|
233
|
+
``,
|
|
234
|
+
`## Progress: ${counts.done + counts.skipped}/${this.nodesById.size} · ${counts.running}↑ · ${counts.error}✗`,
|
|
235
|
+
``,
|
|
236
|
+
];
|
|
237
|
+
lines.push(`## Wave`);
|
|
238
|
+
lines.push(``);
|
|
239
|
+
lines.push(`| id | label | kind | deps | scope | status | verdict | weakness | model | duration |`);
|
|
240
|
+
lines.push(`|----|-------|------|------|-------|--------|---------|----------|-------|----------|`);
|
|
241
|
+
const sorted = [...this.nodesById.values()].sort((a, b) => a.id.localeCompare(b.id)).slice(0, MAX_NODES_IN_TABLE);
|
|
242
|
+
for (const n of sorted) {
|
|
243
|
+
const status = statusEmoji(n.status);
|
|
244
|
+
const scope = n.scope ? n.scope.join(', ') : '';
|
|
245
|
+
const dur = n.durationMs != null ? formatMs(n.durationMs) : '';
|
|
246
|
+
const verdict = n.verdict ?? '';
|
|
247
|
+
const weakness = typeof n.weaknessScore === 'number' ? n.weaknessScore.toFixed(2) : '';
|
|
248
|
+
const deps = n.deps && n.deps.length > 0 ? n.deps.join(', ') : '';
|
|
249
|
+
lines.push(`| ${n.id} | ${n.label} | ${n.kind} | ${deps} | ${scope} | ${status} | ${verdict} | ${weakness} | ${n.model ?? ''} | ${dur} |`);
|
|
250
|
+
}
|
|
251
|
+
lines.push(``);
|
|
252
|
+
lines.push(`## Events (latest ${MAX_EVENTS})`);
|
|
253
|
+
lines.push(``);
|
|
254
|
+
const tail = this.events.slice(-MAX_EVENTS);
|
|
255
|
+
if (tail.length === 0) {
|
|
256
|
+
lines.push(`(no events yet)`);
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
for (const e of tail)
|
|
260
|
+
lines.push(`- ${e.ts.slice(11, 19)} ${e.text}`);
|
|
261
|
+
}
|
|
262
|
+
lines.push(``);
|
|
263
|
+
return lines.join('\n');
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// --- helpers -----------------------------------------------------------------
|
|
267
|
+
function countByStatus(nodes) {
|
|
268
|
+
const out = { pending: 0, running: 0, done: 0, error: 0, skipped: 0 };
|
|
269
|
+
for (const n of nodes)
|
|
270
|
+
out[n.status] += 1;
|
|
271
|
+
return out;
|
|
272
|
+
}
|
|
273
|
+
function statusEmoji(s) {
|
|
274
|
+
switch (s) {
|
|
275
|
+
case 'pending': return '○';
|
|
276
|
+
case 'running': return '↑';
|
|
277
|
+
case 'done': return '✓';
|
|
278
|
+
case 'error': return '✗';
|
|
279
|
+
case 'skipped': return '–';
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
function formatMs(ms) {
|
|
283
|
+
if (ms < 1000)
|
|
284
|
+
return `${ms}ms`;
|
|
285
|
+
if (ms < 60_000)
|
|
286
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
287
|
+
return `${Math.floor(ms / 60_000)}m ${Math.floor((ms % 60_000) / 1000)}s`;
|
|
288
|
+
}
|
|
289
|
+
function formatElapsed(ms) {
|
|
290
|
+
const totalSec = Math.max(0, Math.floor(ms / 1000));
|
|
291
|
+
const hh = String(Math.floor(totalSec / 3600)).padStart(2, '0');
|
|
292
|
+
const mm = String(Math.floor((totalSec % 3600) / 60)).padStart(2, '0');
|
|
293
|
+
const ss = String(totalSec % 60).padStart(2, '0');
|
|
294
|
+
return `${hh}:${mm}:${ss}`;
|
|
295
|
+
}
|
|
296
|
+
//# sourceMappingURL=workbench.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workbench.js","sourceRoot":"","sources":["../../../src/cli/kraken/workbench.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AA+DnD,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAChC,MAAM,UAAU,GAAG,EAAE,CAAC;AACtB,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAE/B,MAAM,UAAU,kBAAkB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACrE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,uBAAuB,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACpE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IAC1E,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,aAAa,CAAC,GAAW,EAAE,OAAe;IACjD,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa,OAAO,KAAK,CAAC,CAAC;AACvE,CAAC;AAED,0DAA0D;AAC1D,MAAM,OAAO,eAAe;IACT,GAAG,CAAS;IACZ,OAAO,CAAS;IAChB,IAAI,CAAS;IACb,OAAO,CAAU;IACjB,UAAU,CAAS;IACnB,SAAS,CAAS;IAClB,SAAS,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC7C,MAAM,GAAqB,EAAE,CAAC;IACvC,KAAK,GAAG,IAAI,CAAC;IACb,KAAK,GAA0B,IAAI,CAAC;IACpC,SAAS,GAAkC,IAAI,CAAC;IAExD,YAAY,IAAsB;QAChC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QACpB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,kBAAkB,EAAE,CAAC;QACpD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,mBAAmB,CAAC;QACzD,IAAI,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC5C,CAAC;IAED;;wCAEoC;IACpC,QAAQ,CAAC,KAAsB;QAC7B,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC1C,IAAI,CAAC,QAAQ;gBAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YAClD,+BAA+B;iBAC1B,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBACvC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,QAAQ,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;YAC3E,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;QACD,IAAI,CAAC,SAAS,EAAE,CAAC;IACnB,CAAC;IAED,SAAS,CAAC,EAAU,EAAE,QAAgC,EAAE;QACtD,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QAC1F,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE;YACrB,GAAG,CAAC;YACJ,GAAG,KAAK;YACR,EAAE;YACF,MAAM,EAAE,SAAS;YACjB,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACvD,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACrG,IAAI,CAAC,SAAS,EAAE,CAAC;IACnB,CAAC;IAED,OAAO,CAAC,EAAU,EAAE,KAAsG;QACxH,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACjC,IAAI,CAAC,CAAC;YAAE,OAAO;QAEf,wEAAwE;QACxE,qEAAqE;QACrE,kEAAkE;QAClE,wDAAwD;QACxD,IAAI,OAAiC,CAAC;QACtC,IAAI,aAAiC,CAAC;QACtC,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChD,MAAM,MAAM,GAAG,mBAAmB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YACnD,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;YACzB,aAAa,GAAG,MAAM,CAAC,aAAa,CAAC;QACvC,CAAC;QAED,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE;YACrB,GAAG,CAAC;YACJ,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,GAAG,CAAC,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3E,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5D,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1D,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/B,GAAG,CAAC,OAAO,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/D,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SAClC,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;YACf,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAC5B,IAAI,EAAE,YAAY,EAAE,IAAI,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,UAAU,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,MAAM,OAAO,GAAG,OAAO,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;SAC7Q,CAAC,CAAC;QACH,IAAI,CAAC,SAAS,EAAE,CAAC;IACnB,CAAC;IAED,QAAQ,CAAC,IAAY;QACnB,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC,SAAS,EAAE,CAAC;IACnB,CAAC;IAED;;sCAEkC;IAClC,QAAQ,CAAC,GAAa;QACpB,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,SAAS,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QACpF,IAAI,CAAC,SAAS,EAAE,CAAC;IACnB,CAAC;IAED;uCACmC;IACnC,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAC/B,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACzB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACpB,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC;IACzB,CAAC;IAED,yEAAyE;IACzE,KAAK;QACH,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACzB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACpB,CAAC;IACH,CAAC;IAED,6EAA6E;IAErE,SAAS;QACf,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO;QACvB,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC3B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YAClB,iEAAiE;YACjE,0DAA0D;YAC1D,wBAAwB;YACxB,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE;gBAC9B,oCAAoC;YACtC,CAAC,CAAC,CAAC;QACL,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IACtB,CAAC;IAEO,KAAK,CAAC,QAAQ;QACpB,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO,IAAI,CAAC,SAAS,CAAC;QACzD,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAClD,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,GAAG,GAAG,IAAI,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC;QACtD,MAAM,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QACtC,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC1B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACtC,OAAO,GAAG,CAAC;IACb,CAAC;IAEO,MAAM;QACZ,MAAM,MAAM,GAAG,aAAa,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC3D,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QACvE,MAAM,KAAK,GAAa;YACtB,oBAAoB;YACpB,EAAE;YACF,aAAa,IAAI,CAAC,IAAI,EAAE;YACxB,mBAAmB,IAAI,CAAC,OAAO,IAAI;YACnC,gBAAgB,IAAI,CAAC,SAAS,mBAAmB,OAAO,EAAE;YAC1D,EAAE;YACF,gBAAgB,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,MAAM,MAAM,CAAC,OAAO,OAAO,MAAM,CAAC,KAAK,GAAG;YAC7G,EAAE;SACH,CAAC;QAEF,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,uFAAuF,CAAC,CAAC;QACpG,KAAK,CAAC,IAAI,CAAC,uFAAuF,CAAC,CAAC;QACpG,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAC;QAClH,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YACrC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAChD,MAAM,GAAG,GAAG,CAAC,CAAC,UAAU,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/D,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC;YAChC,MAAM,QAAQ,GAAG,OAAO,CAAC,CAAC,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACvF,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAClE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,IAAI,MAAM,IAAI,MAAM,KAAK,MAAM,MAAM,MAAM,OAAO,MAAM,QAAQ,MAAM,CAAC,CAAC,KAAK,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;QAC7I,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEf,KAAK,CAAC,IAAI,CAAC,qBAAqB,UAAU,GAAG,CAAC,CAAC;QAC/C,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,UAAU,CAAC,CAAC;QAC5C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,KAAK,MAAM,CAAC,IAAI,IAAI;gBAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACxE,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;CACF;AAED,gFAAgF;AAEhF,SAAS,aAAa,CAAC,KAAsB;IAC3C,MAAM,GAAG,GAA+B,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAClG,KAAK,MAAM,CAAC,IAAI,KAAK;QAAE,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1C,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,WAAW,CAAC,CAAa;IAChC,QAAQ,CAAC,EAAE,CAAC;QACV,KAAK,SAAS,CAAC,CAAC,OAAO,GAAG,CAAC;QAC3B,KAAK,SAAS,CAAC,CAAC,OAAO,GAAG,CAAC;QAC3B,KAAK,MAAM,CAAC,CAAC,OAAO,GAAG,CAAC;QACxB,KAAK,OAAO,CAAC,CAAC,OAAO,GAAG,CAAC;QACzB,KAAK,SAAS,CAAC,CAAC,OAAO,GAAG,CAAC;IAC7B,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,EAAU;IAC1B,IAAI,EAAE,GAAG,IAAI;QAAE,OAAO,GAAG,EAAE,IAAI,CAAC;IAChC,IAAI,EAAE,GAAG,MAAM;QAAE,OAAO,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IACrD,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC;AAC5E,CAAC;AAED,SAAS,aAAa,CAAC,EAAU;IAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;IACpD,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAChE,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACvE,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAClD,OAAO,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kraken workbench writer — tests.
|
|
3
|
+
*
|
|
4
|
+
* Covers:
|
|
5
|
+
* - `flush()` writes a Markdown file to `.zelari/radio/workbench-<id>.md`
|
|
6
|
+
* - The file is rewritten atomically (no partial state on tail)
|
|
7
|
+
* - `setNodes` + `markStart` + `markEnd` produce the expected table
|
|
8
|
+
* - Events are capped at MAX_EVENTS (30)
|
|
9
|
+
* - `ZELARI_KRAKEN_WORKBENCH=0` disables the writer
|
|
10
|
+
* - Disabled writer is a no-op (no file, no error)
|
|
11
|
+
*/
|
|
12
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
13
|
+
import { promises as fs } from 'node:fs';
|
|
14
|
+
import os from 'node:os';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
import { WorkbenchWriter, isWorkbenchEnabled } from './workbench.js';
|
|
17
|
+
let tmp = '';
|
|
18
|
+
let counter = 0;
|
|
19
|
+
async function freshTmp() {
|
|
20
|
+
const t = await fs.mkdtemp(path.join(os.tmpdir(), 'kraken-workbench-'));
|
|
21
|
+
counter += 1;
|
|
22
|
+
return t;
|
|
23
|
+
}
|
|
24
|
+
describe('isWorkbenchEnabled', () => {
|
|
25
|
+
it('defaults to true', () => {
|
|
26
|
+
expect(isWorkbenchEnabled({})).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
it('respects ZELARI_KRAKEN_WORKBENCH=0', () => {
|
|
29
|
+
expect(isWorkbenchEnabled({ ZELARI_KRAKEN_WORKBENCH: '0' })).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
it('treats "no", "false", "off" as disabled', () => {
|
|
32
|
+
expect(isWorkbenchEnabled({ ZELARI_KRAKEN_WORKBENCH: 'no' })).toBe(false);
|
|
33
|
+
expect(isWorkbenchEnabled({ ZELARI_KRAKEN_WORKBENCH: 'false' })).toBe(false);
|
|
34
|
+
expect(isWorkbenchEnabled({ ZELARI_KRAKEN_WORKBENCH: 'off' })).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
describe('WorkbenchWriter', () => {
|
|
38
|
+
beforeEach(async () => {
|
|
39
|
+
tmp = await freshTmp();
|
|
40
|
+
});
|
|
41
|
+
it('writes a Markdown file with the expected header', async () => {
|
|
42
|
+
const w = new WorkbenchWriter({ cwd: tmp, graphId: 'g-1', goal: 'refactor auth' });
|
|
43
|
+
w.setNodes([{ id: 't0001', kind: 'explore', label: 'map', status: 'pending' }]);
|
|
44
|
+
const out = await w.flush();
|
|
45
|
+
expect(out).toBe(path.join(tmp, '.zelari', 'radio', 'workbench-g-1.md'));
|
|
46
|
+
const body = await fs.readFile(out, 'utf8');
|
|
47
|
+
expect(body).toContain('# Kraken workbench');
|
|
48
|
+
expect(body).toContain('**Goal:** refactor auth');
|
|
49
|
+
expect(body).toContain('**Graph id:** `g-1`');
|
|
50
|
+
expect(body).toContain('| t0001 | map | explore |');
|
|
51
|
+
});
|
|
52
|
+
it('updates the table on markStart / markEnd', async () => {
|
|
53
|
+
const w = new WorkbenchWriter({ cwd: tmp, graphId: 'g-2', goal: 'x' });
|
|
54
|
+
w.setNodes([{ id: 't0001', kind: 'general', label: 'do it', status: 'pending', scope: ['src/auth'] }]);
|
|
55
|
+
w.markStart('t0001', { model: 'grok-3-mini' });
|
|
56
|
+
w.markEnd('t0001', { status: 'done', durationMs: 1234 });
|
|
57
|
+
const out = await w.flush();
|
|
58
|
+
const body = await fs.readFile(out, 'utf8');
|
|
59
|
+
expect(body).toContain('✓'); // done emoji
|
|
60
|
+
expect(body).toContain('1.2s'); // formatted duration
|
|
61
|
+
expect(body).toContain('grok-3-mini');
|
|
62
|
+
expect(body).toContain('src/auth');
|
|
63
|
+
});
|
|
64
|
+
it('captures events in the latest-30 list', async () => {
|
|
65
|
+
const w = new WorkbenchWriter({ cwd: tmp, graphId: 'g-3', goal: 'x' });
|
|
66
|
+
for (let i = 0; i < 50; i++)
|
|
67
|
+
w.logEvent(`event ${i}`);
|
|
68
|
+
const out = await w.flush();
|
|
69
|
+
const body = await fs.readFile(out, 'utf8');
|
|
70
|
+
// The first 20 events are dropped; the last 30 survive.
|
|
71
|
+
expect(body).not.toContain('event 0 ');
|
|
72
|
+
expect(body).not.toContain('event 19 ');
|
|
73
|
+
expect(body).toContain('event 20');
|
|
74
|
+
expect(body).toContain('event 49');
|
|
75
|
+
});
|
|
76
|
+
it('marks a node as error and surfaces the message', async () => {
|
|
77
|
+
const w = new WorkbenchWriter({ cwd: tmp, graphId: 'g-4', goal: 'x' });
|
|
78
|
+
w.setNodes([{ id: 't0001', kind: 'general', label: 'do', status: 'pending' }]);
|
|
79
|
+
w.markStart('t0001');
|
|
80
|
+
w.markEnd('t0001', { status: 'error', error: 'kaboom', durationMs: 100 });
|
|
81
|
+
const out = await w.flush();
|
|
82
|
+
const body = await fs.readFile(out, 'utf8');
|
|
83
|
+
expect(body).toContain('✗');
|
|
84
|
+
expect(body).toContain('kaboom');
|
|
85
|
+
});
|
|
86
|
+
it('parses the persona verdict and Bennett weakness score from findings', async () => {
|
|
87
|
+
// Simulate a verify/spec/conformance node whose findings include the
|
|
88
|
+
// standard trailer. The workbench must surface the verdict + weakness
|
|
89
|
+
// in the Wave table so a `tail -f` reader sees whether a PASS was
|
|
90
|
+
// earned by a tightly-asserted or loosely-claimed reviewer.
|
|
91
|
+
const w = new WorkbenchWriter({ cwd: tmp, graphId: 'g-err', goal: 'x' });
|
|
92
|
+
w.setNodes([{ id: 'v-001', kind: 'verify', label: 'review', status: 'pending' }]);
|
|
93
|
+
w.markStart('v-001');
|
|
94
|
+
w.markEnd('v-001', {
|
|
95
|
+
status: 'done',
|
|
96
|
+
durationMs: 100,
|
|
97
|
+
findings: 'All requirements are met. The function returns the right shape.\n\nVERDICT: PASS',
|
|
98
|
+
});
|
|
99
|
+
const out = await w.flush();
|
|
100
|
+
const body = await fs.readFile(out, 'utf8');
|
|
101
|
+
expect(body).toContain('| v-001 |');
|
|
102
|
+
// Header columns for deps + verdict + weakness are present.
|
|
103
|
+
// (v1.31.x+ renders the deps column between kind and scope.)
|
|
104
|
+
expect(body).toMatch(/\|\s*id\s*\|\s*label\s*\|\s*kind\s*\|\s*deps\s*\|\s*scope\s*\|\s*status\s*\|\s*verdict\s*\|\s*weakness\s*\|/);
|
|
105
|
+
// Verdict cell shows pass.
|
|
106
|
+
expect(body).toMatch(/\|\s*v-001\s*\|[^|]+\|\s*verify\s*\|[^|]+\|[^|]+\|[^|]+\|\s*pass\s*\|/);
|
|
107
|
+
// Weakness is a 2-decimal number in [0, 1].
|
|
108
|
+
expect(body).toMatch(/\|\s*pass\s*\|\s*(0\.\d{2}|1\.00)\s*\|/);
|
|
109
|
+
});
|
|
110
|
+
it('records a high weakness (loosely claimed) PASS when findings are vague', async () => {
|
|
111
|
+
// Loosely-claimed reviewer: "looks good" → high weakness (close to 1).
|
|
112
|
+
const writer = new WorkbenchWriter({ cwd: tmp, graphId: 'g-loose', goal: 'x' });
|
|
113
|
+
writer.setNodes([{ id: 'v-002', kind: 'verify', label: 'r', status: 'pending' }]);
|
|
114
|
+
writer.markStart('v-002');
|
|
115
|
+
writer.markEnd('v-002', {
|
|
116
|
+
status: 'done',
|
|
117
|
+
durationMs: 50,
|
|
118
|
+
findings: 'Looks good.\n\nVERDICT: PASS',
|
|
119
|
+
});
|
|
120
|
+
const out = await writer.flush();
|
|
121
|
+
const body = await fs.readFile(out, 'utf8');
|
|
122
|
+
const weaknessCell = body.match(/\|\s*pass\s*\|\s*(\d\.\d{2})\s*\|/);
|
|
123
|
+
expect(weaknessCell).not.toBeNull();
|
|
124
|
+
const weaknessValue = parseFloat(weaknessCell[1]);
|
|
125
|
+
expect(weaknessValue).toBeGreaterThan(0.6);
|
|
126
|
+
});
|
|
127
|
+
it('records a low weakness (tightly asserted) PASS when findings pin specifics', async () => {
|
|
128
|
+
// Tightly-asserted reviewer: pins paths + line + version → low weakness.
|
|
129
|
+
const findings = [
|
|
130
|
+
'The function MUST always return EXACTLY the value at line 42, version 1.2.3.',
|
|
131
|
+
'I GUARANTEED the file is at /src/util.ts.',
|
|
132
|
+
'',
|
|
133
|
+
'VERDICT: PASS',
|
|
134
|
+
].join('\n');
|
|
135
|
+
const writer = new WorkbenchWriter({ cwd: tmp, graphId: 'g-tight', goal: 'x' });
|
|
136
|
+
writer.setNodes([{ id: 'v-003', kind: 'spec', label: 'r', status: 'pending' }]);
|
|
137
|
+
writer.markStart('v-003');
|
|
138
|
+
writer.markEnd('v-003', { status: 'done', durationMs: 50, findings });
|
|
139
|
+
const out = await writer.flush();
|
|
140
|
+
const body = await fs.readFile(out, 'utf8');
|
|
141
|
+
const weaknessCell = body.match(/\|\s*pass\s*\|\s*(\d\.\d{2})\s*\|/);
|
|
142
|
+
expect(weaknessCell).not.toBeNull();
|
|
143
|
+
const weaknessValue = parseFloat(weaknessCell[1]);
|
|
144
|
+
expect(weaknessValue).toBeLessThan(0.5);
|
|
145
|
+
});
|
|
146
|
+
it('surfaces a deps column in the Wave table (root nodes show empty)', async () => {
|
|
147
|
+
const writer = new WorkbenchWriter({ cwd: tmp, graphId: 'g-deps', goal: 'x' });
|
|
148
|
+
writer.setNodes([
|
|
149
|
+
{ id: 't-001', kind: 'explore', label: 'a', status: 'pending' },
|
|
150
|
+
{ id: 't-002', kind: 'general', label: 'b', status: 'pending' },
|
|
151
|
+
]);
|
|
152
|
+
writer.markStart('t-001');
|
|
153
|
+
writer.markEnd('t-001', { status: 'done', durationMs: 10 });
|
|
154
|
+
writer.markStart('t-002');
|
|
155
|
+
writer.markEnd('t-002', {
|
|
156
|
+
status: 'done',
|
|
157
|
+
durationMs: 20,
|
|
158
|
+
deps: ['t-001'],
|
|
159
|
+
});
|
|
160
|
+
const out = await writer.flush();
|
|
161
|
+
const body = await fs.readFile(out, 'utf8');
|
|
162
|
+
// Header has the new deps column.
|
|
163
|
+
expect(body).toMatch(/\|\s*id\s*\|\s*label\s*\|\s*kind\s*\|\s*deps\s*\|/);
|
|
164
|
+
// Root node: empty deps cell.
|
|
165
|
+
expect(body).toMatch(/\|\s*t-001\s*\|[^|]+\|\s*explore\s*\|\s*\|/);
|
|
166
|
+
// Dependent node: deps cell lists upstream id.
|
|
167
|
+
expect(body).toMatch(/\|\s*t-002\s*\|[^|]+\|\s*general\s*\|\s*t-001\s*\|/);
|
|
168
|
+
});
|
|
169
|
+
it('lists multi-dep nodes comma-separated', async () => {
|
|
170
|
+
const writer = new WorkbenchWriter({ cwd: tmp, graphId: 'g-mdep', goal: 'x' });
|
|
171
|
+
writer.setNodes([
|
|
172
|
+
{ id: 'a', kind: 'explore', label: 'a', status: 'pending' },
|
|
173
|
+
{ id: 'b', kind: 'explore', label: 'b', status: 'pending' },
|
|
174
|
+
{ id: 'c', kind: 'general', label: 'c', status: 'pending' },
|
|
175
|
+
]);
|
|
176
|
+
writer.markStart('a');
|
|
177
|
+
writer.markEnd('a', { status: 'done', durationMs: 5 });
|
|
178
|
+
writer.markStart('b');
|
|
179
|
+
writer.markEnd('b', { status: 'done', durationMs: 5 });
|
|
180
|
+
writer.markStart('c');
|
|
181
|
+
writer.markEnd('c', { status: 'done', durationMs: 5, deps: ['a', 'b'] });
|
|
182
|
+
const out = await writer.flush();
|
|
183
|
+
const body = await fs.readFile(out, 'utf8');
|
|
184
|
+
expect(body).toMatch(/\|\s*c\s*\|[^|]+\|\s*general\s*\|\s*a,\s*b\s*\|/);
|
|
185
|
+
});
|
|
186
|
+
it('marks a parallel wave', async () => {
|
|
187
|
+
const w = new WorkbenchWriter({ cwd: tmp, graphId: 'g-5', goal: 'x' });
|
|
188
|
+
w.markWave(['t0001', 't0002', 't0003']);
|
|
189
|
+
const out = await w.flush();
|
|
190
|
+
const body = await fs.readFile(out, 'utf8');
|
|
191
|
+
expect(body).toMatch(/wave: t0001, t0002, t0003/);
|
|
192
|
+
});
|
|
193
|
+
it('preserves running status across setNodes (does NOT reset to pending)', async () => {
|
|
194
|
+
const w = new WorkbenchWriter({ cwd: tmp, graphId: 'g-6', goal: 'x' });
|
|
195
|
+
w.setNodes([{ id: 't0001', kind: 'general', label: 'do', status: 'pending' }]);
|
|
196
|
+
w.markStart('t0001');
|
|
197
|
+
// A second setNodes call shouldn't reset the running state.
|
|
198
|
+
w.setNodes([{ id: 't0001', kind: 'general', label: 'do', status: 'pending' }]);
|
|
199
|
+
const out = await w.flush();
|
|
200
|
+
const body = await fs.readFile(out, 'utf8');
|
|
201
|
+
expect(body).toContain('↑'); // running emoji
|
|
202
|
+
});
|
|
203
|
+
it('is a no-op when disabled', async () => {
|
|
204
|
+
const w = new WorkbenchWriter({ cwd: tmp, graphId: 'g-7', goal: 'x', enabled: false });
|
|
205
|
+
w.setNodes([{ id: 't0001', kind: 'explore', label: 'x', status: 'pending' }]);
|
|
206
|
+
w.markStart('t0001');
|
|
207
|
+
const out = await w.flush();
|
|
208
|
+
expect(out).toBeNull();
|
|
209
|
+
// No file was written.
|
|
210
|
+
const file = path.join(tmp, '.zelari', 'radio', 'workbench-g-7.md');
|
|
211
|
+
await expect(fs.access(file)).rejects.toThrow();
|
|
212
|
+
});
|
|
213
|
+
it('rewrites atomically (no partial state observable on tail)', async () => {
|
|
214
|
+
const w = new WorkbenchWriter({ cwd: tmp, graphId: 'g-8', goal: 'x', debounceMs: 0 });
|
|
215
|
+
w.setNodes([{ id: 't0001', kind: 'explore', label: 'a', status: 'pending' }]);
|
|
216
|
+
await w.flush();
|
|
217
|
+
w.markStart('t0001');
|
|
218
|
+
w.markEnd('t0001', { status: 'done', durationMs: 50 });
|
|
219
|
+
// Two flushes in quick succession: each must produce a complete file.
|
|
220
|
+
await w.flush();
|
|
221
|
+
const body = await fs.readFile(path.join(tmp, '.zelari', 'radio', 'workbench-g-8.md'), 'utf8');
|
|
222
|
+
expect(body).toMatch(/✓/); // last write's status
|
|
223
|
+
// No .tmp files left over.
|
|
224
|
+
const dir = path.join(tmp, '.zelari', 'radio');
|
|
225
|
+
const files = await fs.readdir(dir);
|
|
226
|
+
const tmps = files.filter((f) => f.endsWith('.tmp'));
|
|
227
|
+
expect(tmps).toEqual([]);
|
|
228
|
+
});
|
|
229
|
+
it('close() cancels the pending debounced write', async () => {
|
|
230
|
+
const w = new WorkbenchWriter({ cwd: tmp, graphId: 'g-9', goal: 'x', debounceMs: 1000 });
|
|
231
|
+
w.logEvent('will never land');
|
|
232
|
+
w.close();
|
|
233
|
+
await new Promise((r) => setTimeout(r, 1100));
|
|
234
|
+
const file = path.join(tmp, '.zelari', 'radio', 'workbench-g-9.md');
|
|
235
|
+
await expect(fs.access(file)).rejects.toThrow();
|
|
236
|
+
});
|
|
237
|
+
it('handles a graph with many nodes by truncating the table', async () => {
|
|
238
|
+
const w = new WorkbenchWriter({ cwd: tmp, graphId: 'g-10', goal: 'x' });
|
|
239
|
+
const nodes = [];
|
|
240
|
+
for (let i = 0; i < 250; i++) {
|
|
241
|
+
nodes.push({ id: `t${String(i).padStart(4, '0')}`, kind: 'explore', label: `n${i}`, status: 'pending' });
|
|
242
|
+
}
|
|
243
|
+
w.setNodes(nodes);
|
|
244
|
+
const out = await w.flush();
|
|
245
|
+
const body = await fs.readFile(out, 'utf8');
|
|
246
|
+
// The table caps at 200 rows. (We don't assert exact count, just that
|
|
247
|
+
// it's not 250 — the truncated set is present and the rest is gone.)
|
|
248
|
+
const tableRows = body.split('\n').filter((l) => l.startsWith('| t')).length;
|
|
249
|
+
expect(tableRows).toBeLessThanOrEqual(200);
|
|
250
|
+
expect(tableRows).toBeGreaterThan(0);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
//# sourceMappingURL=workbench.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workbench.test.js","sourceRoot":"","sources":["../../../src/cli/kraken/workbench.test.ts"],"names":[],"mappings":"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"}
|