@linzumi/cli 1.0.101 → 1.0.103

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -58,7 +58,7 @@ Install the CLI or run it with `npx`:
58
58
  ```bash
59
59
  npm install -g @linzumi/cli@latest
60
60
  npx -y @linzumi/cli@latest signup
61
- npx -y @linzumi/cli@1.0.101 --version
61
+ npx -y @linzumi/cli@1.0.103 --version
62
62
  linzumi --version
63
63
  ```
64
64
 
package/dist/index.js CHANGED
@@ -1471,7 +1471,7 @@ To kick the agent off:
1471
1471
  `,L=Buffer.byteLength(P,"utf8");if(L>s)return l("runner_diagnostics.spool_entry_dropped",{reason:"entry_too_large",entry_bytes:L,max_entry_bytes:s}),!1;uSe(n,{recursive:!0}),m(u)+L>i&&f(),_(L);let D=w()?`
1472
1472
  `:"";return sSe(u,`${D}${P}`,"utf8"),!0}catch(R){return l("runner_diagnostics.spool_failed",{error:R instanceof Error?R.message:String(R)}),!1}},v=k=>{let R=[],P=lSe(k,"utf8");for(let L of P.split(`
1473
1473
  `))if(L.trim()!=="")try{let D=JSON.parse(L);D!==null&&typeof D=="object"&&R.push({parsed:D,raw:L})}catch{}return R};return{append:S,drain:async k=>{if(c)return{uploadedBatches:0,uploadedEntries:0,remainingBatches:p().length,skipped:!0};c=!0;let R=0,P=0;try{f(),_(0);let L=a()-o;for(let D of p()){let j;try{j=v(D)}catch(Z){l("runner_diagnostics.spool_batch_unreadable",{file:D,error:Z instanceof Error?Z.message:String(Z)}),y(D,"unreadable");continue}let q=j.filter(Z=>{let fe=ISe(Z.parsed);return fe===void 0||fe>=L});if(q.length===0){y(D,"empty_or_expired");continue}let E=oP(D,".jsonl"),B=q.map(Z=>Z.raw).join(`
