@linzumi/cli 1.0.110 → 1.0.112

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.
@@ -0,0 +1,219 @@
1
+ // Spec: kandan/plans/2026-07-10-compute-nodes-rescript-commander-zero-ts-program.md
2
+ // (M3 cluster 10, commander core / dispatch input queue).
3
+ // Relationship: Builds the TS-side PARITY ORACLE for the per-session
4
+ // dispatch input queue of the ReScript commander port, the sibling of
5
+ // build-pipeline-parity-oracle.mjs (cluster 9, which owns the two recovery
6
+ // watchdog primitives this queue's predicates feed). The oracle bundles the
7
+ // REAL runner.ts exports - createClaudeCodeInputQueue (the streaming-input
8
+ // FIFO with the single-live-turn park/release discipline, the #2589 honest
9
+ // enqueue disposition, the #2590 steer-application tracking, and the
10
+ // BLOCKER A dispatch-confirm watermark seam), claudeCodeTurnStallThresholdMs
11
+ // (the LINZUMI_CLAUDE_TURN_STALL_MS env resolution), and
12
+ // claudeCodeDispatchTerminalFailureReason (the kill-respawn-retry ladder's
13
+ // byte-frozen terminal copy) - behind the cluster 1/2 one-shot BATCH driver
14
+ // (a JSON array of cases on stdin, a JSON array of results on stdout).
15
+ //
16
+ // Byte parity: every trace event (dispatches with their payload bytes,
17
+ // predicate refusals, park positions, watermark commits, steer outcomes and
18
+ // applied resolutions, closed-input errors) is normalized by THIS driver
19
+ // with pinned key order, and delivered message payloads ride the TS message
20
+ // objects VERBATIM (their own key insertion order), so the ReScript side
21
+ // must reproduce field presence AND insertion order exactly.
22
+ //
23
+ // Determinism: the queue owns no timers and no clock - its only
24
+ // nondeterminism is promise scheduling, which the driver serializes by
25
+ // draining the microtask queue after every op (pull resolutions and steer
26
+ // `applied` resolutions land in trace order deterministically). Scripted
27
+ // deps: the isLiveSteerAllowed predicate is a mutable flag flipped via ops;
28
+ // the delivery/wait/watermark callbacks append trace events.
29
+ //
30
+ // Import-time side effects: runner.ts transitively reads
31
+ // src/assets/linzumi-logo.svg relative to the bundle at import time
32
+ // (helloLinzumiProject.ts), so the asset is copied next to the bundle -
33
+ // the build-harness-codex-parity-oracle.mjs convention. Externals match
34
+ // the existing oracles (ws/undici/blessed/claude-agent-sdk stay external;
35
+ // none of them is touched at import time on this driver's paths).
36
+ //
37
+ // SECRETS: the oracle only ever sees the conformance suite's synthetic
38
+ // payloads. The output is a local test artifact (.differential/ is
39
+ // gitignored, never published) and stays unminified for debuggability.
40
+ import { copyFile, mkdir } from 'node:fs/promises';
41
+ import { dirname, join } from 'node:path';
42
+ import { fileURLToPath } from 'node:url';
43
+ import { build } from 'esbuild';
44
+
45
+ const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
46
+
47
+ const driverSource = `
48
+ import {
49
+ claudeCodeDispatchTerminalFailureReason,
50
+ claudeCodeTurnStallThresholdMs,
51
+ createClaudeCodeInputQueue,
52
+ } from './src/runner';
53
+
54
+ // Deterministic promise settlement: every queue-internal resolution is
55
+ // synchronous, so a bounded microtask drain after each op flushes every
56
+ // pending .then into the trace in resolution order.
57
+ async function drainMicrotasks() {
58
+ for (let tick = 0; tick < 8; tick += 1) {
59
+ await Promise.resolve();
60
+ }
61
+ }
62
+
63
+ async function runInputQueueCase(caseInput) {
64
+ const trace = [];
65
+ let liveSteerAllowed = true;
66
+ let steerCount = 0;
67
+ let pullCount = 0;
68
+ const queue = createClaudeCodeInputQueue({
69
+ content: caseInput.content,
70
+ sourceSeq: caseInput.sourceSeq ?? undefined,
71
+ onInputDelivered: () => trace.push({ t: 'inputDelivered' }),
72
+ isLiveSteerAllowed:
73
+ caseInput.liveSteerMode === 'scripted' ? () => liveSteerAllowed : undefined,
74
+ onInputWait: (currentSourceSeq) =>
75
+ trace.push({ t: 'inputWait', seq: currentSourceSeq ?? null }),
76
+ onSourceSeqDelivered: (sourceSeq) => trace.push({ t: 'watermark', seq: sourceSeq }),
77
+ });
78
+ const iterator = queue.messages[Symbol.asyncIterator]();
79
+ for (const op of caseInput.ops ?? []) {
80
+ if (op.op === 'enqueue') {
81
+ try {
82
+ const disposition = queue.enqueue({
83
+ content: op.content,
84
+ sourceSeq: op.sourceSeq ?? undefined,
85
+ });
86
+ trace.push({
87
+ t: 'enqueue',
88
+ result:
89
+ disposition.disposition === 'parked'
90
+ ? { disposition: 'parked', queuePosition: disposition.queuePosition }
91
+ : { disposition: 'delivered' },
92
+ });
93
+ } catch (error) {
94
+ trace.push({
95
+ t: 'enqueue',
96
+ error: error instanceof Error ? error.message : String(error),
97
+ });
98
+ }
99
+ } else if (op.op === 'enqueueSteer') {
100
+ steerCount += 1;
101
+ const steerId = steerCount;
102
+ try {
103
+ const outcome = queue.enqueueSteer(op.content);
104
+ trace.push({ t: 'steer', steerId, steered: outcome.steered });
105
+ void outcome.applied.then((applied) =>
106
+ trace.push({ t: 'steerApplied', steerId, applied })
107
+ );
108
+ } catch (error) {
109
+ trace.push({
110
+ t: 'steer',
111
+ steerId,
112
+ error: error instanceof Error ? error.message : String(error),
113
+ });
114
+ }
115
+ } else if (op.op === 'next') {
116
+ pullCount += 1;
117
+ const pullId = pullCount;
118
+ trace.push({ t: 'pullStart', pullId });
119
+ void iterator.next().then((result) =>
120
+ trace.push(
121
+ result.done === true
122
+ ? { t: 'pulled', pullId, done: true }
123
+ : { t: 'pulled', pullId, done: false, message: result.value }
124
+ )
125
+ );
126
+ } else if (op.op === 'completeTurn') {
127
+ trace.push({ t: 'completeTurn', completed: queue.completeTurn() ?? null });
128
+ } else if (op.op === 'currentSourceSeq') {
129
+ trace.push({ t: 'currentSourceSeq', seq: queue.currentSourceSeq() ?? null });
130
+ } else if (op.op === 'close') {
131
+ queue.close();
132
+ trace.push({ t: 'closed' });
133
+ } else if (op.op === 'drainUndelivered') {
134
+ trace.push({
135
+ t: 'drained',
136
+ followUps: queue.drainUndeliveredFollowUps().map((followUp) => ({
137
+ message: followUp.message,
138
+ sourceSeq: followUp.sourceSeq ?? null,
139
+ })),
140
+ });
141
+ } else if (op.op === 'setLiveSteerAllowed') {
142
+ liveSteerAllowed = op.value === true;
143
+ }
144
+ await drainMicrotasks();
145
+ }
146
+ await drainMicrotasks();
147
+ return { trace };
148
+ }
149
+
150
+ async function runCase(input) {
151
+ switch (input.kind) {
152
+ case 'constants':
153
+ return {
154
+ claudeCodeDispatchTerminalFailureReason,
155
+ };
156
+ case 'turnStallThresholdMs':
157
+ return claudeCodeTurnStallThresholdMs(input.env ?? {});
158
+ case 'inputQueueRun':
159
+ return runInputQueueCase(input);
160
+ default:
161
+ return { error: 'unknown case kind ' + String(input.kind) };
162
+ }
163
+ }
164
+
165
+ async function runBatch() {
166
+ const chunks = [];
167
+ for await (const chunk of process.stdin) chunks.push(chunk);
168
+ const cases = JSON.parse(Buffer.concat(chunks).toString('utf8'));
169
+ const results = [];
170
+ for (const testCase of cases) {
171
+ results.push(await runCase(testCase));
172
+ }
173
+ process.stdout.write(JSON.stringify(results), () => {
174
+ process.exit(0);
175
+ });
176
+ }
177
+
178
+ runBatch().then(
179
+ () => {},
180
+ (error) => {
181
+ process.stderr.write(String(error && error.stack ? error.stack : error) + '\\n');
182
+ process.exit(1);
183
+ }
184
+ );
185
+ `;
186
+
187
+ const outDir = join(packageRoot, '.differential');
188
+ const outPath = join(outDir, 'core-dispatch-input-queue-parity-oracle.mjs');
189
+
190
+ await mkdir(outDir, { recursive: true });
191
+ // runner.ts transitively reads this asset relative to the bundle at import
192
+ // time (helloLinzumiProject.ts readFileSync), so it must exist next to the
193
+ // oracle bundle.
194
+ await mkdir(join(outDir, 'assets'), { recursive: true });
195
+ await copyFile(
196
+ join(packageRoot, 'src/assets/linzumi-logo.svg'),
197
+ join(outDir, 'assets/linzumi-logo.svg')
198
+ );
199
+
200
+ await build({
201
+ stdin: {
202
+ contents: driverSource,
203
+ resolveDir: packageRoot,
204
+ sourcefile: 'core-dispatch-input-queue-parity-driver.ts',
205
+ loader: 'ts',
206
+ },
207
+ bundle: true,
208
+ platform: 'node',
209
+ target: 'node20',
210
+ format: 'esm',
211
+ outfile: outPath,
212
+ minify: false,
213
+ sourcemap: false,
214
+ legalComments: 'none',
215
+ logLevel: 'silent',
216
+ external: ['ws', 'undici', 'blessed', '@anthropic-ai/claude-agent-sdk'],
217
+ });
218
+
219
+ process.stdout.write(outPath + '\n');
@@ -0,0 +1,250 @@
1
+ // Spec: kandan/plans/2026-07-10-compute-nodes-rescript-commander-zero-ts-program.md
2
+ // (M3 cluster 10, commander core).
3
+ // Relationship: Builds the TS-side PARITY ORACLE for the fate-ladder policy
4
+ // engine of the ReScript commander port, the sibling of
5
+ // build-pipeline-parity-oracle.mjs (cluster 9). The oracle bundles the REAL
6
+ // TS module (src/fateLadder.ts - the F2/D51/D53 policy engine: the policy
7
+ // table, the budget env resolution, the run/history record, the one
8
+ // decision entry point, the hold-dispatch commit, the visible-reason
9
+ // strings, the F4 wire receipts, and the rate-limit reset capture) behind
10
+ // the cluster 1/2 one-shot BATCH driver (a JSON array of cases on stdin, a
11
+ // JSON array of results on stdout).
12
+ //
13
+ // Byte parity: fateLadderWireHistory receipts are returned PRE-STRINGIFIED
14
+ // (the TS object's own key insertion order rides JSON.stringify), and every
15
+ // decision/run is serialized through one canonical shape both sides
16
+ // reproduce field-for-field. Determinism: clocks and budgets are injected
17
+ // per case op, the breaker gate is scripted per decide op (its calls are
18
+ // traced so the consult discipline is pinned too) - nothing here reads wall
19
+ // time or process.env except through case inputs.
20
+ //
21
+ // SECRETS: the oracle only ever sees the conformance suite's synthetic
22
+ // payloads. The output is a local test artifact (.differential/ is
23
+ // gitignored, never published) and stays unminified for debuggability.
24
+ import { mkdir } from 'node:fs/promises';
25
+ import { dirname, join } from 'node:path';
26
+ import { fileURLToPath } from 'node:url';
27
+ import { build } from 'esbuild';
28
+
29
+ const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
30
+
31
+ const driverSource = `
32
+ import {
33
+ appendFateLadderEntry,
34
+ commitFateLadderHoldDispatch,
35
+ createFateLadderRun,
36
+ decideFateLadderStep,
37
+ defaultFateLadderBudgets,
38
+ fateLadderBudgets,
39
+ fateLadderPolicies,
40
+ fateLadderRungs,
41
+ fateLadderWireHistory,
42
+ inPlaceResumeMaxAttempts,
43
+ maxFateLadderHistoryEntries,
44
+ rateLimitHoldReason,
45
+ rateLimitHoldReasonPrefix,
46
+ rateLimitResetAtMsFromNotification,
47
+ redispatchAfterLocalFailureReason,
48
+ workerRebuildRetryReason,
49
+ } from './src/fateLadder';
50
+
51
+ // Canonical run/decision serialization shared (by construction) with the
52
+ // ReScript conformance: fixed key order, option fields as null.
53
+ function runJson(run) {
54
+ return {
55
+ fate: run.fate,
56
+ startedAtMs: run.startedAtMs,
57
+ rungCursor: run.rungCursor,
58
+ cursorAttempts: run.cursorAttempts,
59
+ recoveryRungsAttempted: run.recoveryRungsAttempted,
60
+ holdCommitted: run.holdCommitted,
61
+ holdDispatched: run.holdDispatched,
62
+ historyTruncated: run.historyTruncated,
63
+ entries: run.entries.map((entry) => ({
64
+ rung: entry.rung,
65
+ outcome: entry.outcome,
66
+ atMs: entry.atMs,
67
+ detail: entry.detail === undefined ? null : entry.detail,
68
+ resetAtMs: entry.resetAtMs === undefined ? null : entry.resetAtMs,
69
+ })),
70
+ };
71
+ }
72
+
73
+ function decisionJson(decision) {
74
+ switch (decision.kind) {
75
+ case 'stand':
76
+ return { kind: 'stand', cause: decision.cause };
77
+ case 'execute_rung':
78
+ return { kind: 'execute_rung', rung: decision.rung, run: runJson(decision.run) };
79
+ case 'hold_until_reset':
80
+ return {
81
+ kind: 'hold_until_reset',
82
+ resetAtMs: decision.resetAtMs,
83
+ run: runJson(decision.run),
84
+ };
85
+ case 'breaker_hold':
86
+ return { kind: 'breaker_hold', reason: decision.reason, run: runJson(decision.run) };
87
+ case 'red':
88
+ return { kind: 'red', cause: decision.cause, run: runJson(decision.run) };
89
+ default:
90
+ return { kind: 'unknown' };
91
+ }
92
+ }
93
+
94
+ function budgetsOf(raw) {
95
+ return raw === undefined
96
+ ? defaultFateLadderBudgets
97
+ : {
98
+ maxRecoveryRungs: raw.maxRecoveryRungs,
99
+ maxLadderMs: raw.maxLadderMs,
100
+ maxRateLimitHoldMs: raw.maxRateLimitHoldMs,
101
+ };
102
+ }
103
+
104
+ // One scripted per-turn schedule: ops drive the pure engine exactly the way
105
+ // the session layer would (decide -> store returned run -> dispatch), with
106
+ // the breaker verdict scripted per decide op and every gate call traced.
107
+ function runScheduleCase(caseInput) {
108
+ let current = undefined;
109
+ const steps = [];
110
+ for (const op of caseInput.ops ?? []) {
111
+ if (op.op === 'decide') {
112
+ const breakerCalls = [];
113
+ const scripted = op.breaker ?? { action: 'proceed' };
114
+ const gate = (fate, nowMs) => {
115
+ breakerCalls.push({ fate, nowMs });
116
+ if (scripted.action === 'hold') {
117
+ return { action: 'hold', reason: scripted.reason ?? 'scripted_hold' };
118
+ }
119
+ if (scripted.action === 'canary') {
120
+ return { action: 'canary' };
121
+ }
122
+ return { action: 'proceed' };
123
+ };
124
+ const decision = decideFateLadderStep({
125
+ fate: op.fate ?? undefined,
126
+ run: current,
127
+ nowMs: op.nowMs,
128
+ budgets: budgetsOf(op.budgets),
129
+ breaker: gate,
130
+ grants: {
131
+ inPlaceResume: op.grants?.inPlaceResume === true,
132
+ redispatch: op.grants?.redispatch === true,
133
+ workerRebuild: op.grants?.workerRebuild === true,
134
+ },
135
+ rateLimitResetAtMs: op.resetAtMs ?? undefined,
136
+ });
137
+ if (decision.kind !== 'stand') {
138
+ current = decision.run;
139
+ }
140
+ steps.push({ decision: decisionJson(decision), breakerCalls });
141
+ } else if (op.op === 'commitHoldDispatch') {
142
+ if (current === undefined) {
143
+ steps.push({ skipped: true });
144
+ } else {
145
+ current = commitFateLadderHoldDispatch(current, op.nowMs);
146
+ steps.push({ run: runJson(current) });
147
+ }
148
+ } else if (op.op === 'append') {
149
+ if (current === undefined) {
150
+ steps.push({ skipped: true });
151
+ } else {
152
+ current = appendFateLadderEntry(current, {
153
+ rung: op.entry.rung,
154
+ outcome: op.entry.outcome,
155
+ atMs: op.entry.atMs,
156
+ detail: op.entry.detail ?? undefined,
157
+ resetAtMs: op.entry.resetAtMs ?? undefined,
158
+ });
159
+ steps.push({ run: runJson(current) });
160
+ }
161
+ } else if (op.op === 'seed') {
162
+ current = createFateLadderRun(op.fate, op.nowMs);
163
+ steps.push({ run: runJson(current) });
164
+ } else if (op.op === 'wire') {
165
+ steps.push({
166
+ wire: JSON.stringify(fateLadderWireHistory(current, op.fate, budgetsOf(op.budgets))),
167
+ });
168
+ } else if (op.op === 'reset') {
169
+ current = undefined;
170
+ steps.push({ reset: true });
171
+ } else {
172
+ steps.push({ error: 'unknown op ' + String(op.op) });
173
+ }
174
+ }
175
+ return { steps };
176
+ }
177
+
178
+ function runCase(input) {
179
+ switch (input.kind) {
180
+ case 'constants':
181
+ return {
182
+ fateLadderRungs,
183
+ inPlaceResumeMaxAttempts,
184
+ fateLadderPolicies,
185
+ defaultFateLadderBudgets,
186
+ maxFateLadderHistoryEntries,
187
+ redispatchAfterLocalFailureReason,
188
+ workerRebuildRetryReason,
189
+ rateLimitHoldReasonPrefix,
190
+ };
191
+ case 'budgets':
192
+ return fateLadderBudgets(input.env ?? {});
193
+ case 'holdReason':
194
+ return rateLimitHoldReason(input.resetAtMs);
195
+ case 'resetFromNotification':
196
+ return rateLimitResetAtMsFromNotification(input.params) ?? null;
197
+ case 'scheduleRun':
198
+ return runScheduleCase(input);
199
+ default:
200
+ return { error: 'unknown case kind ' + String(input.kind) };
201
+ }
202
+ }
203
+
204
+ async function runBatch() {
205
+ const chunks = [];
206
+ for await (const chunk of process.stdin) chunks.push(chunk);
207
+ const cases = JSON.parse(Buffer.concat(chunks).toString('utf8'));
208
+ const results = [];
209
+ for (const testCase of cases) {
210
+ results.push(runCase(testCase));
211
+ }
212
+ process.stdout.write(JSON.stringify(results), () => {
213
+ process.exit(0);
214
+ });
215
+ }
216
+
217
+ runBatch().then(
218
+ () => {},
219
+ (error) => {
220
+ process.stderr.write(String(error && error.stack ? error.stack : error) + '\\n');
221
+ process.exit(1);
222
+ }
223
+ );
224
+ `;
225
+
226
+ const outDir = join(packageRoot, '.differential');
227
+ const outPath = join(outDir, 'core-fate-ladder-parity-oracle.mjs');
228
+
229
+ await mkdir(outDir, { recursive: true });
230
+
231
+ await build({
232
+ stdin: {
233
+ contents: driverSource,
234
+ resolveDir: packageRoot,
235
+ sourcefile: 'core-fate-ladder-parity-driver.ts',
236
+ loader: 'ts',
237
+ },
238
+ bundle: true,
239
+ platform: 'node',
240
+ target: 'node20',
241
+ format: 'esm',
242
+ outfile: outPath,
243
+ minify: false,
244
+ sourcemap: false,
245
+ legalComments: 'none',
246
+ logLevel: 'silent',
247
+ external: ['ws', 'undici', 'blessed', '@anthropic-ai/claude-agent-sdk'],
248
+ });
249
+
250
+ process.stdout.write(outPath + '\n');