zelari-code 1.28.0 → 1.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/cli/headless.js +14 -0
  2. package/dist/cli/headless.js.map +1 -1
  3. package/dist/cli/hooks/useSlashDispatch.js +11 -0
  4. package/dist/cli/hooks/useSlashDispatch.js.map +1 -1
  5. package/dist/cli/kraken/executor.js +725 -42
  6. package/dist/cli/kraken/executor.js.map +1 -1
  7. package/dist/cli/kraken/graphMemory.js +27 -5
  8. package/dist/cli/kraken/graphMemory.js.map +1 -1
  9. package/dist/cli/kraken/graphStatus.js +59 -0
  10. package/dist/cli/kraken/graphStatus.js.map +1 -1
  11. package/dist/cli/kraken/planner.js +189 -12
  12. package/dist/cli/kraken/planner.js.map +1 -1
  13. package/dist/cli/kraken/planner.test.js +43 -0
  14. package/dist/cli/kraken/planner.test.js.map +1 -0
  15. package/dist/cli/kraken/runtime/compile.js +73 -0
  16. package/dist/cli/kraken/runtime/compile.js.map +1 -0
  17. package/dist/cli/kraken/runtime/runScriptPlan.js +195 -0
  18. package/dist/cli/kraken/runtime/runScriptPlan.js.map +1 -0
  19. package/dist/cli/kraken/scriptPlanner.js +286 -0
  20. package/dist/cli/kraken/scriptPlanner.js.map +1 -0
  21. package/dist/cli/kraken/scriptPlanner.test.js +152 -0
  22. package/dist/cli/kraken/scriptPlanner.test.js.map +1 -0
  23. package/dist/cli/kraken/skillSuggest.js +97 -0
  24. package/dist/cli/kraken/skillSuggest.js.map +1 -0
  25. package/dist/cli/kraken/skillSuggest.test.js +157 -0
  26. package/dist/cli/kraken/skillSuggest.test.js.map +1 -0
  27. package/dist/cli/kraken/weaknessMeter.js +183 -0
  28. package/dist/cli/kraken/weaknessMeter.js.map +1 -0
  29. package/dist/cli/kraken/weaknessMeter.test.js +212 -0
  30. package/dist/cli/kraken/weaknessMeter.test.js.map +1 -0
  31. package/dist/cli/kraken/workbench.js +296 -0
  32. package/dist/cli/kraken/workbench.js.map +1 -0
  33. package/dist/cli/kraken/workbench.test.js +253 -0
  34. package/dist/cli/kraken/workbench.test.js.map +1 -0
  35. package/dist/cli/kraken/workbenchView.js +155 -0
  36. package/dist/cli/kraken/workbenchView.js.map +1 -0
  37. package/dist/cli/kraken/workbenchView.test.js +130 -0
  38. package/dist/cli/kraken/workbenchView.test.js.map +1 -0
  39. package/dist/cli/main.bundled.js +2930 -353
  40. package/dist/cli/main.bundled.js.map +4 -4
  41. package/dist/cli/runHeadless.js +83 -8
  42. package/dist/cli/runHeadless.js.map +1 -1
  43. package/dist/cli/slashCommands.js +16 -0
  44. package/dist/cli/slashCommands.js.map +1 -1
  45. package/dist/cli/slashHandlers/krakenFanout.js +200 -0
  46. package/dist/cli/slashHandlers/krakenFanout.js.map +1 -0
  47. package/dist/cli/slashHandlers/krakenGraph.js +10 -3
  48. package/dist/cli/slashHandlers/krakenGraph.js.map +1 -1
  49. package/dist/cli/slashHandlers/krakenWorkbench.js +49 -0
  50. package/dist/cli/slashHandlers/krakenWorkbench.js.map +1 -0
  51. package/dist/cli/tools/krakenCsvFanout.js +260 -0
  52. package/dist/cli/tools/krakenCsvFanout.js.map +1 -0
  53. package/dist/cli/tools/krakenCsvFanout.test.js +200 -0
  54. package/dist/cli/tools/krakenCsvFanout.test.js.map +1 -0
  55. package/dist/cli/tools/krakenModel.js +32 -0
  56. package/dist/cli/tools/krakenModel.js.map +1 -1
  57. package/dist/cli/tools/krakenRadio.js.map +1 -1
  58. package/dist/cli/tools/taskTool.js +2 -2
  59. package/dist/cli/tools/taskTool.js.map +1 -1
  60. 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":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAsB,MAAM,gBAAgB,CAAC;AAEzF,IAAI,GAAG,GAAG,EAAE,CAAC;AACb,IAAI,OAAO,GAAG,CAAC,CAAC;AAEhB,KAAK,UAAU,QAAQ;IACrB,MAAM,CAAC,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,mBAAmB,CAAC,CAAC,CAAC;IACxE,OAAO,IAAI,CAAC,CAAC;IACb,OAAO,CAAC,CAAC;AACX,CAAC;AAED,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,EAAE,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAC1B,MAAM,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,CAAC,kBAAkB,CAAC,EAAE,uBAAuB,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,CAAC,kBAAkB,CAAC,EAAE,uBAAuB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1E,MAAM,CAAC,kBAAkB,CAAC,EAAE,uBAAuB,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7E,MAAM,CAAC,kBAAkB,CAAC,EAAE,uBAAuB,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,GAAG,GAAG,MAAM,QAAQ,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,CAAC,GAAG,IAAI,eAAe,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,CAAC;QACnF,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;QAChF,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC;QAC5B,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC;QACzE,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAI,EAAE,MAAM,CAAC,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;QAC9C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,2BAA2B,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;QACxD,MAAM,CAAC,GAAG,IAAI,eAAe,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACvE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;QACvG,CAAC,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC;QAC/C,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAI,EAAE,MAAM,CAAC,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,aAAa;QAC1C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,qBAAqB;QACrD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;QACrD,MAAM,CAAC,GAAG,IAAI,eAAe,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACvE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE;YAAE,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACtD,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAI,EAAE,MAAM,CAAC,CAAC;QAC7C,wDAAwD;QACxD,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACxC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,CAAC,GAAG,IAAI,eAAe,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACvE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;QAC/E,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACrB,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;QAC1E,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAI,EAAE,MAAM,CAAC,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QAC5B,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,KAAK,IAAI,EAAE;QACnF,qEAAqE;QACrE,sEAAsE;QACtE,kEAAkE;QAClE,4DAA4D;QAC5D,MAAM,CAAC,GAAG,IAAI,eAAe,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACzE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;QAClF,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACrB,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE;YACjB,MAAM,EAAE,MAAM;YACd,UAAU,EAAE,GAAG;YACf,QAAQ,EAAE,kFAAkF;SAC7F,CAAC,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAI,EAAE,MAAM,CAAC,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACpC,4DAA4D;QAC5D,6DAA6D;QAC7D,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,6GAA6G,CAAC,CAAC;QACpI,2BAA2B;QAC3B,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,uEAAuE,CAAC,CAAC;QAC9F,4CAA4C;QAC5C,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,wCAAwC,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wEAAwE,EAAE,KAAK,IAAI,EAAE;QACtF,uEAAuE;QACvE,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QAChF,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;QAClF,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAC1B,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE;YACtB,MAAM,EAAE,MAAM;YACd,UAAU,EAAE,EAAE;YACd,QAAQ,EAAE,8BAA8B;SACzC,CAAC,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAI,EAAE,MAAM,CAAC,CAAC;QAC7C,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;QACrE,MAAM,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QACpC,MAAM,aAAa,GAAG,UAAU,CAAC,YAAa,CAAC,CAAC,CAAC,CAAC,CAAC;QACnD,MAAM,CAAC,aAAa,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4EAA4E,EAAE,KAAK,IAAI,EAAE;QAC1F,yEAAyE;QACzE,MAAM,QAAQ,GAAG;YACf,8EAA8E;YAC9E,2CAA2C;YAC3C,EAAE;YACF,eAAe;SAChB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACb,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QAChF,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;QAChF,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAC1B,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;QACtE,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAI,EAAE,MAAM,CAAC,CAAC;QAC7C,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;QACrE,MAAM,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QACpC,MAAM,aAAa,GAAG,UAAU,CAAC,YAAa,CAAC,CAAC,CAAC,CAAC,CAAC;QACnD,MAAM,CAAC,aAAa,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;QAChF,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QAC/E,MAAM,CAAC,QAAQ,CAAC;YACd,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE;YAC/D,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE;SAChE,CAAC,CAAC;QACH,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAC1B,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC;QAC5D,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAC1B,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE;YACtB,MAAM,EAAE,MAAM;YACd,UAAU,EAAE,EAAE;YACd,IAAI,EAAE,CAAC,OAAO,CAAC;SAChB,CAAC,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAI,EAAE,MAAM,CAAC,CAAC;QAC7C,kCAAkC;QAClC,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,mDAAmD,CAAC,CAAC;QAC1E,8BAA8B;QAC9B,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,4CAA4C,CAAC,CAAC;QACnE,+CAA+C;QAC/C,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,oDAAoD,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;QACrD,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QAC/E,MAAM,CAAC,QAAQ,CAAC;YACd,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE;YAC3D,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE;YAC3D,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE;SAC5D,CAAC,CAAC;QACH,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACtB,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;QACvD,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACtB,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;QACvD,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACtB,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QACzE,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAI,EAAE,MAAM,CAAC,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,iDAAiD,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EAAE,KAAK,IAAI,EAAE;QACrC,MAAM,CAAC,GAAG,IAAI,eAAe,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACvE,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;QACxC,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAI,EAAE,MAAM,CAAC,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;QACpF,MAAM,CAAC,GAAG,IAAI,eAAe,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACvE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;QAC/E,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACrB,4DAA4D;QAC5D,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;QAC/E,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAI,EAAE,MAAM,CAAC,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,gBAAgB;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;QACxC,MAAM,CAAC,GAAG,IAAI,eAAe,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QACvF,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;QAC9E,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACrB,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC;QAC5B,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC;QACvB,uBAAuB;QACvB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC;QACpE,MAAM,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;QACzE,MAAM,CAAC,GAAG,IAAI,eAAe,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;QACtF,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;QAC9E,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACrB,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC;QACvD,sEAAsE;QACtE,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC;QAChB,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,kBAAkB,CAAC,EAAE,MAAM,CAAC,CAAC;QAC/F,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,sBAAsB;QACjD,2BAA2B;QAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;QAC/C,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QACrD,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC3B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;QAC3D,MAAM,CAAC,GAAG,IAAI,eAAe,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QACzF,CAAC,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC;QAC9B,CAAC,CAAC,KAAK,EAAE,CAAC;QACV,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC;QACpE,MAAM,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,KAAK,IAAI,EAAE;QACvE,MAAM,CAAC,GAAG,IAAI,eAAe,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACxE,MAAM,KAAK,GAAoB,EAAE,CAAC;QAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7B,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;QAC3G,CAAC;QACD,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAClB,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAI,EAAE,MAAM,CAAC,CAAC;QAC7C,sEAAsE;QACtE,qEAAqE;QACrE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;QAC7E,MAAM,CAAC,SAAS,CAAC,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC;QAC3C,MAAM,CAAC,SAAS,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}