@linzumi/cli 1.0.272 → 1.0.274

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 (44) hide show
  1. package/README.md +1 -1
  2. package/dist/impl-res/cloud-supervisor.mjs +69 -69
  3. package/dist/impl-res/index.js +355 -314
  4. package/dist/impl-res/mcp-server.mjs +34 -34
  5. package/dist/impl-ts/cloud-supervisor.mjs +69 -69
  6. package/dist/impl-ts/index.js +495 -495
  7. package/dist/impl-ts/mcp-server.mjs +1 -1
  8. package/dist/mcp-server.mjs +1 -1
  9. package/package.json +3 -2
  10. package/scripts/check-package-size-budget.mjs +9 -0
  11. package/scripts/build-auth-parity-oracle.mjs +0 -709
  12. package/scripts/build-canonical-codex-transpiler-oracle.mjs +0 -93
  13. package/scripts/build-config-identity-parity-oracle.mjs +0 -526
  14. package/scripts/build-core-commander-seams-parity-oracle.mjs +0 -799
  15. package/scripts/build-core-daemon-parity-oracle.mjs +0 -573
  16. package/scripts/build-core-dispatch-input-queue-parity-oracle.mjs +0 -225
  17. package/scripts/build-core-fate-ladder-parity-oracle.mjs +0 -350
  18. package/scripts/build-core-generation-overlap-parity-oracle.mjs +0 -292
  19. package/scripts/build-core-integration-parity-oracle.mjs +0 -453
  20. package/scripts/build-core-lifecycle-guards-parity-oracle.mjs +0 -443
  21. package/scripts/build-core-lock-parity-oracle.mjs +0 -462
  22. package/scripts/build-core-reconnect-resume-parity-oracle.mjs +0 -365
  23. package/scripts/build-core-startup-resume-scan-parity-oracle.mjs +0 -296
  24. package/scripts/build-core-supervisor-parity-oracle.mjs +0 -781
  25. package/scripts/build-cross-harness-failover-oracle.mjs +0 -349
  26. package/scripts/build-editor-parity-oracle.mjs +0 -1525
  27. package/scripts/build-forwarding-parity-oracle.mjs +0 -1390
  28. package/scripts/build-harness-claude-parity-oracle.mjs +0 -407
  29. package/scripts/build-harness-codex-parity-oracle.mjs +0 -479
  30. package/scripts/build-mcp-parity-oracle.mjs +0 -412
  31. package/scripts/build-mcp-selector-parity-oracle.mjs +0 -65
  32. package/scripts/build-onboarding-discovery-parity-oracle.mjs +0 -388
  33. package/scripts/build-onboarding-flow-parity-oracle.mjs +0 -562
  34. package/scripts/build-onboarding-gateway-parity-oracle.mjs +0 -313
  35. package/scripts/build-onboarding-support-parity-oracle.mjs +0 -519
  36. package/scripts/build-pipeline-parity-oracle.mjs +0 -614
  37. package/scripts/build-protocol-parity-oracle.mjs +0 -398
  38. package/scripts/build-queue-parity-oracle.mjs +0 -1012
  39. package/scripts/build-session-registry-parity-oracle.mjs +0 -582
  40. package/scripts/build-transport-parity-oracle.mjs +0 -694
  41. package/scripts/build-tui-parity-oracle.mjs +0 -577
  42. package/scripts/build-vm-sandbox-parity-oracle.mjs +0 -1281
  43. package/scripts/build-worker-entry-parity-oracle.mjs +0 -424
  44. package/scripts/build-worker-spawn-parity-oracle.mjs +0 -823
