@volter/supercode-orchestrator 0.4.118 → 0.4.120
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/board/dispatch.mjs +105 -34
- package/package.json +1 -1
package/board/dispatch.mjs
CHANGED
|
@@ -116,11 +116,13 @@ export function observeFleet() {
|
|
|
116
116
|
const list = sc(['message', 'list', '--json']);
|
|
117
117
|
const sessions = list.code === 0 ? json(list.stdout.trim(), null) : null;
|
|
118
118
|
if (!Array.isArray(sessions)) return { ok: false, alive: new Set(), online: new Set(), error: `message list: ${(list.stderr || list.stdout).trim().slice(-300)}` };
|
|
119
|
-
const machines = sc(['teams', 'machines', '
|
|
120
|
-
const
|
|
119
|
+
const machines = sc(['teams', 'machines', 'events', '--json']);
|
|
120
|
+
const presence = machines.code === 0 ? lastJson(machines.stdout) : null;
|
|
121
121
|
const online = new Set([localMachine()]);
|
|
122
|
-
for (const m of
|
|
123
|
-
return { ok: true, sessions, alive: new Set(sessions.map((s) => s.address).filter(Boolean)), online,
|
|
122
|
+
for (const m of presence?.items ?? []) if (m.linked) online.add(normalMachine(m.name));
|
|
123
|
+
return { ok: true, sessions, alive: new Set(sessions.map((s) => s.address).filter(Boolean)), online,
|
|
124
|
+
presenceKnown: presence?.connected === true, machineEvents: presence,
|
|
125
|
+
error: presence?.connected ? null : 'machine event stream unavailable; retaining existing runs until availability is known' };
|
|
124
126
|
}
|
|
125
127
|
|
|
126
128
|
// how the launcher opens each harness a pane lane can run, and how it names the session: Claude Code is told the id
|
|
@@ -199,27 +201,39 @@ export function lanesOf(profiles = {}) {
|
|
|
199
201
|
return out;
|
|
200
202
|
}
|
|
201
203
|
|
|
204
|
+
const onMachine = machine => normalMachine(machine ?? localMachine()) === localMachine() ? [] : ['--machine', machine];
|
|
205
|
+
const unavailable = message => Object.assign(new Error(message), { launchDeferred: true });
|
|
206
|
+
|
|
202
207
|
// ------------------------------------------------------------------ supervise
|
|
203
208
|
|
|
204
209
|
function supervise(board, fleet, settings, report, entry, effects) {
|
|
205
210
|
const rows = board.db.prepare(entry.workflow.run ? "SELECT id FROM tasks WHERE current_run_id IS NOT NULL" : "SELECT id FROM tasks WHERE status = 'running'").all();
|
|
206
211
|
// The catalog can lag a pane. Read the actual machines once, before any board mutation.
|
|
207
212
|
const observed = new Map();
|
|
213
|
+
const exits = new Map();
|
|
208
214
|
for (const { id } of rows) {
|
|
209
215
|
const s = currentRun(board.db, id)?.metadata?.supercode;
|
|
210
216
|
if (!s?.pane || observed.has(s.machine)) continue;
|
|
211
|
-
const r = sc(['teams', 'panes', 'ls',
|
|
217
|
+
const r = sc(['teams', 'panes', 'ls', ...onMachine(s.machine)], { timeout: 60_000 });
|
|
212
218
|
const list = r.code === 0 ? json(r.stdout.trim(), null) : null;
|
|
213
|
-
observed.set(s.machine, Array.isArray(list) ? new Set(list.map((p) => p.pane)) : null);
|
|
219
|
+
observed.set(s.machine, Array.isArray(list) ? new Set(list.filter(p => !p.dead).map((p) => p.pane)) : null);
|
|
214
220
|
if (!Array.isArray(list)) report.failed.push([id, `pane liveness unavailable; retaining run: ${(r.stderr || r.stdout).trim().slice(-200)}`]);
|
|
215
221
|
}
|
|
222
|
+
for (const {id} of rows) {
|
|
223
|
+
const session=currentRun(board.db,id)?.metadata?.supercode;
|
|
224
|
+
const panes=observed.get(session?.machine);
|
|
225
|
+
if(session?.launch_key && panes instanceof Set && !panes.has(session.pane)) {
|
|
226
|
+
try { const receipt=launchReceipt(board,session);if(receipt?.live===false&&receipt.exit)exits.set(id,receipt.exit); }
|
|
227
|
+
catch(error){report.failed.push([id,`exit receipt unavailable; retaining liveness observation: ${error.message}`]);}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
216
230
|
for (const { id } of rows) {
|
|
217
231
|
tx(board, (db) => {
|
|
218
232
|
const task = getTask(db, id);
|
|
219
233
|
if (!task || (entry.workflow.run ? !task.current_run_id : task.status !== 'running')) return;
|
|
220
234
|
const runRow = currentRun(db, id);
|
|
221
235
|
let session = runRow?.metadata?.supercode ?? null;
|
|
222
|
-
if (session?.launch_pending) {
|
|
236
|
+
if (session?.launch_pending || session?.launch_deferred) {
|
|
223
237
|
extendClaim(db, id, { ttl: settings.ttl, reason: 'launch_receipt_pending' });
|
|
224
238
|
return; // An uncertain launch owns this run until a machine receipt settles it.
|
|
225
239
|
}
|
|
@@ -247,6 +261,30 @@ function supervise(board, fleet, settings, report, entry, effects) {
|
|
|
247
261
|
}
|
|
248
262
|
};
|
|
249
263
|
const limits = limitsOf(db, board, entry, task, session);
|
|
264
|
+
// Availability is the same retained linked/unlinked feed subscribers hear.
|
|
265
|
+
// A failed stream is unknown, not evidence that every remote machine left.
|
|
266
|
+
if (session?.session_id && session.surface !== 'headless') {
|
|
267
|
+
const panes = session.pane ? observed.get(session.machine) : null;
|
|
268
|
+
const reachable = panes instanceof Set || fleet.online.has(session.machine);
|
|
269
|
+
if (!reachable && !fleet.presenceKnown) return;
|
|
270
|
+
if (!reachable) {
|
|
271
|
+
const next = {...session, offline_since: session.offline_since ?? t};
|
|
272
|
+
setRunSession(db,runRow.id,next);
|
|
273
|
+
observe('machine_offline',{machine:session.machine,seconds:t-next.offline_since},null);
|
|
274
|
+
const age = Number(entry.params.block_age_seconds ?? 3600);
|
|
275
|
+
if (t-Number(next.offline_notified_at ?? next.offline_since)>=age) {
|
|
276
|
+
setRunSession(db,runRow.id,{...currentRun(db,id)?.metadata?.supercode,offline_notified_at:t});
|
|
277
|
+
observe('machine_wait_aged',{machine:session.machine,seconds:t-next.offline_since,reason:`machine ${session.machine} has been unlinked for ${t-next.offline_since}s`},null);
|
|
278
|
+
}
|
|
279
|
+
if (getTask(db,id)?.current_run_id===runRow.id) extendClaim(db,id,{ttl:settings.ttl,reason:'machine_offline'});
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (session.offline_since != null) {
|
|
283
|
+
setRunSession(db,runRow.id,{...session,offline_since:null,offline_notified_at:null});
|
|
284
|
+
observe('machine_online',{machine:session.machine,reason:'machine linked again'},null);
|
|
285
|
+
session=currentRun(db,id)?.metadata?.supercode ?? session;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
250
288
|
// Hermes's orphan reconciliation: a running card whose claim bookkeeping is broken, with nothing live behind it
|
|
251
289
|
const paneReading = session?.pane ? observed.get(session.machine) : undefined;
|
|
252
290
|
const paneLive = paneReading instanceof Set ? paneReading.has(session.pane) : null;
|
|
@@ -279,6 +317,11 @@ function supervise(board, fleet, settings, report, entry, effects) {
|
|
|
279
317
|
return;
|
|
280
318
|
}
|
|
281
319
|
if (session?.session_id) {
|
|
320
|
+
if(exits.has(id)) {
|
|
321
|
+
const exit=exits.get(id);
|
|
322
|
+
observe('exited',{code:exit.failed?1:0,error:`native session ${session.session_id} ended: ${exit.how}`,session_id:session.session_id,pane:session.pane},'crashed');
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
282
325
|
if (session.pane && paneLive === null) return; // no machine reading, no loss verdict
|
|
283
326
|
if (!session.pane && !fleet.ok) return;
|
|
284
327
|
const alive = session.pane ? paneLive : sessionAlive(fleet, session);
|
|
@@ -286,13 +329,6 @@ function supervise(board, fleet, settings, report, entry, effects) {
|
|
|
286
329
|
const young = t - Number(session.launched_at ?? 0) < Number(limits.grace ?? settings.grace);
|
|
287
330
|
const missing = !alive && machineUp && !young;
|
|
288
331
|
const next = { ...session };
|
|
289
|
-
// a machine that is off the fleet: how long, for the workflow's machine_offline (the claim is kept meanwhile)
|
|
290
|
-
if (machineUp) next.offline_since = null; else next.offline_since ??= t;
|
|
291
|
-
if (!machineUp) {
|
|
292
|
-
setRunSession(db, runRow.id, next);
|
|
293
|
-
observe('machine_offline', { machine: session.machine, seconds: t - next.offline_since }, null);
|
|
294
|
-
if (getTask(db, id)?.current_run_id !== runRow.id) return; // the workflow moved it
|
|
295
|
-
}
|
|
296
332
|
if (alive) { next.seen_at ??= t; next.missing_since = null; }
|
|
297
333
|
if (missing) next.missing_since ??= t;
|
|
298
334
|
if (missing && t - next.missing_since >= Number(limits.grace ?? settings.grace)) {
|
|
@@ -502,9 +538,23 @@ export function launchRun(board, entry, id, lane, fleet, settings) {
|
|
|
502
538
|
if (slot === 'worker' || (worker?.session_id && worker.session_id === reviewer?.session_id)) throw new Error('a reviewer must have a different session from the implementer');
|
|
503
539
|
}
|
|
504
540
|
const meta = { status, role: spec.role, slot };
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
541
|
+
try {
|
|
542
|
+
const result = surface === 'pane'
|
|
543
|
+
? launchPane(board, task, runId, lane, fleet, { input: prompt, slot, meta })
|
|
544
|
+
: launchHeadless(board, task, runId, lane, settings, { prompt, skills: spec.skills ?? [], meta });
|
|
545
|
+
tx(board,db=>setRunSession(db,runId,{launch_deferred:false,launch_deferred_error:null}));
|
|
546
|
+
return result;
|
|
547
|
+
} catch(error) {
|
|
548
|
+
if(error.launchDeferred) {
|
|
549
|
+
tx(board,db=>{
|
|
550
|
+
const previous=currentRun(db,id)?.metadata?.supercode;
|
|
551
|
+
setRunSession(db,runId,{...meta,launch_deferred:true,launch_deferred_error:error.message});
|
|
552
|
+
if(previous?.launch_deferred_error!==error.message)event(db,id,'launch_deferred',{reason:error.message},runId);
|
|
553
|
+
});
|
|
554
|
+
error.launchPending=true;
|
|
555
|
+
}
|
|
556
|
+
throw error;
|
|
557
|
+
}
|
|
508
558
|
}
|
|
509
559
|
|
|
510
560
|
/** Start one card now by a person's word (a triage card's session, a manager's start): the workflow's start event. */
|
|
@@ -570,9 +620,9 @@ export function closeRequested(board, report, { taskId = null, caller = null } =
|
|
|
570
620
|
try {
|
|
571
621
|
const machine = session.machine ?? localMachine();
|
|
572
622
|
if (!panes.has(machine)) {
|
|
573
|
-
const r = sc(['teams', 'panes', 'ls',
|
|
623
|
+
const r = sc(['teams', 'panes', 'ls', ...onMachine(machine)], { timeout: 60_000, as: boardAddress(board.label) });
|
|
574
624
|
const list = r.code === 0 ? json(r.stdout.trim(), null) : null;
|
|
575
|
-
panes.set(machine, Array.isArray(list) ? { live: new Set(list.map((p) => p.pane)) } : { error: `cannot read panes on ${machine}: ${(r.stderr || r.stdout).trim().slice(-300)}` });
|
|
625
|
+
panes.set(machine, Array.isArray(list) ? { live: new Set(list.filter(p => !p.dead).map((p) => p.pane)) } : { error: `cannot read panes on ${machine}: ${(r.stderr || r.stdout).trim().slice(-300)}` });
|
|
576
626
|
}
|
|
577
627
|
const observed = panes.get(machine);
|
|
578
628
|
if (observed.error) throw new Error(observed.error);
|
|
@@ -618,7 +668,7 @@ function observeIdle(board, fleet, settings, report, entry, effects) {
|
|
|
618
668
|
if (!(entry.workflow.statuses[status]?.on?.session_idle || entry.workflow.on?.session_idle || entry.workflow.run?.on?.session_idle)) return;
|
|
619
669
|
const runRow = currentRun(db, id);
|
|
620
670
|
const session = runRow?.metadata?.supercode;
|
|
621
|
-
if (!session?.session_id || session.surface !== 'pane') return;
|
|
671
|
+
if (!session?.session_id || session.surface !== 'pane' || session.offline_since != null) return;
|
|
622
672
|
const live = fleet.sessions.find((x) => x.address?.endsWith(`:${session.session_id}`));
|
|
623
673
|
const observedAt = now();
|
|
624
674
|
const lastAt = Math.floor(Number(live?.last_message_at_ms) / 1000);
|
|
@@ -727,6 +777,7 @@ function launcherCheck(db, task, lane, fleet) {
|
|
|
727
777
|
if (!task.machine) return 'the card names no machine; a pane session runs on the machine its card names';
|
|
728
778
|
const machine = normalMachine(task.machine);
|
|
729
779
|
if (!fleet.ok) return `supercode could not read the fleet: ${fleet.error}`;
|
|
780
|
+
if (machine !== localMachine() && !fleet.presenceKnown) return 'machine event stream unavailable; launch waits for a current availability reading';
|
|
730
781
|
if (!fleet.online.has(machine)) return `machine ${machine} is offline; the card starts when it is back`;
|
|
731
782
|
// a card on this machine gets its workspace made here (scratch, dir or worktree, as Hermes makes it); another
|
|
732
783
|
// machine's card names its directory there
|
|
@@ -781,12 +832,12 @@ const getTaskNow = (board, id) => view(board, (db) => getTask(db, id));
|
|
|
781
832
|
|
|
782
833
|
/** A transport error is not a negative launch receipt. The key remains owned by its run. */
|
|
783
834
|
function launchReceipt(board, session) {
|
|
784
|
-
const r = sc(['teams', 'launches', 'get', session.launch_key,
|
|
835
|
+
const r = sc(['teams', 'launches', 'get', session.launch_key, ...onMachine(session.machine)],
|
|
785
836
|
{ timeout: 30_000, as: boardAddress(board.label) });
|
|
786
|
-
if (r.code !== 0) throw
|
|
837
|
+
if (r.code !== 0) throw unavailable(`launch receipt unavailable on ${session.machine}: install the matching Teams CLI and machine capability (teams launches get): ${(r.stderr || r.stdout).trim().slice(-300)}`);
|
|
787
838
|
if (r.stdout.trim() === 'null') return null;
|
|
788
839
|
const receipt = lastJson(r.stdout);
|
|
789
|
-
if (!receipt || receipt.key !== session.launch_key) throw
|
|
840
|
+
if (!receipt || receipt.key !== session.launch_key) throw unavailable(`invalid launch receipt for ${session.launch_key} on ${session.machine}`);
|
|
790
841
|
return receipt;
|
|
791
842
|
}
|
|
792
843
|
|
|
@@ -834,7 +885,22 @@ export function reconcileCallerLaunch(board, sid) {
|
|
|
834
885
|
}
|
|
835
886
|
}
|
|
836
887
|
|
|
837
|
-
function reconcileLaunches(board, fleet, report, entry, effects, settings) {
|
|
888
|
+
function reconcileLaunches(board, fleet, report, entry, effects, settings, lanes) {
|
|
889
|
+
const deferred=view(board,db=>db.prepare("SELECT t.id FROM tasks t JOIN task_runs r ON r.id=t.current_run_id WHERE r.ended_at IS NULL AND json_extract(r.metadata,'$.supercode.launch_deferred')=1").all());
|
|
890
|
+
for(const {id} of deferred) {
|
|
891
|
+
try {
|
|
892
|
+
const task=getTaskNow(board,id),status=view(board,db=>statusOf(db,task)),spec=runSpec(entry,status,task);
|
|
893
|
+
const profile=spec&&view(board,db=>evaluate(entry.workflow.roles?.[spec.role]??'card.assignee',scopeOf(db,board,task,{params:entry.params})));
|
|
894
|
+
const lane=lanes?.[profile];if(!lane){report.queued.push([id,'launch waits for its configured lane']);continue;}
|
|
895
|
+
const why=launcherCheck(board.db,task,lane,fleet);if(why){report.queued.push([id,why]);continue;}
|
|
896
|
+
const session=launchRun(board,entry,id,lane,fleet,settings);
|
|
897
|
+
if(session)report.started.push([id,session.address??session.session_id,session.mode]);
|
|
898
|
+
}catch(error){
|
|
899
|
+
if(error.launchPending){report.queued.push([id,error.message]);continue;}
|
|
900
|
+
const moved=tx(board,db=>fire(db,board,entry,id,'spawn_failed',{roles:['platform'],payload:{error:error.message}}));effects.push(...moved.effects);
|
|
901
|
+
report.failed.push([id,error.message]);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
838
904
|
const rows = view(board, (db) => db.prepare(`SELECT id, task_id, ended_at, metadata FROM task_runs
|
|
839
905
|
WHERE json_extract(metadata, '$.supercode.launch_pending') = 1
|
|
840
906
|
AND json_extract(metadata, '$.supercode.close_confirmed_at') IS NULL`).all());
|
|
@@ -883,15 +949,15 @@ function launchPane(board, task, runId, lane, fleet, { input = null, slot = 'wor
|
|
|
883
949
|
const pending = view(board, (db) => db.prepare('SELECT id, ended_at, metadata FROM task_runs WHERE task_id = ? AND id != ?').all(task.id, runId))
|
|
884
950
|
.map((r) => ({ ...r, session: json(r.metadata, {})?.supercode }))
|
|
885
951
|
.find((r) => (r.session?.pane || r.session?.launch_key) && !r.session.close_transferred_to && (r.session.slot ?? 'worker') === slot && (r.ended_at || r.session.close_requested) && !r.session.close_confirmed_at);
|
|
886
|
-
if (pending) throw
|
|
952
|
+
if (pending) throw unavailable(`slot ${slot} still owns pane ${pending.session.pane} from run ${pending.id}; closure must be confirmed before another session starts: ${pending.session.close_error ?? 'close pending'}`);
|
|
887
953
|
let prev = view(board, (db) => lastSession(db, task.id, slot));
|
|
888
954
|
if (prev?.session_id && !prev.forgotten && (prev.harness ?? 'claude-code') === harness && prev.machine === machine) {
|
|
889
955
|
const fresh = observeFleet();
|
|
890
|
-
if (!fresh.ok) throw
|
|
891
|
-
const r = sc(['teams', 'panes', 'ls',
|
|
956
|
+
if (!fresh.ok) throw unavailable(`cannot check for a manual continuation of ${prev.session_id}: ${fresh.error}`);
|
|
957
|
+
const r = sc(['teams', 'panes', 'ls', ...onMachine(machine)], { timeout: 60_000 });
|
|
892
958
|
const listed = r.code === 0 ? json(r.stdout.trim(), null) : null;
|
|
893
|
-
if (!Array.isArray(listed)) throw
|
|
894
|
-
const pane = continuationPane(fresh, prev, new Set(listed.map((p) => p.pane)));
|
|
959
|
+
if (!Array.isArray(listed)) throw unavailable(`cannot read panes before continuing ${prev.session_id}: ${(r.stderr || r.stdout).trim().slice(-300)}`);
|
|
960
|
+
const pane = continuationPane(fresh, prev, new Set(listed.filter(p => !p.dead).map((p) => p.pane)));
|
|
895
961
|
if (pane && pane !== prev.pane) prev = { ...prev, pane, close_requested: null, closed_at: null, close_confirmed_at: null, close_error: null };
|
|
896
962
|
fleet = fresh;
|
|
897
963
|
}
|
|
@@ -900,7 +966,7 @@ function launchPane(board, task, runId, lane, fleet, { input = null, slot = 'wor
|
|
|
900
966
|
closeRequested(board, report, { taskId: task.id });
|
|
901
967
|
prev = view(board, (db) => lastSession(db, task.id, slot));
|
|
902
968
|
// a pane that could not be closed still holds the session: no second process is started on it
|
|
903
|
-
if (prev?.close_requested && !prev.closed_at) throw
|
|
969
|
+
if (prev?.close_requested && !prev.closed_at) throw unavailable(`the slot's session ${prev.session_id} asked to be closed and its pane ${prev.pane} could not be: ${report.undelivered.map((u) => u[2]).join('; ') || 'no reason given'}`);
|
|
904
970
|
}
|
|
905
971
|
const openOwned = (key, sid, mode, args) => {
|
|
906
972
|
launchReceipt(board, { machine, launch_key: key }); // Prove the exact receipt door before starting a process.
|
|
@@ -1015,10 +1081,10 @@ function launchPane(board, task, runId, lane, fleet, { input = null, slot = 'wor
|
|
|
1015
1081
|
|
|
1016
1082
|
/** A machine reading, not a missing catalog entry, establishes whether a pane is alive. */
|
|
1017
1083
|
function panePresent(machine, pane) {
|
|
1018
|
-
const r = sc(['teams', 'panes', 'ls',
|
|
1084
|
+
const r = sc(['teams', 'panes', 'ls', ...onMachine(machine)], { timeout: 60_000 });
|
|
1019
1085
|
const panes = r.code === 0 ? json(r.stdout.trim(), null) : null;
|
|
1020
|
-
if (!Array.isArray(panes)) throw
|
|
1021
|
-
return panes.some((p) => p.pane === pane);
|
|
1086
|
+
if (!Array.isArray(panes)) throw unavailable(`cannot establish pane ${pane} liveness on ${machine}: ${(r.stderr || r.stdout).trim().slice(-300)}`);
|
|
1087
|
+
return panes.some((p) => p.pane === pane && !p.dead);
|
|
1022
1088
|
}
|
|
1023
1089
|
|
|
1024
1090
|
/** Whether a pane just launched is still there after the harness had a moment to start. */
|
|
@@ -1438,6 +1504,7 @@ function readMailbox(board, address, report) {
|
|
|
1438
1504
|
try { renameSync(path, join(box, 'cur', name)); } catch { continue; } // another reader took it
|
|
1439
1505
|
// an answer to a question the board asked is what a card waits on (read by the card's workflow), not a verb
|
|
1440
1506
|
if (envelope.in_reply_to) continue;
|
|
1507
|
+
if (envelope.from === `sc:${localMachine()}:operator:teams-events` && json(envelope.body, {})?.machine_event) { report.mailed.push([envelope.from, 'machine_event']); continue; }
|
|
1441
1508
|
const argv = json(envelope.body, {})?.workflow;
|
|
1442
1509
|
let answer;
|
|
1443
1510
|
if (!Array.isArray(argv) || !argv.length || NOT_BY_MAIL.has(argv[0])) answer = `not applied: a mailed card update is one \`supercode workflow\` verb other than ${[...NOT_BY_MAIL].join(', ')}`;
|
|
@@ -1548,10 +1615,14 @@ export async function tick({ root, lanes, settings, only = null, slugs = null, d
|
|
|
1548
1615
|
// where the tick's time goes, phase by phase (ms), printed by `serve --verbose` and in `--json`
|
|
1549
1616
|
let mark = Date.now();
|
|
1550
1617
|
const phase = (name) => { const t = Date.now(); report.ms[name] = t - mark; mark = t; };
|
|
1618
|
+
if (!fleet.machineEvents?.subscriptions?.includes(boardAddress(board.label))) {
|
|
1619
|
+
const subscription = sc(['teams','machines','subscribe',boardAddress(board.label),'--json']);
|
|
1620
|
+
if (subscription.code !== 0) report.failed.push(['machine_events',(subscription.stderr || subscription.stdout).trim().slice(-200)]);
|
|
1621
|
+
}
|
|
1551
1622
|
readMailbox(board, boardAddress(board.label), report); phase('mailbox');
|
|
1552
1623
|
await refreshRelationAnswers(board); phase('answers');
|
|
1553
1624
|
const effects = [];
|
|
1554
|
-
reconcileLaunches(board, fleet, report, entry, effects, settings); supervise(board, fleet, settings, report, entry, effects); await runEffects(board, effects.splice(0), { retryNotices: true }); closeRequested(board, report); phase('supervise');
|
|
1625
|
+
reconcileLaunches(board, fleet, report, entry, effects, settings, lanes); supervise(board, fleet, settings, report, entry, effects); await runEffects(board, effects.splice(0), { retryNotices: true }); closeRequested(board, report); phase('supervise');
|
|
1555
1626
|
report.unsettled = [];
|
|
1556
1627
|
report.promoted = recomputeReady(board, effects, report.unsettled);
|
|
1557
1628
|
await sendBoardMessages(board, report);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-orchestrator",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.120",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The orchestrator runtime over the Volter Harness ontology: one typed operational model whose folder is its serialization, read and written through the harness orchestration doors (docs/ORCHESTRATOR-IR.md)",
|
|
6
6
|
"exports": {
|