1474
- `),z=q.map(Z=>Z.parsed.spooled_at).filter(Z=>typeof Z=="string"),W={bundle_name:"runner-diagnostics-spool",exported_at:new Date(a()).toISOString(),idempotency_key:E,diagnostics:{kind:"runner_diagnostic_log",spool_batch:!0,idempotency_key:E,entry_count:q.length,...z.length===0?{}:{first_spooled_at:z[0],last_spooled_at:z[z.length-1]}},telemetry_ndjson:B};try{await k(W)}catch(Z){l("runner_diagnostics.spool_upload_failed",{file:D,remaining_batches:p().length,error:Z instanceof Error?Z.message:String(Z)});break}R+=1,P+=q.length;try{h9(D)}catch(Z){l("runner_diagnostics.spool_delete_failed",{file:D,error:Z instanceof Error?Z.message:String(Z)})}}}catch(L){l("runner_diagnostics.spool_drain_failed",{error:L instanceof Error?L.message:String(L)})}finally{c=!1}return{uploadedBatches:R,uploadedEntries:P,remainingBatches:p().length,skipped:!1}},pendingBatchFiles:p}}var ySe,xSe,_Se,bSe,wSe,g9,SSe,kSe,x9=F(()=>{"use strict";Xy();ySe=32*1024*1024,xSe=10080*60*1e3,_Se=4*1024*1024,bSe=1024*1024,wSe="spool.jsonl",g9=/^batch-(\d{1,16})-[0-9a-f-]+\.jsonl$/;SSe=/\b([A-Z][A-Z0-9_]{1,})=("[^"\n]*"|'[^'\n]*'|[^\s"'`;,)\]}]+)/g,kSe=/(?:\/(?:[A-Za-z0-9._@~+-]|%[0-9A-Fa-f]{2})+){2,}/g});var jt,sP,ea=F(()=>{"use strict";jt="1.0.101",sP=`linzumi ${jt}`});import{closeSync as b9,existsSync as dP,mkdirSync as RSe,openSync as w9,readFileSync as v9,renameSync as ESe,unlinkSync as gd,writeFileSync as DSe,writeSync as S9}from"node:fs";import{dirname as k9,join as MSe}from"node:path";function uP(e){return e instanceof hs||e instanceof Error&&e.name===T9&&"lockPath"in e&&"heldBy"in e}function cP(e,n=Wn(),t){let r=t===void 0?encodeURIComponent(e):dd(t);return MSe(k9(n),"runners",`${r}.lock`)}function u0(e){let n=cP(e.machineId,e.configPath,e.linzumiUrl),t=e.isPidAlive??A9,r=e.now??(()=>new Date),o=e.killPid??KSe,i=r().toISOString(),s={version:1,machineId:e.machineId,runnerId:e.runnerId,pid:e.pid??process.pid,cwd:e.cwd,workspace:e.workspace,...e.linzumiUrl===void 0?{}:{linzumiUrl:qn(e.linzumiUrl)},...e.launchSource===void 0?{}:{launchSource:e.launchSource},startedAt:i,heartbeatAt:i,cliVersion:e.cliVersion??jt,...e.gracefulUpdateDrain===!0?{gracefulUpdateDrain:!0}:{}};LSe(e.machineId,e.configPath,e.linzumiUrl,t,r,o,e.onTakeover),jSe(n,s,t,r,o,e.onTakeover,e.beforeReadExistingLock,e.beforeReplaceStaleLock);let a=()=>P9(n,s,r),d=setInterval(a,C9);return d.unref(),{path:n,record:s,heartbeat:a,release:()=>{clearInterval(d),D9(n,s)}}}function P9(e,n,t){try{let r=l0(e);if(r===void 0||r.machineId!==n.machineId||r.runnerId!==n.runnerId||r.pid!==n.pid||r.startedAt!==n.startedAt)return;let o={...r,heartbeatAt:t().toISOString()},i=`${e}.tmp`;DSe(i,`${JSON.stringify(o,null,2)}
1474
+ `),z=q.map(Z=>Z.parsed.spooled_at).filter(Z=>typeof Z=="string"),W={bundle_name:"runner-diagnostics-spool",exported_at:new Date(a()).toISOString(),idempotency_key:E,diagnostics:{kind:"runner_diagnostic_log",spool_batch:!0,idempotency_key:E,entry_count:q.length,...z.length===0?{}:{first_spooled_at:z[0],last_spooled_at:z[z.length-1]}},telemetry_ndjson:B};try{await k(W)}catch(Z){l("runner_diagnostics.spool_upload_failed",{file:D,remaining_batches:p().length,error:Z instanceof Error?Z.message:String(Z)});break}R+=1,P+=q.length;try{h9(D)}catch(Z){l("runner_diagnostics.spool_delete_failed",{file:D,error:Z instanceof Error?Z.message:String(Z)})}}}catch(L){l("runner_diagnostics.spool_drain_failed",{error:L instanceof Error?L.message:String(L)})}finally{c=!1}return{uploadedBatches:R,uploadedEntries:P,remainingBatches:p().length,skipped:!1}},pendingBatchFiles:p}}var ySe,xSe,_Se,bSe,wSe,g9,SSe,kSe,x9=F(()=>{"use strict";Xy();ySe=32*1024*1024,xSe=10080*60*1e3,_Se=4*1024*1024,bSe=1024*1024,wSe="spool.jsonl",g9=/^batch-(\d{1,16})-[0-9a-f-]+\.jsonl$/;SSe=/\b([A-Z][A-Z0-9_]{1,})=("[^"\n]*"|'[^'\n]*'|[^\s"'`;,)\]}]+)/g,kSe=/(?:\/(?:[A-Za-z0-9._@~+-]|%[0-9A-Fa-f]{2})+){2,}/g});var jt,sP,ea=F(()=>{"use strict";jt="1.0.103",sP=`linzumi ${jt}`});import{closeSync as b9,existsSync as dP,mkdirSync as RSe,openSync as w9,readFileSync as v9,renameSync as ESe,unlinkSync as gd,writeFileSync as DSe,writeSync as S9}from"node:fs";import{dirname as k9,join as MSe}from"node:path";function uP(e){return e instanceof hs||e instanceof Error&&e.name===T9&&"lockPath"in e&&"heldBy"in e}function cP(e,n=Wn(),t){let r=t===void 0?encodeURIComponent(e):dd(t);return MSe(k9(n),"runners",`${r}.lock`)}function u0(e){let n=cP(e.machineId,e.configPath,e.linzumiUrl),t=e.isPidAlive??A9,r=e.now??(()=>new Date),o=e.killPid??KSe,i=r().toISOString(),s={version:1,machineId:e.machineId,runnerId:e.runnerId,pid:e.pid??process.pid,cwd:e.cwd,workspace:e.workspace,...e.linzumiUrl===void 0?{}:{linzumiUrl:qn(e.linzumiUrl)},...e.launchSource===void 0?{}:{launchSource:e.launchSource},startedAt:i,heartbeatAt:i,cliVersion:e.cliVersion??jt,...e.gracefulUpdateDrain===!0?{gracefulUpdateDrain:!0}:{}};LSe(e.machineId,e.configPath,e.linzumiUrl,t,r,o,e.onTakeover),jSe(n,s,t,r,o,e.onTakeover,e.beforeReadExistingLock,e.beforeReplaceStaleLock);let a=()=>P9(n,s,r),d=setInterval(a,C9);return d.unref(),{path:n,record:s,heartbeat:a,release:()=>{clearInterval(d),D9(n,s)}}}function P9(e,n,t){try{let r=l0(e);if(r===void 0||r.machineId!==n.machineId||r.runnerId!==n.runnerId||r.pid!==n.pid||r.startedAt!==n.startedAt)return;let o={...r,heartbeatAt:t().toISOString()},i=`${e}.tmp`;DSe(i,`${JSON.stringify(o,null,2)}
1475
1475
  `,"utf8"),ESe(i,e)}catch{}}function yd(e,n,t){if(!n(e.pid))return{state:"dead"};let r=e.heartbeatAt??e.startedAt,o=Date.parse(r);return Number.isNaN(o)||t().getTime()-o>ASe?{state:"wedged",reason:e.heartbeatAt===void 0?"missing_heartbeat":"stale_heartbeat"}:{state:"live"}}function I9(e,n,t,r){return t!==c0||e.launchSource!==c0?!1:r.state!=="live"?!0:$Se(n,e.cliVersion)>0}function $Se(e,n){let t=_9(e),r=_9(n);for(let o=0;o<3;o++){let i=t.core[o],s=r.core[o];if(i===void 0||s===void 0)throw new Error(`compareCliVersions: unexpected undefined at core index ${o}`);if(i!==s)return i>s?1:-1}return t.prerelease.length===0&&r.prerelease.length===0?0:t.prerelease.length===0?1:r.prerelease.length===0?-1:OSe(t.prerelease,r.prerelease)}function _9(e){let n=e.trim().replace(/^v/i,"").split("+",1)[0]??"",t=n.indexOf("-"),r=t===-1?n:n.slice(0,t),o=t===-1?"":n.slice(t+1),i=r.split("."),s=[aP(i[0]),aP(i[1]),aP(i[2])],a=o===""?[]:o.split(".").filter(d=>d!=="");return{core:s,prerelease:a}}function aP(e){let n=Number.parseInt(e??"",10);return Number.isInteger(n)&&n>=0?n:0}function OSe(e,n){let t=Math.max(e.length,n.length);for(let r=0;r<t;r++){let o=e[r],i=n[r];if(o===void 0)return-1;if(i===void 0)return 1;let s=Number.parseInt(o,10),a=Number.parseInt(i,10),d=s.toString()===o,u=a.toString()===i;if(d&&u){if(s!==a)return s>a?1:-1}else{if(d!==u)return d?-1:1;if(o!==i)return o>i?1:-1}}return 0}function R9(e,n,t,r,o){try{r(n.pid)}catch(i){let s=i!==null&&typeof i=="object"&&"code"in i?i.code:void 0;if(s!=="ESRCH")throw s==="EPERM"||s==="EACCES"?new Error(`Cannot take over the runner lock at ${e}: its holder process (pid ${n.pid}) is owned by another user and cannot be stopped. Stop that process, or remove the lock file, then retry.`):i}o?.({lockPath:e,holderPid:n.pid,holderRunnerId:n.runnerId,reason:t})}function LSe(e,n,t,r,o,i,s){if(t===void 0||yr(t)!==yr(Bt))return;let a=cP(e,n),d=l0(a);if(d!==void 0){if(yd(d,r,o).state==="live")throw new hs(a,d);E9(a,r,()=>{let u=dP(a)?lP(a):void 0;if(u===void 0)return;let c=yd(u,r,o);if(c.state==="live")throw new hs(a,u);c.state==="wedged"&&R9(a,u,c.reason,i,s),gd(a)})}}function jSe(e,n,t,r,o,i,s,a){if(d0(e,n))return;s?.();let d=l0(e);if(d===void 0){if(d0(e,n))return;throw new Error(`another Linzumi runner lock appeared while starting: ${e}`)}if(yd(d,t,r).state==="live")throw new hs(e,d);a?.(),E9(e,t,()=>{let u=dP(e)?lP(e):void 0;if(u!==void 0){let c=yd(u,t,r);if(c.state==="live")throw new hs(e,u);c.state==="wedged"&&R9(e,u,c.reason,o,i),gd(e)}if(!d0(e,n))throw new Error(`another Linzumi runner lock appeared while starting: ${e}`)})}function d0(e,n){RSe(k9(e),{recursive:!0});try{let t=w9(e,"wx");try{S9(t,`${JSON.stringify(n,null,2)}
1476
1476
  `)}finally{b9(t)}return!0}catch(t){if(Bc(t,"EEXIST"))return!1;throw t}}function E9(e,n,t){let r=`${e}.replace`;for(;;)try{let o=w9(r,"wx");try{S9(o,`${process.pid}
1477
1477
  `),t()}finally{b9(o),gd(r)}return}catch(o){if(Bc(o,"EEXIST")){let i=FSe(r);if(i===void 0)continue;if(n(i))throw new Error(["another Linzumi runner is already replacing a stale runner lock",`lock: ${e}`,"Wait for that startup to finish, then retry."].join(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linzumi/cli",
3
- "version": "1.0.101",
3
+ "version": "1.0.103",
4
4
  "description": "Linzumi CLI \u2014 point a Codex agent at the real code on your laptop, with your team watching and steering from shared threads.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,899 @@
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
+ readFileSync,
45
+ realpathSync,
46
+ statSync,
47
+ writeFileSync,
48
+ } from 'node:fs';
49
+ import { tmpdir } from 'node:os';
50
+ import { join, relative } from 'node:path';
51
+ import {
52
+ codexInputForQueuedKandanMessage,
53
+ formatQueuedKandanMessage,
54
+ interruptQueuedMessages,
55
+ normalizeCodexSteerInput,
56
+ queuedKandanMessageSourceSeqs,
57
+ steerInputText,
58
+ } from './src/kandanQueue';
59
+ import {
60
+ createPendingKandanMessageQueue,
61
+ dequeuePendingKandanMessage,
62
+ drainAllPendingKandanMessages,
63
+ enqueuePendingKandanMessage,
64
+ interruptPendingKandanMessages,
65
+ listPendingKandanMessages,
66
+ pendingKandanMessageQueueHasSeq,
67
+ pendingKandanMessageQueueLength,
68
+ requeuePendingKandanMessageFront,
69
+ } from './src/pendingKandanMessageQueue';
70
+ import {
71
+ commitAppliedSourceSeq,
72
+ createInFlightSourceSeqGuard,
73
+ highestProcessedSourceSeq,
74
+ parseResumeReconcileEntries,
75
+ parseServerMessageStateRows,
76
+ reconcileAppliedSourceSeq,
77
+ resumeReconcileServerRows,
78
+ shouldApplySourceSeq,
79
+ sourceSeqAppliedOrInFlight,
80
+ threadSourceSeqWatermarkKey,
81
+ } from './src/appliedSourceSeqWatermark';
82
+ import {
83
+ createDurableInboundThreadQueue,
84
+ defaultDeadLetterPolicy,
85
+ durableInboundQueuePath,
86
+ foldQueueRecords,
87
+ shouldDeadLetter,
88
+ } from './src/durableInboundQueue';
89
+ import {
90
+ controlQuarantineFilePath,
91
+ createControlQuarantine,
92
+ fileWithheldControlAckStore,
93
+ withheldControlAckStorePath,
94
+ } from './src/controlQuarantine';
95
+ import {
96
+ outboxJournalPath,
97
+ recoverOutboxJournal,
98
+ } from './src/pipeline/outboxJournal';
99
+ import { createThreadOutbox } from './src/pipeline/outbox';
100
+ import {
101
+ applyResumeDispatchOutcome,
102
+ commitLiveAcceptedSourceSeq,
103
+ drainResumeQueue,
104
+ receiveResumeMessage,
105
+ } from './src/resumeKeystone';
106
+ import { createLeaseEpochRegistry, isFencedClaim } from './src/leaseEpochGuard';
107
+ import { createThreadReconcilerLock } from './src/threadReconcilerLock';
108
+
109
+ // --- Placeholder + snapshot plumbing --------------------------------------------
110
+
111
+ const dirPlaceholder = '$DIR';
112
+
113
+ function scratch() {
114
+ // realpath so macOS /var -> /private/var never splits the two impls'
115
+ // placeholder substitution.
116
+ return realpathSync(mkdtempSync(join(tmpdir(), 'linzumi-queue-oracle-')));
117
+ }
118
+
119
+ function substitute(value, from, to) {
120
+ if (typeof value === 'string') {
121
+ return value.split(from).join(to);
122
+ }
123
+ if (Array.isArray(value)) {
124
+ return value.map((entry) => substitute(entry, from, to));
125
+ }
126
+ if (typeof value === 'object' && value !== null) {
127
+ return Object.fromEntries(
128
+ Object.entries(value).map(([key, entry]) => [
129
+ substitute(key, from, to),
130
+ substitute(entry, from, to),
131
+ ])
132
+ );
133
+ }
134
+ return value;
135
+ }
136
+
137
+ function snapshotFiles(dir) {
138
+ const files = {};
139
+ const walk = (current) => {
140
+ for (const name of readdirSync(current).sort()) {
141
+ const full = join(current, name);
142
+ if (statSync(full).isDirectory()) {
143
+ walk(full);
144
+ } else {
145
+ files[relative(dir, full)] = readFileSync(full, 'utf8');
146
+ }
147
+ }
148
+ };
149
+ walk(dir);
150
+ return files;
151
+ }
152
+
153
+ function outcome(run) {
154
+ try {
155
+ const value = run();
156
+ return value === undefined ? { defined: false } : { defined: true, value };
157
+ } catch (error) {
158
+ return {
159
+ threw: true,
160
+ message: error instanceof Error ? error.message : String(error),
161
+ };
162
+ }
163
+ }
164
+
165
+ function collectingLog(events, dir) {
166
+ return (event, payload) => {
167
+ events.push({ event, payload: substitute(payload, dir, dirPlaceholder) });
168
+ };
169
+ }
170
+
171
+ // --- Queued-message pure transforms ------------------------------------------------
172
+
173
+ function handleQueuedMessage(testCase) {
174
+ switch (testCase.fn) {
175
+ case 'normalizeCodexSteerInput':
176
+ return outcome(() => normalizeCodexSteerInput(testCase.input));
177
+ case 'steerInputText':
178
+ return outcome(() => steerInputText(testCase.input));
179
+ case 'format':
180
+ return outcome(() => formatQueuedKandanMessage(testCase.message));
181
+ case 'codexInput':
182
+ return outcome(() => codexInputForQueuedKandanMessage(testCase.message));
183
+ case 'sourceSeqs':
184
+ return outcome(() => queuedKandanMessageSourceSeqs(testCase.message));
185
+ case 'interrupt':
186
+ return outcome(() =>
187
+ interruptQueuedMessages(testCase.queue, testCase.throughSeq ?? undefined)
188
+ );
189
+ default:
190
+ return { threw: true, message: 'unknown queuedMessage fn ' + String(testCase.fn) };
191
+ }
192
+ }
193
+
194
+ // --- Pending FIFO ---------------------------------------------------------------------
195
+
196
+ function handlePendingQueue(testCase) {
197
+ const queue = createPendingKandanMessageQueue();
198
+ const results = (testCase.actions ?? []).map((action) => {
199
+ switch (action.fn) {
200
+ case 'enqueue':
201
+ return outcome(() => enqueuePendingKandanMessage(queue, action.message));
202
+ case 'dequeue':
203
+ return outcome(() => dequeuePendingKandanMessage(queue));
204
+ case 'requeueFront':
205
+ return outcome(() => requeuePendingKandanMessageFront(queue, action.message));
206
+ case 'drainAll':
207
+ return outcome(() => drainAllPendingKandanMessages(queue));
208
+ case 'interrupt':
209
+ return outcome(() =>
210
+ interruptPendingKandanMessages(queue, action.throughSeq ?? undefined)
211
+ );
212
+ case 'hasSeq':
213
+ return outcome(() => pendingKandanMessageQueueHasSeq(queue, action.seq));
214
+ case 'list':
215
+ return outcome(() => listPendingKandanMessages(queue));
216
+ case 'length':
217
+ return outcome(() => pendingKandanMessageQueueLength(queue));
218
+ default:
219
+ return { threw: true, message: 'unknown pendingQueue fn ' + String(action.fn) };
220
+ }
221
+ });
222
+ return { results };
223
+ }
224
+
225
+ // --- Watermark pure semantics -----------------------------------------------------------
226
+
227
+ function watermarkMemStore(calls) {
228
+ const map = new Map();
229
+ return {
230
+ initial: (key) => map.get(key),
231
+ onAdvance: (key, seq) => {
232
+ calls.push({ call: 'onAdvance', key, seq });
233
+ const existing = map.get(key);
234
+ if (existing === undefined || seq > existing) {
235
+ map.set(key, seq);
236
+ }
237
+ },
238
+ flush: () => {
239
+ calls.push({ call: 'flush' });
240
+ },
241
+ dump: () => Object.fromEntries(map),
242
+ };
243
+ }
244
+
245
+ function handleWatermark(testCase) {
246
+ switch (testCase.fn) {
247
+ case 'shouldApply':
248
+ return outcome(() =>
249
+ shouldApplySourceSeq(testCase.applied ?? undefined, testCase.sourceSeq)
250
+ );
251
+ case 'key':
252
+ return outcome(() =>
253
+ threadSourceSeqWatermarkKey(testCase.workspace, testCase.channel, testCase.threadId)
254
+ );
255
+ case 'highestProcessed':
256
+ return outcome(() => highestProcessedSourceSeq(testCase.rows));
257
+ case 'reconcile':
258
+ return outcome(() =>
259
+ reconcileAppliedSourceSeq(testCase.local ?? undefined, testCase.rows)
260
+ );
261
+ case 'parseRows':
262
+ return outcome(() => parseServerMessageStateRows(testCase.value));
263
+ case 'parseResumeReconcile':
264
+ return outcome(() => parseResumeReconcileEntries(testCase.value));
265
+ case 'resumeReconcileRows':
266
+ return outcome(() => resumeReconcileServerRows(testCase.entry));
267
+ case 'isFencedClaim':
268
+ return outcome(() =>
269
+ isFencedClaim(testCase.held ?? undefined, testCase.control ?? undefined)
270
+ );
271
+ case 'guardScenario': {
272
+ const calls = [];
273
+ const store = watermarkMemStore(calls);
274
+ const guard = createInFlightSourceSeqGuard();
275
+ const results = (testCase.actions ?? []).map((action) => {
276
+ switch (action.fn) {
277
+ case 'mark':
278
+ return outcome(() => guard.mark(action.key, action.seq));
279
+ case 'has':
280
+ return outcome(() => guard.has(action.key, action.seq));
281
+ case 'clearSeq':
282
+ return outcome(() => guard.clearSeq(action.key, action.seq));
283
+ case 'clearThread':
284
+ return outcome(() => guard.clearThread(action.key));
285
+ case 'commit':
286
+ return outcome(() => commitAppliedSourceSeq(store, action.key, action.seq));
287
+ case 'commitLiveAccepted':
288
+ return outcome(() =>
289
+ commitLiveAcceptedSourceSeq({
290
+ store,
291
+ watermarkKey: action.key,
292
+ sourceSeq: action.seq,
293
+ })
294
+ );
295
+ case 'appliedOrInFlight':
296
+ return outcome(() =>
297
+ sourceSeqAppliedOrInFlight(
298
+ store.initial(action.key),
299
+ guard,
300
+ action.key,
301
+ action.seq
302
+ )
303
+ );
304
+ default:
305
+ return { threw: true, message: 'unknown guard fn ' + String(action.fn) };
306
+ }
307
+ });
308
+ return { results, storeCalls: calls, store: store.dump() };
309
+ }
310
+ default:
311
+ return { threw: true, message: 'unknown watermark fn ' + String(testCase.fn) };
312
+ }
313
+ }
314
+
315
+ // --- Durable inbound queue --------------------------------------------------------------
316
+
317
+ function normalizeEntry(entry) {
318
+ return entry === undefined ? undefined : { ...entry };
319
+ }
320
+
321
+ function handleInboundQueue(testCase) {
322
+ if (testCase.fn === 'foldLines') {
323
+ return outcome(() => foldQueueRecords(testCase.lines));
324
+ }
325
+ if (testCase.fn === 'shouldDeadLetter') {
326
+ return outcome(() =>
327
+ shouldDeadLetter(testCase.entry, testCase.policy ?? defaultDeadLetterPolicy, testCase.nowMs)
328
+ );
329
+ }
330
+ if (testCase.fn === 'path') {
331
+ process.env.LINZUMI_INBOUND_QUEUE_DIR = testCase.envDir ?? '';
332
+ const result = outcome(() =>
333
+ durableInboundQueuePath(testCase.workspace, testCase.channel, testCase.threadId)
334
+ );
335
+ delete process.env.LINZUMI_INBOUND_QUEUE_DIR;
336
+ return result;
337
+ }
338
+ // Scripted lifecycle in a scratch dir. fsync spies record ORDERING facts
339
+ // (an 'fsync' event lands in the stream before the action result is
340
+ // recorded), proving fsync-before-return (#2974) on BOTH impls.
341
+ const dir = scratch();
342
+ const path = join(dir, 'thread.log');
343
+ if (testCase.initialContents !== undefined) {
344
+ writeFileSync(path, testCase.initialContents, 'utf8');
345
+ }
346
+ const events = [];
347
+ const log = collectingLog(events, dir);
348
+ const deps = {
349
+ fsyncFd: () => {
350
+ events.push({ event: 'spy.fsync_data' });
351
+ },
352
+ fsyncDirFd: () => {
353
+ events.push({ event: 'spy.fsync_dir' });
354
+ },
355
+ };
356
+ let queue = createDurableInboundThreadQueue(path, log, deps);
357
+ const results = (testCase.actions ?? []).map((action) => {
358
+ switch (action.fn) {
359
+ case 'enqueue': {
360
+ const result = outcome(() =>
361
+ queue.enqueue(action.sourceSeq, action.kind ?? 'live', action.payload ?? {}, action.nowMs)
362
+ );
363
+ events.push({ event: 'action.enqueue_returned', sourceSeq: action.sourceSeq });
364
+ return result;
365
+ }
366
+ case 'ack':
367
+ return outcome(() => queue.ack(action.sourceSeq));
368
+ case 'recordAttempt':
369
+ return outcome(() => queue.recordAttempt(action.sourceSeq));
370
+ case 'deadLetter':
371
+ return outcome(() => normalizeEntry(queue.deadLetter(action.sourceSeq)));
372
+ case 'compact':
373
+ return outcome(() => queue.compact());
374
+ case 'entries':
375
+ return outcome(() => queue.entries().map(normalizeEntry));
376
+ case 'ackedEntries':
377
+ return outcome(() => queue.ackedEntries().map(normalizeEntry));
378
+ case 'head':
379
+ return outcome(() => normalizeEntry(queue.head()));
380
+ case 'highWater':
381
+ return outcome(() => queue.highWaterSourceSeq());
382
+ case 'tornTail':
383
+ return outcome(() => queue.tornTail());
384
+ case 'reconciledTornTail':
385
+ return outcome(() => queue.reconciledTornTail());
386
+ case 'appendRaw':
387
+ // Simulate a crash-torn tail / foreign bytes.
388
+ return outcome(() => appendFileSync(path, action.raw, 'utf8'));
389
+ case 'reload': {
390
+ // Simulated SIGKILL + restart: a fresh handle folds the same file.
391
+ queue = createDurableInboundThreadQueue(path, log, deps);
392
+ return { defined: false };
393
+ }
394
+ default:
395
+ return { threw: true, message: 'unknown inboundQueue fn ' + String(action.fn) };
396
+ }
397
+ });
398
+ return substitute(
399
+ { results, events, files: snapshotFiles(dir) },
400
+ dir,
401
+ dirPlaceholder
402
+ );
403
+ }
404
+
405
+ // --- Control quarantine -------------------------------------------------------------------
406
+
407
+ function handleQuarantine(testCase) {
408
+ if (testCase.fn === 'paths') {
409
+ return {
410
+ store: outcome(() => withheldControlAckStorePath('$STATE', testCase.runnerId)),
411
+ quarantine: outcome(() => controlQuarantineFilePath('$STATE', testCase.runnerId)),
412
+ };
413
+ }
414
+ const dir = scratch();
415
+ const storePath = join(dir, 'withheld-acks.json');
416
+ const quarantinePath = join(dir, 'quarantined-controls.jsonl');
417
+ if (testCase.initialStoreContents !== undefined) {
418
+ writeFileSync(storePath, testCase.initialStoreContents, 'utf8');
419
+ }
420
+ if (testCase.initialQuarantineContents !== undefined) {
421
+ writeFileSync(quarantinePath, testCase.initialQuarantineContents, 'utf8');
422
+ }
423
+ const events = [];
424
+ const emitted = [];
425
+ const log = collectingLog(events, dir);
426
+ const writeErrors = [];
427
+ const store = fileWithheldControlAckStore(storePath, (message) =>
428
+ writeErrors.push(message)
429
+ );
430
+ let epoch = testCase.epoch ?? undefined;
431
+ const quarantine = createControlQuarantine({
432
+ store,
433
+ quarantineFilePath: quarantinePath,
434
+ log,
435
+ emitRunnerEvent: (payload) => {
436
+ emitted.push(substitute(payload, dir, dirPlaceholder));
437
+ },
438
+ currentEpoch: () => epoch,
439
+ ...(testCase.limit === undefined ? {} : { limit: testCase.limit }),
440
+ now: () => new Date(testCase.nowMs ?? 0),
441
+ });
442
+ const results = (testCase.rejections ?? []).map((rejection) => {
443
+ if (rejection.setEpoch !== undefined) {
444
+ epoch = rejection.setEpoch === null ? undefined : rejection.setEpoch;
445
+ }
446
+ return outcome(() =>
447
+ quarantine.onControlRejected(
448
+ rejection.control,
449
+ new Error(rejection.message ?? 'scripted rejection')
450
+ )
451
+ );
452
+ });
453
+ return substitute(
454
+ { results, events, emitted, writeErrors, files: snapshotFiles(dir) },
455
+ dir,
456
+ dirPlaceholder
457
+ );
458
+ }
459
+
460
+ // --- Outbox journal (recover-from-bytes) -----------------------------------------------------
461
+
462
+ function normalizeRecovered(recovered) {
463
+ return {
464
+ pendingEntries: recovered.pendingEntries,
465
+ kv: Object.fromEntries(recovered.kv),
466
+ counters: recovered.counters,
467
+ };
468
+ }
469
+
470
+ function handleOutboxJournal(testCase) {
471
+ if (testCase.fn === 'path') {
472
+ return outcome(() => outboxJournalPath('$PERSIST', testCase.threadKey));
473
+ }
474
+ // recover: fold the given journal bytes.
475
+ const dir = scratch();
476
+ const path = join(dir, 'thread.outbox.jsonl');
477
+ if (testCase.contents !== undefined) {
478
+ writeFileSync(path, testCase.contents, 'utf8');
479
+ }
480
+ const events = [];
481
+ const recovered = outcome(() =>
482
+ normalizeRecovered(recoverOutboxJournal(path, collectingLog(events, dir)))
483
+ );
484
+ return substitute({ recovered, events }, dir, dirPlaceholder);
485
+ }
486
+
487
+ // --- Outbox flow scenarios ---------------------------------------------------------------------
488
+
489
+ function buildersFromSpec(spec, recordedReplies) {
490
+ const builders = {};
491
+ for (const [kind, config] of Object.entries(spec ?? {})) {
492
+ builders[kind] = {
493
+ build: (data) => {
494
+ if (config.mode === 'skip') {
495
+ return { skip: true, reason: String(data.skipReason ?? 'scripted-skip') };
496
+ }
497
+ if (config.mode === 'fail') {
498
+ throw new Error(String(data.failMessage ?? 'scripted-build-failure'));
499
+ }
500
+ return {
501
+ event: 'evt:' + kind,
502
+ payload: { ...data },
503
+ ...(config.timeoutMs === undefined ? {} : { timeoutMs: config.timeoutMs }),
504
+ };
505
+ },
506
+ ...(config.onReply === 'record'
507
+ ? {
508
+ onReply: (data, reply) => {
509
+ recordedReplies.push({ kind, data, reply });
510
+ },
511
+ }
512
+ : {}),
513
+ ...(config.coalesce === 'concat-bodies'
514
+ ? {
515
+ coalesce: (older, newer) => ({
516
+ ...newer,
517
+ body: String(older.body ?? '') + '|' + String(newer.body ?? ''),
518
+ }),
519
+ }
520
+ : {}),
521
+ };
522
+ }
523
+ return builders;
524
+ }
525
+
526
+ async function runOutboxPhase(phase, shared) {
527
+ // The outbox unrefs its retry timers by design (a pending backoff must
528
+ // never hold the runner process open), so a one-shot driver awaiting a
529
+ // flush across a backoff would see its event loop empty. Keep it alive
530
+ // for the phase.
531
+ const keepAlive = setInterval(() => {}, 25);
532
+ try {
533
+ return await runOutboxPhaseInner(phase, shared);
534
+ } finally {
535
+ clearInterval(keepAlive);
536
+ }
537
+ }
538
+
539
+ async function runOutboxPhaseInner(phase, shared) {
540
+ const pushScript = [...(phase.pushScript ?? [])];
541
+ const pushes = [];
542
+ const permanentFailures = [];
543
+ const recordedReplies = [];
544
+ const events = [];
545
+ const log = collectingLog(events, shared.dir);
546
+ let nowMs = phase.nowMs ?? 1_000;
547
+ const outbox = createThreadOutbox({
548
+ threadKey: phase.threadKey ?? shared.threadKey,
549
+ push: async (event, payload, timeoutMs, onSent) => {
550
+ const step = pushScript.shift() ?? { kind: 'fail', message: 'push script exhausted' };
551
+ pushes.push({
552
+ event,
553
+ payload,
554
+ hasTimeout: timeoutMs !== undefined,
555
+ });
556
+ onSent?.();
557
+ if (step.delayMs !== undefined) {
558
+ await new Promise((resolve) => setTimeout(resolve, step.delayMs));
559
+ }
560
+ if (step.kind === 'ack') {
561
+ return step.reply ?? { ok: true };
562
+ }
563
+ const failure = new Error(String(step.message ?? 'scripted push failure'));
564
+ if (step.budgetExempt === true) {
565
+ failure.name = 'ScriptedBudgetExempt';
566
+ }
567
+ throw failure;
568
+ },
569
+ builders: buildersFromSpec(phase.builders ?? shared.builders, recordedReplies),
570
+ persistDir: shared.persistDir,
571
+ retryDelaysMs: phase.retryDelaysMs ?? [1, 1],
572
+ isRetryBudgetExempt: (error) => error.name === 'ScriptedBudgetExempt',
573
+ onPermanentFailure: (entry, error) => {
574
+ permanentFailures.push({
575
+ entrySeq: entry.entrySeq,
576
+ kind: entry.kind,
577
+ error: error.message,
578
+ });
579
+ },
580
+ log,
581
+ now: () => nowMs,
582
+ ...(phase.maxInFlightPushes === undefined
583
+ ? {}
584
+ : { maxInFlightPushes: phase.maxInFlightPushes }),
585
+ ...(phase.journalCompactionThreshold === undefined
586
+ ? {}
587
+ : { journalCompactionThreshold: phase.journalCompactionThreshold }),
588
+ ...(phase.journalRecoveryMaxAgeMs === undefined
589
+ ? {}
590
+ : { journalRecoveryMaxAgeMs: phase.journalRecoveryMaxAgeMs }),
591
+ });
592
+
593
+ const results = [];
594
+ for (const action of phase.actions ?? []) {
595
+ switch (action.fn) {
596
+ case 'advanceNow':
597
+ nowMs += action.byMs;
598
+ results.push({ defined: false });
599
+ break;
600
+ case 'enqueue':
601
+ results.push(
602
+ outcome(() =>
603
+ outbox.enqueue({
604
+ kind: action.kind,
605
+ data: action.data ?? {},
606
+ ...(action.coalesceKey === undefined ? {} : { coalesceKey: action.coalesceKey }),
607
+ resolution: action.resolution ?? 'must_ack',
608
+ })
609
+ )
610
+ );
611
+ break;
612
+ case 'pause':
613
+ results.push(outcome(() => outbox.pause()));
614
+ break;
615
+ case 'resume':
616
+ results.push(outcome(() => outbox.resume()));
617
+ break;
618
+ case 'flush': {
619
+ try {
620
+ await outbox.flush(action.timeoutMs);
621
+ results.push({ flushed: true });
622
+ } catch (error) {
623
+ results.push({
624
+ flushed: false,
625
+ message: error instanceof Error ? error.message : String(error),
626
+ });
627
+ }
628
+ break;
629
+ }
630
+ case 'metrics': {
631
+ const metrics = outbox.metrics();
632
+ results.push({
633
+ defined: true,
634
+ value: {
635
+ depth: metrics.depth,
636
+ totalEnqueued: metrics.totalEnqueued,
637
+ totalAcked: metrics.totalAcked,
638
+ totalRetried: metrics.totalRetried,
639
+ totalDroppedBestEffort: metrics.totalDroppedBestEffort,
640
+ totalPermanentFailures: metrics.totalPermanentFailures,
641
+ totalExpiredOnRecovery: metrics.totalExpiredOnRecovery,
642
+ journalDegraded: metrics.journalDegraded,
643
+ paused: metrics.paused,
644
+ ackedInWindow: metrics.pushLatency?.ackedInWindow ?? 0,
645
+ },
646
+ });
647
+ break;
648
+ }
649
+ case 'journalSettled':
650
+ await outbox.journalSettled();
651
+ results.push({ defined: false });
652
+ break;
653
+ case 'rekeyJournal':
654
+ results.push(outcome(() => outbox.rekeyJournal(action.threadKey)));
655
+ await outbox.journalSettled();
656
+ break;
657
+ case 'sleep':
658
+ await new Promise((resolve) => setTimeout(resolve, action.ms));
659
+ results.push({ defined: false });
660
+ break;
661
+ case 'close':
662
+ await outbox.close(action.timeoutMs);
663
+ results.push({ defined: false });
664
+ break;
665
+ default:
666
+ results.push({ threw: true, message: 'unknown outbox fn ' + String(action.fn) });
667
+ }
668
+ }
669
+ // Make phase end deterministic: everything journaled so far is on disk.
670
+ await outbox.journalSettled();
671
+ return {
672
+ results,
673
+ pushes,
674
+ permanentFailures,
675
+ recordedReplies,
676
+ // Log EVENT NAMES in order (payloads carry timing values that legally
677
+ // differ across impls; names + counts are the stable contract).
678
+ logEvents: events.map((entry) => entry.event),
679
+ };
680
+ }
681
+
682
+ async function handleOutboxScenario(testCase) {
683
+ const dir = scratch();
684
+ // Cross-impl seed: files the OTHER impl wrote (journal bytes), laid down
685
+ // before any phase so recovery reads foreign-written bytes.
686
+ for (const [relPath, contents] of Object.entries(testCase.initialFiles ?? {})) {
687
+ const full = join(dir, relPath);
688
+ mkdirSync(join(full, '..'), { recursive: true });
689
+ writeFileSync(full, contents, 'utf8');
690
+ }
691
+ const shared = {
692
+ dir,
693
+ persistDir: testCase.inMemory === true ? undefined : join(dir, 'commander-outbox'),
694
+ threadKey: testCase.threadKey ?? 'linzumi:42:thread-a',
695
+ builders: testCase.builders ?? {},
696
+ };
697
+ const phases = [];
698
+ for (const phase of testCase.phases ?? []) {
699
+ phases.push(await runOutboxPhase(phase, shared));
700
+ }
701
+ return substitute(
702
+ { phases, files: snapshotFiles(dir) },
703
+ dir,
704
+ dirPlaceholder
705
+ );
706
+ }
707
+
708
+ // --- Resume keystone flow scenarios ----------------------------------------------------------
709
+
710
+ async function handleKeystoneScenario(testCase) {
711
+ const dir = scratch();
712
+ const path = join(dir, 'thread.log');
713
+ if (testCase.initialContents !== undefined) {
714
+ writeFileSync(path, testCase.initialContents, 'utf8');
715
+ }
716
+ const events = [];
717
+ const log = collectingLog(events, dir);
718
+ const queue = createDurableInboundThreadQueue(path, log);
719
+ const storeCalls = [];
720
+ const store = watermarkMemStore(storeCalls);
721
+ if (typeof testCase.initialWatermark === 'number') {
722
+ store.onAdvance(testCase.watermarkKey, testCase.initialWatermark);
723
+ storeCalls.length = 0;
724
+ }
725
+ const lock = createThreadReconcilerLock();
726
+ const leases = createLeaseEpochRegistry();
727
+ const guard = createInFlightSourceSeqGuard();
728
+ const deadLetters = [];
729
+ const dispatchScript = [...(testCase.dispatchScript ?? [])];
730
+ const dispatchCalls = [];
731
+ const dispatch = async (sourceSeq, payload) => {
732
+ dispatchCalls.push({ sourceSeq, payload });
733
+ const step = dispatchScript.shift() ?? { outcome: 'ignored' };
734
+ return step.outcome;
735
+ };
736
+ const onDeadLetter = (args) => {
737
+ deadLetters.push({
738
+ threadId: args.threadId,
739
+ sourceSeq: args.sourceSeq,
740
+ reason: args.reason,
741
+ attempts: args.attempts,
742
+ alreadyPublished: args.alreadyPublished,
743
+ });
744
+ };
745
+ const results = [];
746
+ for (const step of testCase.steps ?? []) {
747
+ if (step.fn === 'receive') {
748
+ const outcomeValue = await receiveResumeMessage({
749
+ queue,
750
+ store,
751
+ lock,
752
+ leases,
753
+ threadId: testCase.threadId,
754
+ watermarkKey: testCase.watermarkKey,
755
+ sourceSeq: step.sourceSeq,
756
+ kind: step.kind ?? 'live',
757
+ payload: step.payload ?? {},
758
+ leaseEpoch: step.leaseEpoch ?? undefined,
759
+ dispatch,
760
+ serverRows: step.serverRows ?? [],
761
+ log,
762
+ nowMs: step.nowMs ?? 1_000,
763
+ ...(step.policy === undefined ? {} : { policy: step.policy }),
764
+ onlyDispatchUnattempted: step.onlyDispatchUnattempted ?? false,
765
+ onDeadLetter,
766
+ inFlightGuard: guard,
767
+ });
768
+ results.push({ receive: outcomeValue });
769
+ } else if (step.fn === 'drain') {
770
+ const dispatched = await drainResumeQueue({
771
+ queue,
772
+ store,
773
+ lock,
774
+ threadId: testCase.threadId,
775
+ watermarkKey: testCase.watermarkKey,
776
+ serverRows: step.serverRows ?? [],
777
+ dispatch,
778
+ log,
779
+ nowMs: step.nowMs ?? 1_000,
780
+ ...(step.policy === undefined ? {} : { policy: step.policy }),
781
+ onlyDispatchUnattempted: step.onlyDispatchUnattempted ?? false,
782
+ onTornTailUnrecoverable: (threadId) => {
783
+ deadLetters.push({ threadId, tornTailSurfaced: true });
784
+ },
785
+ onDeadLetter,
786
+ inFlightGuard: guard,
787
+ });
788
+ results.push({ drained: dispatched });
789
+ } else if (step.fn === 'executionCommit') {
790
+ // The turn EXECUTED to a terminal: the live path commits the floor
791
+ // (I58 execution-confirm) and clears the in-flight guard.
792
+ const advanced = commitLiveAcceptedSourceSeq({
793
+ store,
794
+ watermarkKey: testCase.watermarkKey,
795
+ sourceSeq: step.sourceSeq,
796
+ });
797
+ guard.clearSeq(testCase.watermarkKey, step.sourceSeq);
798
+ results.push({ executionCommit: advanced });
799
+ } else if (step.fn === 'workerDeath') {
800
+ guard.clearThread(testCase.watermarkKey);
801
+ results.push({ workerDeath: true });
802
+ } else if (step.fn === 'applyOutcome') {
803
+ await applyResumeDispatchOutcome({
804
+ queue,
805
+ store,
806
+ watermarkKey: testCase.watermarkKey,
807
+ sourceSeq: step.sourceSeq,
808
+ threadId: testCase.threadId,
809
+ outcome: step.outcome,
810
+ log,
811
+ nowMs: step.nowMs ?? 1_000,
812
+ ...(step.policy === undefined ? {} : { policy: step.policy }),
813
+ onDeadLetter,
814
+ inFlightGuard: guard,
815
+ });
816
+ results.push({ applied: step.outcome });
817
+ } else {
818
+ results.push({ threw: true, message: 'unknown keystone fn ' + String(step.fn) });
819
+ }
820
+ }
821
+ return substitute(
822
+ {
823
+ results,
824
+ dispatchCalls,
825
+ deadLetters,
826
+ storeCalls,
827
+ store: store.dump(),
828
+ liveEntries: queue.entries().map((entry) => ({ ...entry })),
829
+ ackedEntries: queue.ackedEntries().map((entry) => ({ ...entry })),
830
+ logEvents: events.map((entry) => entry.event),
831
+ files: snapshotFiles(dir),
832
+ },
833
+ dir,
834
+ dirPlaceholder
835
+ );
836
+ }
837
+
838
+ // --- Dispatch --------------------------------------------------------------------------------
839
+
840
+ async function handle(testCase) {
841
+ switch (testCase.op) {
842
+ case 'queuedMessage':
843
+ return handleQueuedMessage(testCase);
844
+ case 'pendingQueue':
845
+ return handlePendingQueue(testCase);
846
+ case 'watermark':
847
+ return handleWatermark(testCase);
848
+ case 'inboundQueue':
849
+ return handleInboundQueue(testCase);
850
+ case 'quarantine':
851
+ return handleQuarantine(testCase);
852
+ case 'outboxJournal':
853
+ return handleOutboxJournal(testCase);
854
+ case 'outboxScenario':
855
+ return await handleOutboxScenario(testCase);
856
+ case 'keystoneScenario':
857
+ return await handleKeystoneScenario(testCase);
858
+ default:
859
+ return { threw: true, message: 'unknown op ' + String(testCase.op) };
860
+ }
861
+ }
862
+
863
+ let stdin = '';
864
+ process.stdin.setEncoding('utf8');
865
+ for await (const chunk of process.stdin) {
866
+ stdin += chunk;
867
+ }
868
+ const cases = JSON.parse(stdin);
869
+ const results = [];
870
+ for (const testCase of cases) {
871
+ results.push(await handle(testCase));
872
+ }
873
+ // Exit only after stdout is fully flushed: scripted outbox scenarios can
874
+ // leave unref'd timers alive, so the driver exits explicitly - but a bare
875
+ // process.exit(0) truncates large result arrays mid-pipe.
876
+ process.stdout.write(JSON.stringify(results), () => {
877
+ process.exit(0);
878
+ });
879
+ `;
880
+
881
+ await mkdir(join(packageRoot, '.differential'), { recursive: true });
882
+
883
+ await build({
884
+ stdin: {
885
+ contents: driverSource,
886
+ resolveDir: packageRoot,
887
+ sourcefile: 'queue-parity-driver.ts',
888
+ loader: 'ts',
889
+ },
890
+ bundle: true,
891
+ platform: 'node',
892
+ target: 'node20',
893
+ format: 'esm',
894
+ outfile: join(packageRoot, '.differential/queue-parity-oracle.mjs'),
895
+ minify: false,
896
+ sourcemap: false,
897
+ legalComments: 'none',
898
+ external: ['ws', 'undici', 'blessed', '@anthropic-ai/claude-agent-sdk'],
899
+ });
@@ -12,7 +12,7 @@
12
12
  // publishes @linzumi/cli@<new-version> to npm.
13
13
  //
14
14
  // The package version is pinned in several places (package.json,
15
- // src/version.ts, README.md, and version-pinning tests). This script rewrites
15
+ // src/version.ts, README.md, the public alias manifest, and version-pinning tests). This script rewrites
16
16
  // every known site, FAILS LOUDLY if any expected pattern is missing, and then
17
17
  // scans package.json/src/README/test/scripts/bin for any leftover
18
18
  // old-version string - so a future, not-yet-listed pin site cannot silently
@@ -71,6 +71,7 @@ if (!/^\d+\.\d+\.\d+(-[0-9A-Za-z][0-9A-Za-z.-]*)?$/.test(newVersion)) {
71
71
 
72
72
  const packageRoot =
73
73
  explicitRoot ?? dirname(dirname(fileURLToPath(import.meta.url)));
74
+ const aliasPackageRoot = join(dirname(packageRoot), 'linzumi');
74
75
 
75
76
  let manifest;
76
77
 
@@ -96,6 +97,24 @@ if (oldVersion === newVersion) {
96
97
  // the bump aborts; add new entries here when a new pin site appears (the
97
98
  // leftover scan below is what tells you one appeared).
98
99
  const knownSites = [
100
+ {
101
+ file: '../linzumi/package.json',
102
+ description: 'public alias manifest version',
103
+ pattern: `"version": "${oldVersion}"`,
104
+ replacement: `"version": "${newVersion}"`,
105
+ },
106
+ {
107
+ file: '../linzumi/package.json',
108
+ description: 'public alias exact canonical dependency',
109
+ pattern: `"@linzumi/cli": "${oldVersion}"`,
110
+ replacement: `"@linzumi/cli": "${newVersion}"`,
111
+ },
112
+ {
113
+ file: '../../pnpm-lock.yaml',
114
+ description: 'public alias lockfile dependency specifier',
115
+ pattern: `specifier: ${oldVersion}\n version: link:../linzumi-cli`,
116
+ replacement: `specifier: ${newVersion}\n version: link:../linzumi-cli`,
117
+ },
99
118
  {
100
119
  file: 'package.json',
101
120
  description: 'npm package manifest version',
@@ -209,6 +228,11 @@ function scanTargets() {
209
228
  const roots = ['src', 'test', 'scripts', 'bin'];
210
229
  const files = ['package.json', 'README.md'];
211
230
 
231
+ // Spec: plans/2026-07-12-linzumi-unscoped-npm-package-spec.md
232
+ // The alias version and canonical dependency are one release invariant.
233
+ files.push(relative(packageRoot, join(aliasPackageRoot, 'package.json')));
234
+ files.push('../../pnpm-lock.yaml');
235
+
212
236
  for (const root of roots) {
213
237
  const rootPath = join(packageRoot, root);
214
238
  let entries;