@@ -1,1012 +0,0 @@
1
- // Spec: kandan/plans/2026-07-10-compute-nodes-rescript-commander-zero-ts-program.md
2
- // (M3 cluster 5, queues-watermarks).
3
- // Relationship: Builds the TS-side PARITY ORACLE for the queues/watermarks
4
- // cluster of the ReScript commander port, the sibling of
5
- // build-config-identity-parity-oracle.mjs (cluster 2). The oracle bundles
6
- // the REAL TS queue/watermark modules (kandanQueue.ts,
7
- // pendingKandanMessageQueue.ts, appliedSourceSeqWatermark.ts,
8
- // durableInboundQueue.ts, controlQuarantine.ts, resumeKeystone.ts +
9
- // threadReconcilerLock.ts/leaseEpochGuard.ts, pipeline/outbox.ts +
10
- // pipeline/outboxJournal.ts) behind the cluster 1/2 one-shot batch driver:
11
- // a JSON array of cases on stdin, a JSON array of results on stdout. The
12
- // ReScript package's QueueParityConformance drives the SAME scripted
13
- // inputs through the ported modules in its own scratch directories and
14
- // asserts per-action outcome equivalence AND byte-identical durable files
15
- // (inbound-queue logs, commander-outbox journals, withheld-ack records,
16
- // quarantine evidence) - the cross-impl round-trip contract that lets both
17
- // commanders share ~/.linzumi through M4.
18
- //
19
- // Determinism: every source of nondeterminism is pinned per case - clocks
20
- // come from a fixed `nowMs` (the queue/outbox/keystone seams are all
21
- // injectable), scripted push outcomes resolve in order, and scratch paths
22
- // are normalized through the "$DIR" placeholder in both directions. Retry
23
- // delays in scripted outbox scenarios are millisecond-scale so a scenario
24
- // settles fast without changing the ladder's shape.
25
- //
26
- // SECRETS: the oracle only ever sees the conformance suite's synthetic
27
- // payloads; nothing here logs case contents.
28
- //
29
- // The output is a local test artifact (.differential/ is gitignored, never
30
- // published) and stays unminified for debuggability.
31
- import { mkdir } from 'node:fs/promises';
32
- import { dirname, join } from 'node:path';
33
- import { fileURLToPath } from 'node:url';
34
- import { build } from 'esbuild';
35
-
36
- const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
37
-
38
- const driverSource = `
39
- import {
40
- appendFileSync,
41
- mkdtempSync,
42
- mkdirSync,
43
- readdirSync,
44
- rmSync,
45
- readFileSync,
46
- realpathSync,
47
- renameSync,
48
- statSync,
49
- writeFileSync,
50
- } from 'node:fs';
51
- import { tmpdir } from 'node:os';
52
- import { join, relative } from 'node:path';
53
- import {
54
- codexInputForQueuedKandanMessage,
55
- formatQueuedKandanMessage,
56
- interruptQueuedMessages,
57
- normalizeCodexSteerInput,
58
- queuedKandanMessageSourceSeqs,
59
- steerInputText,
60
- } from './src/kandanQueue';
61
- import {
62
- createPendingKandanMessageQueue,
63
- dequeuePendingKandanMessage,
64
- drainAllPendingKandanMessages,
65
- enqueuePendingKandanMessage,
66
- interruptPendingKandanMessages,
67
- listPendingKandanMessages,
68
- pendingKandanMessageQueueHasSeq,
69
- pendingKandanMessageQueueLength,
70
- requeuePendingKandanMessageFront,
71
- } from './src/pendingKandanMessageQueue';
72
- import {
73
- commitAppliedSourceSeq,
74
- createInFlightSourceSeqGuard,
75
- highestProcessedSourceSeq,
76
- parseResumeReconcileEntries,
77
- parseServerMessageStateRows,
78
- reconcileAppliedSourceSeq,
79
- resumeReconcileServerRows,
80
- shouldApplySourceSeq,
81
- sourceSeqAppliedOrInFlight,
82
- threadSourceSeqWatermarkKey,
83
- } from './src/appliedSourceSeqWatermark';
84
- import {
85
- createDurableInboundThreadQueue,
86
- defaultDeadLetterPolicy,
87
- durableInboundQueuePath,
88
- foldQueueRecords,
89
- shouldDeadLetter,
90
- } from './src/durableInboundQueue';
91
- import {
92
- controlQuarantineFilePath,
93
- createControlQuarantine,
94
- fileWithheldControlAckStore,
95
- withheldControlAckStorePath,
96
- } from './src/controlQuarantine';
97
- import {
98
- outboxJournalPath,
99
- recoverOutboxJournal,
100
- createOutboxJournal,
101
- } from './src/pipeline/outboxJournal';
102
- import { createThreadOutbox } from './src/pipeline/outbox';
103
- import { createSessionPipelineBuilders, enqueueTerminalDebtRepair } from './src/pipeline/integration';
104
- import {
105
- applyResumeDispatchOutcome,
106
- commitLiveAcceptedSourceSeq,
107
- drainResumeQueue,
108
- receiveResumeMessage,
109
- } from './src/resumeKeystone';
110
- import { createLeaseEpochRegistry, isFencedClaim } from './src/leaseEpochGuard';
111
- import { createThreadReconcilerLock } from './src/threadReconcilerLock';
112
-
113
- // --- Placeholder + snapshot plumbing --------------------------------------------
114
-
115
- const dirPlaceholder = '$DIR';
116
-
117
- function scratch() {
118
- // realpath so macOS /var -> /private/var never splits the two impls'
119
- // placeholder substitution.
120
- return realpathSync(mkdtempSync(join(tmpdir(), 'linzumi-queue-oracle-')));
121
- }
122
-
123
- function substitute(value, from, to) {
124
- if (typeof value === 'string') {
125
- return value.split(from).join(to);
126
- }
127
- if (Array.isArray(value)) {
128
- return value.map((entry) => substitute(entry, from, to));
129
- }
130
- if (typeof value === 'object' && value !== null) {
131
- return Object.fromEntries(
132
- Object.entries(value).map(([key, entry]) => [
133
- substitute(key, from, to),
134
- substitute(entry, from, to),
135
- ])
136
- );
137
- }
138
- return value;
139
- }
140
-
141
- function snapshotFiles(dir) {
142
- const files = {};
143
- const walk = (current) => {
144
- for (const name of readdirSync(current).sort()) {
145
- const full = join(current, name);
146
- if (statSync(full).isDirectory()) {
147
- walk(full);
148
- } else {
149
- files[relative(dir, full)] = readFileSync(full, 'utf8');
150
- }
151
- }
152
- };
153
- walk(dir);
154
- return files;
155
- }
156
-
157
- function outcome(run) {
158
- try {
159
- const value = run();
160
- return value === undefined ? { defined: false } : { defined: true, value };
161
- } catch (error) {
162
- return {
163
- threw: true,
164
- message: error instanceof Error ? error.message : String(error),
165
- };
166
- }
167
- }
168
-
169
- function collectingLog(events, dir) {
170
- return (event, payload) => {
171
- events.push({ event, payload: substitute(payload, dir, dirPlaceholder) });
172
- };
173
- }
174
-
175
- // --- Queued-message pure transforms ------------------------------------------------
176
-
177
- function handleQueuedMessage(testCase) {
178
- switch (testCase.fn) {
179
- case 'normalizeCodexSteerInput':
180
- return outcome(() => normalizeCodexSteerInput(testCase.input));
181
- case 'steerInputText':
182
- return outcome(() => steerInputText(testCase.input));
183
- case 'format':
184
- return outcome(() => formatQueuedKandanMessage(testCase.message));
185
- case 'codexInput':
186
- return outcome(() => codexInputForQueuedKandanMessage(testCase.message));
187
- case 'sourceSeqs':
188
- return outcome(() => queuedKandanMessageSourceSeqs(testCase.message));
189
- case 'interrupt':
190
- return outcome(() =>
191
- interruptQueuedMessages(testCase.queue, testCase.throughSeq ?? undefined)
192
- );
193
- default:
194
- return { threw: true, message: 'unknown queuedMessage fn ' + String(testCase.fn) };
195
- }
196
- }
197
-
198
- // --- Pending FIFO ---------------------------------------------------------------------
199
-
200
- function handlePendingQueue(testCase) {
201
- const queue = createPendingKandanMessageQueue();
202
- const results = (testCase.actions ?? []).map((action) => {
203
- switch (action.fn) {
204
- case 'enqueue':
205
- return outcome(() => enqueuePendingKandanMessage(queue, action.message));
206
- case 'dequeue':
207
- return outcome(() => dequeuePendingKandanMessage(queue));
208
- case 'requeueFront':
209
- return outcome(() => requeuePendingKandanMessageFront(queue, action.message));
210
- case 'drainAll':
211
- return outcome(() => drainAllPendingKandanMessages(queue));
212
- case 'interrupt':
213
- return outcome(() =>
214
- interruptPendingKandanMessages(queue, action.throughSeq ?? undefined)
215
- );
216
- case 'hasSeq':
217
- return outcome(() => pendingKandanMessageQueueHasSeq(queue, action.seq));
218
- case 'list':
219
- return outcome(() => listPendingKandanMessages(queue));
220
- case 'length':
221
- return outcome(() => pendingKandanMessageQueueLength(queue));
222
- default:
223
- return { threw: true, message: 'unknown pendingQueue fn ' + String(action.fn) };
224
- }
225
- });
226
- return { results };
227
- }
228
-
229
- // --- Watermark pure semantics -----------------------------------------------------------
230
-
231
- function watermarkMemStore(calls) {
232
- const map = new Map();
233
- return {
234
- initial: (key) => map.get(key),
235
- onAdvance: (key, seq) => {
236
- calls.push({ call: 'onAdvance', key, seq });
237
- const existing = map.get(key);
238
- if (existing === undefined || seq > existing) {
239
- map.set(key, seq);
240
- }
241
- },
242
- flush: () => {
243
- calls.push({ call: 'flush' });
244
- },
245
- dump: () => Object.fromEntries(map),
246
- };
247
- }
248
-
249
- function handleWatermark(testCase) {
250
- switch (testCase.fn) {
251
- case 'shouldApply':
252
- return outcome(() =>
253
- shouldApplySourceSeq(testCase.applied ?? undefined, testCase.sourceSeq)
254
- );
255
- case 'key':
256
- return outcome(() =>
257
- threadSourceSeqWatermarkKey(testCase.workspace, testCase.channel, testCase.threadId)
258
- );
259
- case 'highestProcessed':
260
- return outcome(() => highestProcessedSourceSeq(testCase.rows));
261
- case 'reconcile':
262
- return outcome(() =>
263
- reconcileAppliedSourceSeq(testCase.local ?? undefined, testCase.rows)
264
- );
265
- case 'parseRows':
266
- return outcome(() => parseServerMessageStateRows(testCase.value));
267
- case 'parseResumeReconcile':
268
- return outcome(() => parseResumeReconcileEntries(testCase.value));
269
- case 'resumeReconcileRows':
270
- return outcome(() => resumeReconcileServerRows(testCase.entry));
271
- case 'isFencedClaim':
272
- return outcome(() =>
273
- isFencedClaim(testCase.held ?? undefined, testCase.control ?? undefined)
274
- );
275
- case 'guardScenario': {
276
- const calls = [];
277
- const store = watermarkMemStore(calls);
278
- const guard = createInFlightSourceSeqGuard();
279
- const results = (testCase.actions ?? []).map((action) => {
280
- switch (action.fn) {
281
- case 'mark':
282
- return outcome(() => guard.mark(action.key, action.seq));
283
- case 'has':
284
- return outcome(() => guard.has(action.key, action.seq));
285
- case 'clearSeq':
286
- return outcome(() => guard.clearSeq(action.key, action.seq));
287
- case 'clearThread':
288
- return outcome(() => guard.clearThread(action.key));
289
- case 'commit':
290
- return outcome(() => commitAppliedSourceSeq(store, action.key, action.seq));
291
- case 'commitLiveAccepted':
292
- return outcome(() =>
293
- commitLiveAcceptedSourceSeq({
294
- store,
295
- watermarkKey: action.key,
296
- sourceSeq: action.seq,
297
- })
298
- );
299
- case 'appliedOrInFlight':
300
- return outcome(() =>
301
- sourceSeqAppliedOrInFlight(
302
- store.initial(action.key),
303
- guard,
304
- action.key,
305
- action.seq
306
- )
307
- );
308
- default:
309
- return { threw: true, message: 'unknown guard fn ' + String(action.fn) };
310
- }
311
- });
312
- return { results, storeCalls: calls, store: store.dump() };
313
- }
314
- default:
315
- return { threw: true, message: 'unknown watermark fn ' + String(testCase.fn) };
316
- }
317
- }
318
-
319
- // --- Durable inbound queue --------------------------------------------------------------
320
-
321
- function normalizeEntry(entry) {
322
- return entry === undefined ? undefined : { ...entry };
323
- }
324
-
325
- function handleInboundQueue(testCase) {
326
- if (testCase.fn === 'foldLines') {
327
- return outcome(() => foldQueueRecords(testCase.lines));
328
- }
329
- if (testCase.fn === 'shouldDeadLetter') {
330
- return outcome(() =>
331
- shouldDeadLetter(testCase.entry, testCase.policy ?? defaultDeadLetterPolicy, testCase.nowMs)
332
- );
333
- }
334
- if (testCase.fn === 'path') {
335
- process.env.LINZUMI_INBOUND_QUEUE_DIR = testCase.envDir ?? '';
336
- const result = outcome(() =>
337
- durableInboundQueuePath(testCase.workspace, testCase.channel, testCase.threadId)
338
- );
339
- delete process.env.LINZUMI_INBOUND_QUEUE_DIR;
340
- return result;
341
- }
342
- // Scripted lifecycle in a scratch dir. fsync spies record ORDERING facts
343
- // (an 'fsync' event lands in the stream before the action result is
344
- // recorded), proving fsync-before-return (#2974) on BOTH impls.
345
- const dir = scratch();
346
- const path = join(dir, 'thread.log');
347
- if (testCase.initialContents !== undefined) {
348
- writeFileSync(path, testCase.initialContents, 'utf8');
349
- }
350
- const events = [];
351
- const log = collectingLog(events, dir);
352
- const deps = {
353
- fsyncFd: () => {
354
- events.push({ event: 'spy.fsync_data' });
355
- },
356
- fsyncDirFd: () => {
357
- events.push({ event: 'spy.fsync_dir' });
358
- },
359
- };
360
- let queue = createDurableInboundThreadQueue(path, log, deps);
361
- const results = (testCase.actions ?? []).map((action) => {
362
- switch (action.fn) {
363
- case 'enqueue': {
364
- const result = outcome(() =>
365
- queue.enqueue(action.sourceSeq, action.kind ?? 'live', action.payload ?? {}, action.nowMs)
366
- );
367
- events.push({ event: 'action.enqueue_returned', sourceSeq: action.sourceSeq });
368
- return result;
369
- }
370
- case 'ack':
371
- return outcome(() => queue.ack(action.sourceSeq));
372
- case 'recordAttempt':
373
- return outcome(() => queue.recordAttempt(action.sourceSeq));
374
- case 'deadLetter':
375
- return outcome(() => normalizeEntry(queue.deadLetter(action.sourceSeq)));
376
- case 'compact':
377
- return outcome(() => queue.compact());
378
- case 'entries':
379
- return outcome(() => queue.entries().map(normalizeEntry));
380
- case 'ackedEntries':
381
- return outcome(() => queue.ackedEntries().map(normalizeEntry));
382
- case 'head':
383
- return outcome(() => normalizeEntry(queue.head()));
384
- case 'highWater':
385
- return outcome(() => queue.highWaterSourceSeq());
386
- case 'tornTail':
387
- return outcome(() => queue.tornTail());
388
- case 'reconciledTornTail':
389
- return outcome(() => queue.reconciledTornTail());
390
- case 'appendRaw':
391
- // Simulate a crash-torn tail / foreign bytes.
392
- return outcome(() => appendFileSync(path, action.raw, 'utf8'));
393
- case 'reload': {
394
- // Simulated SIGKILL + restart: a fresh handle folds the same file.
395
- queue = createDurableInboundThreadQueue(path, log, deps);
396
- return { defined: false };
397
- }
398
- default:
399
- return { threw: true, message: 'unknown inboundQueue fn ' + String(action.fn) };
400
- }
401
- });
402
- return substitute(
403
- { results, events, files: snapshotFiles(dir) },
404
- dir,
405
- dirPlaceholder
406
- );
407
- }
408
-
409
- // --- Control quarantine -------------------------------------------------------------------
410
-
411
- function handleQuarantine(testCase) {
412
- if (testCase.fn === 'paths') {
413
- return {
414
- store: outcome(() => withheldControlAckStorePath('$STATE', testCase.runnerId)),
415
- quarantine: outcome(() => controlQuarantineFilePath('$STATE', testCase.runnerId)),
416
- };
417
- }
418
- const dir = scratch();
419
- const storePath = join(dir, 'withheld-acks.json');
420
- const quarantinePath = join(dir, 'quarantined-controls.jsonl');
421
- if (testCase.initialStoreContents !== undefined) {
422
- writeFileSync(storePath, testCase.initialStoreContents, 'utf8');
423
- }
424
- if (testCase.initialQuarantineContents !== undefined) {
425
- writeFileSync(quarantinePath, testCase.initialQuarantineContents, 'utf8');
426
- }
427
- const events = [];
428
- const emitted = [];
429
- const log = collectingLog(events, dir);
430
- const writeErrors = [];
431
- const store = fileWithheldControlAckStore(storePath, (message) =>
432
- writeErrors.push(message)
433
- );
434
- let epoch = testCase.epoch ?? undefined;
435
- const quarantine = createControlQuarantine({
436
- store,
437
- quarantineFilePath: quarantinePath,
438
- log,
439
- emitRunnerEvent: (payload) => {
440
- emitted.push(substitute(payload, dir, dirPlaceholder));
441
- },
442
- currentEpoch: () => epoch,
443
- ...(testCase.limit === undefined ? {} : { limit: testCase.limit }),
444
- now: () => new Date(testCase.nowMs ?? 0),
445
- });
446
- const results = (testCase.rejections ?? []).map((rejection) => {
447
- if (rejection.setEpoch !== undefined) {
448
- epoch = rejection.setEpoch === null ? undefined : rejection.setEpoch;
449
- }
450
- return outcome(() =>
451
- quarantine.onControlRejected(
452
- rejection.control,
453
- new Error(rejection.message ?? 'scripted rejection')
454
- )
455
- );
456
- });
457
- return substitute(
458
- { results, events, emitted, writeErrors, files: snapshotFiles(dir) },
459
- dir,
460
- dirPlaceholder
461
- );
462
- }
463
-
464
- // --- Outbox journal (recover-from-bytes) -----------------------------------------------------
465
-
466
- function normalizeRecovered(recovered) {
467
- return {
468
- pendingEntries: recovered.pendingEntries,
469
- kv: Object.fromEntries(recovered.kv),
470
- counters: recovered.counters,
471
- };
472
- }
473
-
474
- function handleOutboxJournal(testCase) {
475
- if (testCase.fn === 'path') {
476
- return outcome(() => outboxJournalPath('$PERSIST', testCase.threadKey));
477
- }
478
- // recover: fold the given journal bytes.
479
- const dir = scratch();
480
- const path = join(dir, 'thread.outbox.jsonl');
481
- if (testCase.contents !== undefined) {
482
- writeFileSync(path, testCase.contents, 'utf8');
483
- }
484
- const events = [];
485
- const recovered = outcome(() =>
486
- normalizeRecovered(recoverOutboxJournal(path, collectingLog(events, dir)))
487
- );
488
- return substitute({ recovered, events }, dir, dirPlaceholder);
489
- }
490
-
491
- // Spec: spec/commander-recovery-rules.md rule01. Failed terminal publication
492
- // remains evidence/debt, never ordinary replay or an acknowledged terminal.
493
- async function handleFailedTerminalJournal(testCase) {
494
- const dir = scratch();
495
- const path = join(dir, 'failed-terminal.outbox.jsonl');
496
- try {
497
- writeFileSync(path, testCase.contents, 'utf8');
498
- const events = [];
499
- let recovered = recoverOutboxJournal(path, collectingLog(events, dir));
500
- // Spec: commander-recovery rules1/5. A decoded healthy snapshot must remain
501
- // classifiable after repeated real compaction, not merely retain a counter.
502
- if (recovered.recovery.type !== 'healthy') throw new Error('initial failure inventory unavailable');
503
- if (testCase.compact) {
504
- for (let cycle = 0; cycle < 2; cycle += 1) {
505
- const snapshot = recovered;
506
- const journal = createOutboxJournal({path, compactionThreshold: 1,
507
- snapshot: () => snapshot, log: collectingLog(events, dir)});
508
- journal.appendAck(63);
509
- await journal.settled();
510
- await journal.close();
511
- recovered = recoverOutboxJournal(path, collectingLog(events, dir));
512
- if (recovered.recovery.type !== 'healthy') throw new Error('compacted failure inventory unavailable');
513
- if (recovered.counters.knownNonterminalFailures !== 1 || recovered.counters.totalPermanentFailures !== 2)
514
- throw new Error('compaction changed observed failure classification');
515
- }
516
- }
517
- return {pendingEntries: recovered.pendingEntries,
518
- failedTerminalEntries: recovered.failedTerminalEntries, counters: recovered.counters, events};
519
- } finally {rmSync(dir, {recursive: true, force: true});}
520
- }
521
-
522
- // --- Outbox flow scenarios ---------------------------------------------------------------------
523
-
524
- function buildersFromSpec(spec, recordedReplies) {
525
- const builders = {};
526
- for (const [kind, config] of Object.entries(spec ?? {})) {
527
- builders[kind] = {
528
- build: (data) => {
529
- if (config.mode === 'skip') {
530
- return { skip: true, reason: String(data.skipReason ?? 'scripted-skip') };
531
- }
532
- if (config.mode === 'fail') {
533
- throw new Error(String(data.failMessage ?? 'scripted-build-failure'));
534
- }
535
- return {
536
- event: 'evt:' + kind,
537
- payload: { ...data },
538
- ...(config.timeoutMs === undefined ? {} : { timeoutMs: config.timeoutMs }),
539
- };
540
- },
541
- // Spec: spec/commander-recovery-rules.md. These generic parity entries
542
- // exercise must_ack/best_effort transport acknowledgement, never the
543
- // approval-failure consensus protocol.
544
- replyPolicy: { kind: 'transport_ack' },
545
- ...(config.onReply === 'record'
546
- ? {
547
- onReply: (data, reply) => {
548
- recordedReplies.push({ kind, data, reply });
549
- },
550
- }
551
- : {}),
552
- ...(config.coalesce === 'concat-bodies'
553
- ? {
554
- coalesce: (older, newer) => ({
555
- ...newer,
556
- body: String(older.body ?? '') + '|' + String(newer.body ?? ''),
557
- }),
558
- }
559
- : {}),
560
- };
561
- }
562
- return builders;
563
- }
564
-
565
- async function runOutboxPhase(phase, shared) {
566
- // The outbox unrefs its retry timers by design (a pending backoff must
567
- // never hold the runner process open), so a one-shot driver awaiting a
568
- // flush across a backoff would see its event loop empty. Keep it alive
569
- // for the phase.
570
- const keepAlive = setInterval(() => {}, 25);
571
- try {
572
- return await runOutboxPhaseInner(phase, shared);
573
- } finally {
574
- clearInterval(keepAlive);
575
- }
576
- }
577
-
578
- async function runOutboxPhaseInner(phase, shared) {
579
- const pushScript = [...(phase.pushScript ?? [])];
580
- const pushes = [];
581
- const permanentFailures = [];
582
- const recordedReplies = [];
583
- const events = [];
584
- const log = collectingLog(events, shared.dir);
585
- let nowMs = phase.nowMs ?? 1_000;
586
- const builders = buildersFromSpec(phase.builders ?? shared.builders, recordedReplies);
587
- if (phase.terminalRepairBuilder === true) {
588
- builders['adt.terminal_attempt'] = createSessionPipelineBuilders({
589
- instanceId: 'repair-fixture', wire: () => ({workspaceSlug: 'synthetic', channelSlug: 'synthetic',
590
- kandanThreadId: shared.threadKey, codexThreadId: 'synthetic-provider'}),
591
- runnerPayload: () => ({}), messageStateReplyShape: {kind: 'full_reply'}, log,
592
- })['adt.terminal_attempt'];
593
- }
594
- const outbox = createThreadOutbox({
595
- threadKey: phase.threadKey ?? shared.threadKey,
596
- push: async (event, payload, timeoutMs, onSent) => {
597
- const step = pushScript.shift() ?? { kind: 'fail', message: 'push script exhausted' };
598
- pushes.push({
599
- event,
600
- payload,
601
- hasTimeout: timeoutMs !== undefined,
602
- });
603
- onSent?.();
604
- if (step.delayMs !== undefined) {
605
- await new Promise((resolve) => setTimeout(resolve, step.delayMs));
606
- }
607
- if (step.kind === 'ack') {
608
- return step.reply ?? { ok: true };
609
- }
610
- const failure = new Error(String(step.message ?? 'scripted push failure'));
611
- if (step.budgetExempt === true) {
612
- failure.name = 'ScriptedBudgetExempt';
613
- }
614
- throw failure;
615
- },
616
- builders,
617
- persistDir: shared.persistDir,
618
- retryDelaysMs: phase.retryDelaysMs ?? [1, 1],
619
- isRetryBudgetExempt: (error) => error.name === 'ScriptedBudgetExempt',
620
- onPermanentFailure: (entry, error) => {
621
- permanentFailures.push({
622
- entrySeq: entry.entrySeq,
623
- kind: entry.kind,
624
- error: error.message,
625
- });
626
- },
627
- log,
628
- now: () => nowMs,
629
- ...(phase.maxInFlightPushes === undefined
630
- ? {}
631
- : { maxInFlightPushes: phase.maxInFlightPushes }),
632
- ...(phase.journalCompactionThreshold === undefined
633
- ? {}
634
- : { journalCompactionThreshold: phase.journalCompactionThreshold }),
635
- ...(phase.journalRecoveryMaxAgeMs === undefined
636
- ? {}
637
- : { journalRecoveryMaxAgeMs: phase.journalRecoveryMaxAgeMs }),
638
- });
639
-
640
- const results = [];
641
- for (const action of phase.actions ?? []) {
642
- switch (action.fn) {
643
- case 'advanceNow':
644
- nowMs += action.byMs;
645
- results.push({ defined: false });
646
- break;
647
- case 'enqueue':
648
- results.push(
649
- outcome(() =>
650
- outbox.enqueue({
651
- kind: action.kind,
652
- data: action.data ?? {},
653
- ...(action.coalesceKey === undefined ? {} : { coalesceKey: action.coalesceKey }),
654
- resolution: action.resolution ?? 'must_ack',
655
- })
656
- )
657
- );
658
- break;
659
- case 'cycleReceipt': {
660
- const receipt = outbox.recallDurableFact(action.key);
661
- if(receipt === null || typeof receipt !== 'object') throw new Error('cyclic receipt fixture missing stored object');
662
- receipt.cycle = receipt;
663
- results.push({defined:false});
664
- break;
665
- }
666
- case 'pause':
667
- results.push(outcome(() => outbox.pause()));
668
- break;
669
- case 'resume':
670
- results.push(outcome(() => outbox.resume()));
671
- break;
672
- case 'flush': {
673
- try {
674
- await outbox.flush(action.timeoutMs);
675
- results.push({ flushed: true });
676
- } catch (error) {
677
- results.push({
678
- flushed: false,
679
- message: error instanceof Error ? error.message : String(error),
680
- });
681
- }
682
- break;
683
- }
684
- case 'metrics': {
685
- const metrics = outbox.metrics();
686
- results.push({
687
- defined: true,
688
- value: {
689
- depth: metrics.depth,
690
- totalEnqueued: metrics.totalEnqueued,
691
- totalAcked: metrics.totalAcked,
692
- totalRetried: metrics.totalRetried,
693
- totalDroppedBestEffort: metrics.totalDroppedBestEffort,
694
- totalPermanentFailures: metrics.totalPermanentFailures,
695
- totalExpiredOnRecovery: metrics.totalExpiredOnRecovery,
696
- journalDegraded: metrics.journalDegraded,
697
- paused: metrics.paused,
698
- ackedInWindow: metrics.pushLatency?.ackedInWindow ?? 0,
699
- },
700
- });
701
- break;
702
- }
703
- case 'journalSettled':
704
- await outbox.journalSettled();
705
- results.push({ defined: false });
706
- break;
707
- // Spec: spec/commander-recovery-rules.md. Only this scenario's fresh
708
- // scratch directory is moved; a real filesystem refusal tests both implementations.
709
- case 'blockJournalWrites':
710
- case 'restoreJournalWrites': {
711
- await outbox.journalSettled();
712
- const blocked = shared.persistDir + '.blocked';
713
- if (action.fn === 'blockJournalWrites') renameSync(shared.persistDir, blocked);
714
- else renameSync(blocked, shared.persistDir);
715
- results.push({defined: false});
716
- break;
717
- }
718
- case 'appendUncertainTail':
719
- case 'blockJournalCompaction':
720
- case 'blockRekeyDestination':
721
- case 'restoreJournalCompaction': {
722
- await outbox.journalSettled();
723
- const path = outboxJournalPath(shared.persistDir, shared.threadKey);
724
- if (action.fn === 'blockRekeyDestination') mkdirSync(outboxJournalPath(shared.persistDir, action.threadKey));
725
- else if (action.fn === 'appendUncertainTail') appendFileSync(path, '{"t":"kv","key":', 'utf8');
726
- else if (action.fn === 'blockJournalCompaction') mkdirSync(path + '.tmp');
727
- else renameSync(path + '.tmp', path + '.blocked-compaction');
728
- results.push({defined: false});
729
- break;
730
- }
731
- case 'failedTerminalEntries':
732
- results.push({failedTerminalEntries: outbox.failedTerminalEntries()});
733
- break;
734
- case 'terminalPublicationDebt':
735
- results.push({terminalPublicationDebt: outbox.terminalPublicationDebt()});
736
- break;
737
- case 'rememberDurableFact':
738
- outbox.rememberDurableFact(action.key, action.value);
739
- await outbox.journalSettled();
740
- results.push({defined: false});
741
- break;
742
- case 'rememberWithoutSettle':
743
- outbox.rememberDurableFact(action.key, action.value);
744
- results.push({defined: false});
745
- break;
746
- case 'repairTerminalDebt':
747
- results.push({repair: await enqueueTerminalDebtRepair(outbox, {
748
- threadId: shared.threadKey, sourceSeq: 44, sessionId: 3600, originalLeaseEpoch: 1,
749
- binding: action.binding, authoritativeTerminal: action.authoritativeTerminal === true,
750
- authorize: () => true,
751
- }, log)});
752
- break;
753
- case 'rekeyJournal':
754
- results.push(outcome(() => outbox.rekeyJournal(action.threadKey)));
755
- await outbox.journalSettled();
756
- break;
757
- case 'sleep':
758
- await new Promise((resolve) => setTimeout(resolve, action.ms));
759
- results.push({ defined: false });
760
- break;
761
- case 'close':
762
- await outbox.close(action.timeoutMs);
763
- results.push({ defined: false });
764
- break;
765
- default:
766
- results.push({ threw: true, message: 'unknown outbox fn ' + String(action.fn) });
767
- }
768
- }
769
- // Make phase end deterministic: everything journaled so far is on disk.
770
- await outbox.journalSettled();
771
- return {
772
- results,
773
- pushes,
774
- permanentFailures,
775
- recordedReplies,
776
- // Log EVENT NAMES in order (payloads carry timing values that legally
777
- // differ across impls; names + counts are the stable contract).
778
- logEvents: events.map((entry) => entry.event),
779
- };
780
- }
781
-
782
- async function handleOutboxScenario(testCase) {
783
- const dir = scratch();
784
- // Cross-impl seed: files the OTHER impl wrote (journal bytes), laid down
785
- // before any phase so recovery reads foreign-written bytes.
786
- for (const [relPath, contents] of Object.entries(testCase.initialFiles ?? {})) {
787
- const full = join(dir, relPath);
788
- mkdirSync(join(full, '..'), { recursive: true });
789
- writeFileSync(full, contents, 'utf8');
790
- }
791
- const shared = {
792
- dir,
793
- persistDir: testCase.inMemory === true ? undefined : join(dir, 'commander-outbox'),
794
- threadKey: testCase.threadKey ?? 'linzumi:42:thread-a',
795
- builders: testCase.builders ?? {},
796
- };
797
- const phases = [];
798
- for (const phase of testCase.phases ?? []) {
799
- phases.push(await runOutboxPhase(phase, shared));
800
- }
801
- return substitute(
802
- { phases, files: snapshotFiles(dir) },
803
- dir,
804
- dirPlaceholder
805
- );
806
- }
807
-
808
- // --- Resume keystone flow scenarios ----------------------------------------------------------
809
-
810
- async function handleKeystoneScenario(testCase) {
811
- const dir = scratch();
812
- const path = join(dir, 'thread.log');
813
- if (testCase.initialContents !== undefined) {
814
- writeFileSync(path, testCase.initialContents, 'utf8');
815
- }
816
- const events = [];
817
- const log = collectingLog(events, dir);
818
- const queue = createDurableInboundThreadQueue(path, log);
819
- const storeCalls = [];
820
- const store = watermarkMemStore(storeCalls);
821
- if (typeof testCase.initialWatermark === 'number') {
822
- store.onAdvance(testCase.watermarkKey, testCase.initialWatermark);
823
- storeCalls.length = 0;
824
- }
825
- const lock = createThreadReconcilerLock();
826
- const leases = createLeaseEpochRegistry();
827
- const guard = createInFlightSourceSeqGuard();
828
- const deadLetters = [];
829
- const dispatchScript = [...(testCase.dispatchScript ?? [])];
830
- const dispatchCalls = [];
831
- const dispatch = async (sourceSeq, payload) => {
832
- dispatchCalls.push({ sourceSeq, payload });
833
- const step = dispatchScript.shift() ?? { outcome: 'ignored' };
834
- return step.outcome;
835
- };
836
- const onDeadLetter = (args) => {
837
- deadLetters.push({
838
- threadId: args.threadId,
839
- sourceSeq: args.sourceSeq,
840
- reason: args.reason,
841
- attempts: args.attempts,
842
- alreadyPublished: args.alreadyPublished,
843
- });
844
- };
845
- const results = [];
846
- for (const step of testCase.steps ?? []) {
847
- if (step.fn === 'receive') {
848
- const outcomeValue = await receiveResumeMessage({
849
- queue,
850
- store,
851
- lock,
852
- leases,
853
- threadId: testCase.threadId,
854
- watermarkKey: testCase.watermarkKey,
855
- sourceSeq: step.sourceSeq,
856
- kind: step.kind ?? 'live',
857
- payload: step.payload ?? {},
858
- leaseEpoch: step.leaseEpoch ?? undefined,
859
- dispatch,
860
- serverRows: step.serverRows ?? [],
861
- log,
862
- nowMs: step.nowMs ?? 1_000,
863
- ...(step.policy === undefined ? {} : { policy: step.policy }),
864
- onlyDispatchUnattempted: step.onlyDispatchUnattempted ?? false,
865
- onDeadLetter,
866
- inFlightGuard: guard,
867
- });
868
- results.push({ receive: outcomeValue });
869
- } else if (step.fn === 'drain') {
870
- const dispatched = await drainResumeQueue({
871
- queue,
872
- store,
873
- lock,
874
- threadId: testCase.threadId,
875
- watermarkKey: testCase.watermarkKey,
876
- serverRows: step.serverRows ?? [],
877
- dispatch,
878
- log,
879
- nowMs: step.nowMs ?? 1_000,
880
- ...(step.policy === undefined ? {} : { policy: step.policy }),
881
- onlyDispatchUnattempted: step.onlyDispatchUnattempted ?? false,
882
- onTornTailUnrecoverable: (threadId) => {
883
- deadLetters.push({ threadId, tornTailSurfaced: true });
884
- },
885
- onDeadLetter,
886
- inFlightGuard: guard,
887
- });
888
- results.push({ drained: dispatched });
889
- } else if (step.fn === 'executionCommit') {
890
- // The turn EXECUTED to a terminal: the live path commits the floor
891
- // (I58 execution-confirm) and clears the in-flight guard.
892
- const advanced = commitLiveAcceptedSourceSeq({
893
- store,
894
- watermarkKey: testCase.watermarkKey,
895
- sourceSeq: step.sourceSeq,
896
- });
897
- guard.clearSeq(testCase.watermarkKey, step.sourceSeq);
898
- results.push({ executionCommit: advanced });
899
- } else if (step.fn === 'workerDeath') {
900
- guard.clearThread(testCase.watermarkKey);
901
- results.push({ workerDeath: true });
902
- } else if (step.fn === 'applyOutcome') {
903
- await applyResumeDispatchOutcome({
904
- queue,
905
- store,
906
- watermarkKey: testCase.watermarkKey,
907
- sourceSeq: step.sourceSeq,
908
- threadId: testCase.threadId,
909
- outcome: step.outcome,
910
- log,
911
- nowMs: step.nowMs ?? 1_000,
912
- ...(step.policy === undefined ? {} : { policy: step.policy }),
913
- onDeadLetter,
914
- inFlightGuard: guard,
915
- });
916
- results.push({ applied: step.outcome });
917
- } else {
918
- results.push({ threw: true, message: 'unknown keystone fn ' + String(step.fn) });
919
- }
920
- }
921
- return substitute(
922
- {
923
- results,
924
- dispatchCalls,
925
- deadLetters,
926
- storeCalls,
927
- store: store.dump(),
928
- liveEntries: queue.entries().map((entry) => ({ ...entry })),
929
- ackedEntries: queue.ackedEntries().map((entry) => ({ ...entry })),
930
- logEvents: events.map((entry) => entry.event),
931
- files: snapshotFiles(dir),
932
- },
933
- dir,
934
- dirPlaceholder
935
- );
936
- }
937
-
938
- // --- Dispatch --------------------------------------------------------------------------------
939
-
940
- async function handle(testCase) {
941
- switch (testCase.op) {
942
- case 'queuedMessage':
943
- return handleQueuedMessage(testCase);
944
- case 'pendingQueue':
945
- return handlePendingQueue(testCase);
946
- case 'watermark':
947
- return handleWatermark(testCase);
948
- case 'inboundQueue':
949
- return handleInboundQueue(testCase);
950
- case 'quarantine':
951
- return handleQuarantine(testCase);
952
- case 'outboxJournal':
953
- return handleOutboxJournal(testCase);
954
- case 'failedTerminalJournal':
955
- return await handleFailedTerminalJournal(testCase);
956
- case 'outboxScenario':
957
- return await handleOutboxScenario(testCase);
958
- case 'keystoneScenario':
959
- return await handleKeystoneScenario(testCase);
960
- default:
961
- return { threw: true, message: 'unknown op ' + String(testCase.op) };
962
- }
963
- }
964
-
965
- let stdin = '';
966
- process.stdin.setEncoding('utf8');
967
- for await (const chunk of process.stdin) {
968
- stdin += chunk;
969
- }
970
- const cases = JSON.parse(stdin);
971
- const results = [];
972
- for (const testCase of cases) {
973
- results.push(await handle(testCase));
974
- }
975
- // Exit only after stdout is fully flushed: scripted outbox scenarios can
976
- // leave unref'd timers alive, so the driver exits explicitly - but a bare
977
- // process.exit(0) truncates large result arrays mid-pipe.
978
- process.stdout.write(JSON.stringify(results), () => {
979
- process.exit(0);
980
- });
981
- `;
982
-
983
- await mkdir(join(packageRoot, '.differential'), { recursive: true });
984
-
985
- await build({
986
- stdin: {
987
- contents: driverSource,
988
- resolveDir: packageRoot,
989
- sourcefile: 'queue-parity-driver.ts',
990
- loader: 'ts',
991
- },
992
- bundle: true,
993
- // Same production ESM/CJS bridge as the existing core integration oracle;
994
- // the shared typed terminal-repair validator reaches CommanderNode bindings.
995
- banner: {
996
- js: "import { createRequire as __queueOracleCreateRequire } from 'node:module';\nconst require = __queueOracleCreateRequire(import.meta.url);",
997
- },
998
- platform: 'node',
999
- target: 'node20',
1000
- format: 'esm',
1001
- // Match the transport/session-registry oracles: generated native Node seams
1002
- // reach CommonJS dependencies. Keep real Node require semantics in this ESM
1003
- // test artifact; the unique alias avoids bundled createRequire collisions.
1004
- banner: {
1005
- js: "import { createRequire as __queueOracleCreateRequire } from 'node:module';\nconst require = __queueOracleCreateRequire(import.meta.url);",
1006
- },
1007
- outfile: join(packageRoot, '.differential/queue-parity-oracle.mjs'),
1008
- minify: false,
1009
- sourcemap: false,
1010
- legalComments: 'none',
1011
- external: ['ws', 'undici', 'blessed', '@anthropic-ai/claude-agent-sdk'],
1012
- });