@volter/supercode-orchestrator 0.5.35 → 0.5.36
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/cli.mjs +7 -7
- package/board/dispatch.mjs +10 -3
- package/package.json +1 -1
package/board/cli.mjs
CHANGED
|
@@ -15,7 +15,7 @@ import { actorOf, view, backingPaths, layOut, backingOf, boardExists, boardPath,
|
|
|
15
15
|
import { isLocalWorkspace } from './workspace.mjs';
|
|
16
16
|
import { openZtrackBoard } from './ztrack.mjs';
|
|
17
17
|
import * as engine from './engine.mjs';
|
|
18
|
-
import { requestAdoption, alertManagers, boardAddress, boardLabel, boardSettings, callerSession, commandEnv, reconcileCallerLaunch, closeRequested, lanesOf, localMachine, observeFleet, paused, plan, startCard, supercodeBin, tick } from './dispatch.mjs';
|
|
18
|
+
import { requestAdoption, alertManagers, boardAddress, boardLabel, boardSettings, callerSession, commandEnv, reconcileCallerLaunch, closeRequested, lanesOf, localMachine, observeFleet, paused, plan, startCard, stopDispatching, supercodeBin, tick } from './dispatch.mjs';
|
|
19
19
|
import { watchBoard } from './wake.mjs';
|
|
20
20
|
import { streamBoardPublications } from './event-stream.mjs';
|
|
21
21
|
import { completionOwnerOf, isLatestReviewer, loadWorkflow, requestSessionClose, scopeOf, statusOf, useWorkflow, workflowEntry, workflowFor } from './workflow.mjs';
|
|
@@ -1379,9 +1379,9 @@ async function serve(root, args = {}) {
|
|
|
1379
1379
|
if (args.pidfile) writeFileSync(args.pidfile, `${process.pid}\n`);
|
|
1380
1380
|
let stopped = false;
|
|
1381
1381
|
let events;
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1382
|
+
// a signal ends the round in progress with nothing new started (dispatch.mjs stopDispatching) and no round after it
|
|
1383
|
+
const stop = () => { stopped = true; stopDispatching(); events?.close(); };
|
|
1384
|
+
for (const signal of ['SIGTERM', 'SIGINT', 'SIGHUP']) process.on(signal, stop);
|
|
1385
1385
|
// what the serve says is kept in the home too (logs/workflow-serve.log), so why it stopped outlives its pane
|
|
1386
1386
|
const kept = join(root, 'logs', 'workflow-serve.log');
|
|
1387
1387
|
const log = (text) => {
|
|
@@ -1488,7 +1488,7 @@ async function serve(root, args = {}) {
|
|
|
1488
1488
|
interval = settings.interval;
|
|
1489
1489
|
const reports = await tick({ root, lanes: state.lanes, settings, params, deploymentReceipt: true });
|
|
1490
1490
|
round += 1;
|
|
1491
|
-
await watchRounds(reports);
|
|
1491
|
+
if (!stopped) await watchRounds(reports);
|
|
1492
1492
|
for (const r of reports) {
|
|
1493
1493
|
nextMailAt = Math.min(nextMailAt, r.next_mail_at ?? Infinity);
|
|
1494
1494
|
const said = summary(r);
|
|
@@ -1509,7 +1509,7 @@ async function serve(root, args = {}) {
|
|
|
1509
1509
|
}
|
|
1510
1510
|
} catch (error) {
|
|
1511
1511
|
log(`tick failed: ${error.message}`);
|
|
1512
|
-
await watchRounds([{ board: '*', error: `tick failed: ${error.message}` }]);
|
|
1512
|
+
if (!stopped) await watchRounds([{ board: '*', error: `tick failed: ${error.message}` }]);
|
|
1513
1513
|
}
|
|
1514
1514
|
// each round is a line of its own in the slow log, with the steps it spent its time on
|
|
1515
1515
|
recordSlow({ kind: 'call', name: 'dispatch round', ms: Date.now() - began, slowest: null, extra: { steps: takeSteps() } });
|
|
@@ -1529,7 +1529,7 @@ async function serve(root, args = {}) {
|
|
|
1529
1529
|
}
|
|
1530
1530
|
} finally {
|
|
1531
1531
|
events?.close();
|
|
1532
|
-
|
|
1532
|
+
for (const signal of ['SIGTERM', 'SIGINT', 'SIGHUP']) process.off(signal, stop);
|
|
1533
1533
|
if (args.pidfile) { try { unlinkSync(args.pidfile); } catch { /* gone */ } }
|
|
1534
1534
|
}
|
|
1535
1535
|
log(stopped ? 'stopped (a signal)' : 'stopped');
|
package/board/dispatch.mjs
CHANGED
|
@@ -2009,6 +2009,7 @@ export async function sendBoardMessages(board, report, taskId = null) {
|
|
|
2009
2009
|
const planned = tx(board, db => pendingRequestMail(db, taskId));
|
|
2010
2010
|
if (Number.isFinite(planned.nextAt)) report.next_mail_at = Math.min(report.next_mail_at ?? Infinity, planned.nextAt);
|
|
2011
2011
|
for (const mail of planned.ready) {
|
|
2012
|
+
if (stopping) break;
|
|
2012
2013
|
const first = mail.members[0];
|
|
2013
2014
|
try {
|
|
2014
2015
|
const task = view(board, db => getTask(db, first.task_id));
|
|
@@ -2189,7 +2190,7 @@ async function notify(board, fleet, report, entry) {
|
|
|
2189
2190
|
} else text = noticeOf(entry, role, ev.kind, task, p);
|
|
2190
2191
|
if (text) {
|
|
2191
2192
|
const body = ['worker', 'dependency'].includes(role) ? identifyNotice(ev, text) : text;
|
|
2192
|
-
if (refused.has(sub.chat_id)) { untried = true; break; }
|
|
2193
|
+
if (refused.has(sub.chat_id) || stopping) { untried = true; break; }
|
|
2193
2194
|
let receipt;
|
|
2194
2195
|
try {
|
|
2195
2196
|
receipt = await send(board, from, board.label, sub.chat_id, body, `board-${sub.task_id}-${ev.id}-${sub.chat_id.slice(-12)}`, { queue: String(sub.chat_id).includes(':claude-code:') ? false : !working || (!entry.workflow.run && sub.delivery_mode === 'notify'), subject: `${ev.kind.replaceAll('_', ' ')} · run ${ev.run_id ?? 'none'} · event ${ev.id} · ${task?.title || sub.task_id}` });
|
|
@@ -2454,6 +2455,11 @@ function scAsync(args, { input = null, timeout = 120_000 } = {}) {
|
|
|
2454
2455
|
});
|
|
2455
2456
|
}
|
|
2456
2457
|
|
|
2458
|
+
// A dispatcher told to stop (its serve's SIGTERM, SIGINT or SIGHUP) starts nothing more in the round it is in: no further
|
|
2459
|
+
// board, card start, board message or notice. What it leaves untried is not a failure; the next dispatcher sends it.
|
|
2460
|
+
let stopping = false;
|
|
2461
|
+
export function stopDispatching() { stopping = true; }
|
|
2462
|
+
|
|
2457
2463
|
// how many deliveries a tick has in flight at once (each is a `supercode message` process of a few seconds)
|
|
2458
2464
|
const SEND_CONCURRENCY = 8;
|
|
2459
2465
|
|
|
@@ -2461,7 +2467,7 @@ const SEND_CONCURRENCY = 8;
|
|
|
2461
2467
|
async function pool(items, n, fn) {
|
|
2462
2468
|
let next = 0;
|
|
2463
2469
|
await Promise.all(Array.from({ length: Math.min(n, items.length) }, async () => {
|
|
2464
|
-
while (next < items.length) { const i = next; next += 1; await fn(items[i]); }
|
|
2470
|
+
while (next < items.length && !stopping) { const i = next; next += 1; await fn(items[i]); }
|
|
2465
2471
|
}));
|
|
2466
2472
|
}
|
|
2467
2473
|
|
|
@@ -2598,6 +2604,7 @@ export async function tick({ root, lanes, settings, only = null, slugs = null, d
|
|
|
2598
2604
|
const held = dispatch ? homeHoldings(root, boards, lanes, fleet, entry) : [];
|
|
2599
2605
|
const out = [];
|
|
2600
2606
|
for (const slug of boards) {
|
|
2607
|
+
if (stopping) break;
|
|
2601
2608
|
const report = { board: slug, ms: { fleet: fleetMs }, mailed: [], started: [], queued: [], failed: [], lost: [], crashed: [], timed_out: [], reclaimed: [], auto_blocked: [], promoted: 0, unassigned: [], nonspawnable: [], notified: [], undelivered: [], fleet: fleet.ok ? 'read' : fleet.error };
|
|
2602
2609
|
// the mailbox's answers in flight (not part of the report the caller prints)
|
|
2603
2610
|
Object.defineProperty(report, 'answers', { value: [], enumerable: false });
|
|
@@ -2628,7 +2635,7 @@ export async function tick({ root, lanes, settings, only = null, slugs = null, d
|
|
|
2628
2635
|
tx(board,db=>auditEndedRuns(db,board,entry));
|
|
2629
2636
|
if (dispatch) await activatePullRequestReviews(board, entry, report, effects, { only });
|
|
2630
2637
|
await sendBoardMessages(board, report);
|
|
2631
|
-
if (dispatch) startCards(board, fleet, lanes, settings, report, entry, effects, { only, held });
|
|
2638
|
+
if (dispatch && !stopping) startCards(board, fleet, lanes, settings, report, entry, effects, { only, held });
|
|
2632
2639
|
phase('start');
|
|
2633
2640
|
if (dispatch && !only && !report.paused) observeIdle(board, fleet, settings, report, entry, effects);
|
|
2634
2641
|
// what the moves asked for, once they are committed (messages to actors, closed panes, cleaned workspaces)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-orchestrator",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.36",
|
|
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": {
|