@volter/supercode-orchestrator 0.4.104 → 0.4.105

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 (2) hide show
  1. package/board/dispatch.mjs +19 -6
  2. package/package.json +1 -1
@@ -176,6 +176,16 @@ export function lanesOf(profiles = {}) {
176
176
 
177
177
  function supervise(board, fleet, settings, report, entry, effects) {
178
178
  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();
179
+ // The catalog can lag a pane. Read the actual machines once, before any board mutation.
180
+ const observed = new Map();
181
+ for (const { id } of rows) {
182
+ const s = currentRun(board.db, id)?.metadata?.supercode;
183
+ if (!s?.pane || observed.has(s.machine)) continue;
184
+ const r = sc(['teams', 'panes', 'ls', '--machine', s.machine ?? localMachine()], { timeout: 60_000 });
185
+ const list = r.code === 0 ? json(r.stdout.trim(), null) : null;
186
+ observed.set(s.machine, Array.isArray(list) ? new Set(list.map((p) => p.pane)) : null);
187
+ if (!Array.isArray(list)) report.failed.push([id, `pane liveness unavailable; retaining run: ${(r.stderr || r.stdout).trim().slice(-200)}`]);
188
+ }
179
189
  for (const { id } of rows) {
180
190
  tx(board, (db) => {
181
191
  const task = getTask(db, id);
@@ -197,7 +207,9 @@ function supervise(board, fleet, settings, report, entry, effects) {
197
207
  };
198
208
  const limits = limitsOf(db, board, entry, task, session);
199
209
  // Hermes's orphan reconciliation: a running card whose claim bookkeeping is broken, with nothing live behind it
200
- const live = session?.surface === 'headless' ? pidAlive(session.pid) : session?.session_id ? (fleet.ok ? sessionAlive(fleet, session) : true) : false;
210
+ const paneReading = session?.pane ? observed.get(session.machine) : undefined;
211
+ const paneLive = paneReading instanceof Set ? paneReading.has(session.pane) : null;
212
+ const live = session?.surface === 'headless' ? pidAlive(session.pid) : session?.pane ? paneLive !== false : session?.session_id ? (fleet.ok ? sessionAlive(fleet, session) : true) : false;
201
213
  // (whether it is reconciled is the workflow's: its `orphaned` transitions and their guard)
202
214
  if ((!task.claim_lock || !task.claim_expires) && !live) {
203
215
  observe('orphaned', { reason: 'orphaned_running', claim_lock: task.claim_lock, claim_expires: task.claim_expires, error: 'orphaned running card (broken claim bookkeeping)' }, 'reclaimed');
@@ -226,20 +238,21 @@ function supervise(board, fleet, settings, report, entry, effects) {
226
238
  return;
227
239
  }
228
240
  if (session?.session_id) {
229
- if (!fleet.ok) return; // no reading, no verdict
230
- const alive = sessionAlive(fleet, session);
231
- const machineUp = fleet.online.has(session.machine);
241
+ if (session.pane && paneLive === null) return; // no machine reading, no loss verdict
242
+ if (!session.pane && !fleet.ok) return;
243
+ const alive = session.pane ? paneLive : sessionAlive(fleet, session);
244
+ const machineUp = paneReading instanceof Set || fleet.online.has(session.machine);
232
245
  const young = t - Number(session.launched_at ?? 0) < Number(limits.grace ?? settings.grace);
233
246
  const missing = !alive && machineUp && !young;
234
247
  const next = { ...session };
235
248
  // a machine that is off the fleet: how long, for the workflow's machine_offline (the claim is kept meanwhile)
236
- if (machineUp) delete next.offline_since; else next.offline_since ??= t;
249
+ if (machineUp) next.offline_since = null; else next.offline_since ??= t;
237
250
  if (!machineUp) {
238
251
  setRunSession(db, runRow.id, next);
239
252
  observe('machine_offline', { machine: session.machine, seconds: t - next.offline_since }, null);
240
253
  if (getTask(db, id)?.current_run_id !== runRow.id) return; // the workflow moved it
241
254
  }
242
- if (alive) { next.seen_at ??= t; delete next.missing_since; }
255
+ if (alive) { next.seen_at ??= t; next.missing_since = null; }
243
256
  if (missing) next.missing_since ??= t;
244
257
  if (missing && t - next.missing_since >= Number(limits.grace ?? settings.grace)) {
245
258
  // lost: gone from an online machine for the whole grace. A resume that never came up is not resumable.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/supercode-orchestrator",
3
- "version": "0.4.104",
3
+ "version": "0.4.105",
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": {