@volter/supercode-orchestrator 0.4.91 → 0.4.93

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 CHANGED
@@ -9,7 +9,7 @@ import { spawnSync } from 'node:child_process';
9
9
  import { basename, join, resolve } from 'node:path';
10
10
  import { tmpdir } from 'node:os';
11
11
  import { read } from '../orchestration.mjs';
12
- import { backingPaths, layOut, backingOf, boardExists, boardPath, DEFAULT_BOARD, event, json, listBoards, logsDir, now, openBoard, setActor, tx } from './store.mjs';
12
+ import { view, backingPaths, layOut, backingOf, boardExists, boardPath, DEFAULT_BOARD, event, json, listBoards, logsDir, now, openBoard, setActor, tx } from './store.mjs';
13
13
  import { openZtrackBoard } from './ztrack.mjs';
14
14
  import * as engine from './engine.mjs';
15
15
  import { adoptSession, boardAddress, boardLabel, boardSettings, callerSession, lanesOf, localMachine, observeFleet, paused, plan, startCard, tick } from './dispatch.mjs';
@@ -304,7 +304,7 @@ export async function runWorkflow(argv) {
304
304
  // the creator hears what happens from here on; a start answered above is not told twice
305
305
  const address = args['no-notify'] ? null : args.notify ?? callerAddress();
306
306
  if (address) withBoard((b) => engine.subscribe(b, created.id, address, 'creator'));
307
- const task = withBoard((b) => tx(b, (db) => engine.getTask(db, created.id)));
307
+ const task = withBoard((b) => view(b, (db) => engine.getTask(db, created.id)));
308
308
  if (args.json) { say(args, '', { ...cardDict(task), ...answer, notify: address ?? null }); return answer.failed ? 1 : 0; }
309
309
  const said = answer.started ? `; started session ${answer.session} (${answer.mode})` : answer.failed ? `; did not start: ${answer.failed}` : answer.waits ? `; waits: ${answer.waits}` : '';
310
310
  say(args, `${created.existing ? 'Existing' : 'Created'} ${created.id} (${task.status}, assignee=${task.assignee || '-'})${said}${address ? `; notices go to ${address}` : ''}`, null);
@@ -435,7 +435,7 @@ export async function runWorkflow(argv) {
435
435
  return 0;
436
436
  }
437
437
 
438
- case 'context': need(1, 'a card id'); process.stdout.write(`${withBoard((b) => tx(b, (db) => engine.cardContext(db, id)))}\n`); return 0;
438
+ case 'context': need(1, 'a card id'); process.stdout.write(`${withBoard((b) => view(b, (db) => engine.cardContext(db, id)))}\n`); return 0;
439
439
 
440
440
  case 'runs': {
441
441
  need(1, 'a card id');
@@ -471,7 +471,7 @@ export async function runWorkflow(argv) {
471
471
  const ttl = args.ttl ? duration(args.ttl) : engine.claimTtl();
472
472
  const runId = withBoard((b) => engine.claim(b, id, { ttl }));
473
473
  if (!runId) throw new Error(`cannot claim ${id} (not ready, already claimed, or waiting on parents)`);
474
- const task = withBoard((b) => tx(b, (db) => engine.getTask(db, id)));
474
+ const task = withBoard((b) => view(b, (db) => engine.getTask(db, id)));
475
475
  // Hermes's claim resolves the workspace and prints it; a scratch one is made under the board's workspaces
476
476
  let workspace = task.workspace_path;
477
477
  if (!workspace && task.workspace_kind === 'scratch') {
@@ -658,7 +658,7 @@ export async function runWorkflow(argv) {
658
658
  const file = join(logsDir(root, slug), `${id}.log`);
659
659
  if (!existsSync(file)) {
660
660
  // a pane session keeps no log file: its transcript is the harness's own, named by the run
661
- const s = withBoard((b) => tx(b, (db) => engine.lastSession(db, id)));
661
+ const s = withBoard((b) => view(b, (db) => engine.lastSession(db, id)));
662
662
  process.stdout.write(s ? `(${id} runs as session ${s.address}; its transcript is that session's: \`supercode sessions show ${s.session_id}\`)\n` : `(no log for ${id})\n`);
663
663
  return 0;
664
664
  }
@@ -21,7 +21,7 @@ import { hostname, tmpdir } from 'node:os';
21
21
  import { basename, dirname, join } from 'node:path';
22
22
  import { randomUUID } from 'node:crypto';
23
23
  import { fileURLToPath } from 'node:url';
24
- import { boardPath, claimer, event, json, listBoards, logsDir, now, openBoard, setActor, tx } from './store.mjs';
24
+ import { view, boardPath, claimer, event, json, listBoards, logsDir, now, openBoard, setActor, tx } from './store.mjs';
25
25
  import { doors, fire, loadWorkflow, resumeOf, runSpec, refreshRelationAnswers, transitionsFor, scopeOf, statusOf, templateValue, workflowFor } from './workflow.mjs';
26
26
  import { evaluate, holds, render } from './expr.mjs';
27
27
  import {
@@ -417,22 +417,22 @@ function startCards(board, fleet, lanes, settings, report, entry, effects, { onl
417
417
  /** Launch what the card's status runs: its role's lane, in the run's session slot, told the run's prompt and doors. */
418
418
  export function launchRun(board, entry, id, lane, fleet, settings) {
419
419
  const task = getTaskNow(board, id);
420
- const status = tx(board, (db) => statusOf(db, task));
420
+ const status = view(board, (db) => statusOf(db, task));
421
421
  const spec = runSpec(entry, status, task);
422
422
  if (!spec) return null;
423
423
  const runId = task.current_run_id;
424
424
  const surface = lane.surface === 'pane' ? 'pane' : 'headless';
425
- const scope = tx(board, (db) => scopeOf(db, board, task, { params: entry.params }));
425
+ const scope = view(board, (db) => scopeOf(db, board, task, { params: entry.params }));
426
426
  const where = whereOf(board, task);
427
427
  // a subtask's run also sees its arc: the arc's text, tasks and thread (where its owner's rulings are)
428
- const arcContext = task.subtask_of ? tx(board, (db) => cardContext(db, task.subtask_of)) : '';
429
- const prompt = render(spec.prompt, { ...scope, where, context: tx(board, (db) => cardContext(db, id)), arc_context: arcContext, doors: doors(entry.workflow, status, spec.role, id, where, surface) });
428
+ const arcContext = task.subtask_of ? view(board, (db) => cardContext(db, task.subtask_of)) : '';
429
+ const prompt = render(spec.prompt, { ...scope, where, context: view(board, (db) => cardContext(db, id)), arc_context: arcContext, doors: doors(entry.workflow, status, spec.role, id, where, surface) });
430
430
  // the run's session record knows its status, role and slot (a reviewer's slot is never the implementer's); the slot
431
431
  // is a template, so a step can ask for isolation by naming a new one
432
432
  const slot = String(templateValue(spec.session ?? 'worker', scope) ?? 'worker');
433
433
  if (spec.role === 'reviewer') {
434
- const worker = tx(board, (db) => lastSession(db, id, 'worker'));
435
- const reviewer = tx(board, (db) => lastSession(db, id, slot));
434
+ const worker = view(board, (db) => lastSession(db, id, 'worker'));
435
+ const reviewer = view(board, (db) => lastSession(db, id, slot));
436
436
  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');
437
437
  }
438
438
  const meta = { status, role: spec.role, slot };
@@ -448,7 +448,7 @@ export async function startCard(board, id, { roles = ['manager'], lanes, setting
448
448
  const moved = tx(board, (db) => fire(db, board, entry, id, entry.workflow.dispatch.start, { roles, payload: { lock: claimer() } }));
449
449
  const task = getTaskNow(board, id);
450
450
  const spec = runSpec(entry, moved.to, task);
451
- const profile = spec ? tx(board, (db) => evaluate(entry.workflow.roles?.[spec.role] ?? 'card.assignee', scopeOf(db, board, task, { params: entry.params }))) : null;
451
+ const profile = spec ? view(board, (db) => evaluate(entry.workflow.roles?.[spec.role] ?? 'card.assignee', scopeOf(db, board, task, { params: entry.params }))) : null;
452
452
  const lane = profile ? lanes[profile] : null;
453
453
  if (spec && !lane) throw new Error(`${id} is ${moved.to}, run by ${spec.role} (${profile ?? 'nobody'}), which is no lane of this home`);
454
454
  return lane ? launchRun(board, entry, id, lane, fleet, settings) : null;
@@ -507,7 +507,7 @@ function observeIdle(board, fleet, settings, report, entry, effects) {
507
507
  /** A session effect a committed move asked for: a message to the card's current actor, or closing a slot's pane. */
508
508
  export async function sessionEffect(board, e) {
509
509
  if (e.kind === 'send_to_actor') {
510
- const run = tx(board, (db) => currentRun(db, e.id));
510
+ const run = view(board, (db) => currentRun(db, e.id));
511
511
  const session = run?.metadata?.supercode;
512
512
  const address = addressOf(session);
513
513
  if (!address) return;
@@ -515,7 +515,7 @@ export async function sessionEffect(board, e) {
515
515
  return;
516
516
  }
517
517
  if (e.kind === 'close_session') {
518
- const rows = tx(board, (db) => db.prepare('SELECT id, metadata FROM task_runs WHERE task_id = ? ORDER BY id DESC').all(e.id));
518
+ const rows = view(board, (db) => db.prepare('SELECT id, metadata FROM task_runs WHERE task_id = ? ORDER BY id DESC').all(e.id));
519
519
  for (const row of rows) {
520
520
  // a run newer than the one that left the status is its successor, already started in this tick: not closed
521
521
  if (e.runId && row.id > e.runId) continue;
@@ -609,7 +609,7 @@ export function paused(root) {
609
609
 
610
610
 
611
611
 
612
- const getTaskNow = (board, id) => tx(board, (db) => getTask(db, id));
612
+ const getTaskNow = (board, id) => view(board, (db) => getTask(db, id));
613
613
 
614
614
 
615
615
  function launchPane(board, task, runId, lane, fleet, { input = null, slot = 'worker', meta = {} } = {}) {
@@ -628,11 +628,11 @@ function launchPane(board, task, runId, lane, fleet, { input = null, slot = 'wor
628
628
  // the slot's last session: kept when live, resumed when lost, never another slot's (a reviewer's is its own)
629
629
  // a close its session's own command asked for is carried out first, so its pane is gone before the slot's next
630
630
  // session starts (never two processes on one session)
631
- let prev = tx(board, (db) => lastSession(db, task.id, slot));
631
+ let prev = view(board, (db) => lastSession(db, task.id, slot));
632
632
  if (prev?.close_requested && !prev.closed_at) {
633
633
  const report = { undelivered: [] };
634
634
  closeRequested(board, report);
635
- prev = tx(board, (db) => lastSession(db, task.id, slot));
635
+ prev = view(board, (db) => lastSession(db, task.id, slot));
636
636
  // a pane that could not be closed still holds the session: no second process is started on it
637
637
  if (prev?.close_requested && !prev.closed_at) throw new Error(`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'}`);
638
638
  }
@@ -867,7 +867,7 @@ async function send(from, label, to, body, key, { queue = false, ask = false } =
867
867
  * (the id is the send's idempotency key). One that fails is tried again next tick.
868
868
  */
869
869
  async function sendBoardMessages(board, report) {
870
- const rows = tx(board, (db) => db.prepare(`SELECT id, task_id, payload FROM task_events e WHERE kind = 'board_message' AND NOT EXISTS
870
+ const rows = view(board, (db) => db.prepare(`SELECT id, task_id, payload FROM task_events e WHERE kind = 'board_message' AND NOT EXISTS
871
871
  (SELECT 1 FROM task_events s WHERE s.kind = 'board_message_sent' AND s.task_id = e.task_id AND json_extract(s.payload, '$.event') = e.id) ORDER BY id`).all());
872
872
  for (const row of rows) {
873
873
  const m = json(row.payload, {}) ?? {};
@@ -937,7 +937,7 @@ async function notify(board, fleet, report, entry) {
937
937
  .all(sub.task_id, sub.last_event_id, ...kinds);
938
938
  if (!events.length) return;
939
939
  // the card as the workflow names it (its status the workflow's: triaging, reviewing, stopped; `stored_as` kanban.db's)
940
- const task = tx(board, (db) => { const t = getTask(db, sub.task_id); return t ? { ...t, status: statusOf(db, t), stored_as: t.status } : t; });
940
+ const task = view(board, (db) => { const t = getTask(db, sub.task_id); return t ? { ...t, status: statusOf(db, t), stored_as: t.status } : t; });
941
941
  let cursor = Number(sub.last_event_id);
942
942
  let failed = null;
943
943
  try {
@@ -995,7 +995,7 @@ export async function notifyChats(root, adapters, log = () => {}, wake = null) {
995
995
  if (sub.platform === 'supercode' || !adapter) continue;
996
996
  const events = board.db.prepare(`SELECT id, kind, payload FROM task_events WHERE task_id = ? AND id > ? AND kind IN (${kinds.map(() => '?').join(', ')}) ORDER BY id`).all(sub.task_id, sub.last_event_id, ...kinds);
997
997
  if (!events.length) continue;
998
- const task = tx(board, (db) => getTask(db, sub.task_id));
998
+ const task = view(board, (db) => getTask(db, sub.task_id));
999
999
  let cursor = Number(sub.last_event_id);
1000
1000
  let failed = false;
1001
1001
  for (const ev of events) {
package/board/engine.mjs CHANGED
@@ -11,7 +11,7 @@ import { spawn, spawnSync } from 'node:child_process';
11
11
  import { existsSync, rmSync } from 'node:fs';
12
12
  import { join, resolve } from 'node:path';
13
13
  import { DatabaseSync } from 'node:sqlite';
14
- import { claimer, event, json, newTaskId, now, tx } from './store.mjs';
14
+ import { view, claimer, event, json, newTaskId, now, tx } from './store.mjs';
15
15
  import { refreshCardInputs, eventTo, fire, loadWorkflow, setStatus, settle, startNew, statusOf, runEvent, workflowFor } from './workflow.mjs';
16
16
 
17
17
  export const STATUSES = ['triage', 'todo', 'scheduled', 'ready', 'running', 'blocked', 'review', 'done', 'archived'];
@@ -80,7 +80,7 @@ export function endRun(db, taskId, { outcome, status = null, summary = null, err
80
80
  // terminates a reclaimed worker (a board's one-shot workers run where the board is); a pane session is a person-visible session and is never killed from the board
81
81
  const worker = outcome === 'reclaimed' ? json(was, {})?.supercode : null;
82
82
  const lock = one(db, 'SELECT claim_lock FROM task_runs WHERE id = ?', runId)?.claim_lock ?? one(db, 'SELECT claim_lock FROM tasks WHERE id = ?', taskId)?.claim_lock;
83
- if (worker?.surface === 'headless' && worker.pid && String(lock ?? '').startsWith(`${claimer().split(':')[0]}:`)) terminate(Number(worker.pid));
83
+ if (worker?.surface === 'headless' && worker.pid && String(lock ?? '').startsWith(`${claimer().split(':')[0]}:`)) run(db, 'INSERT INTO supercode_effects(effect) VALUES(?)', JSON.stringify({kind:'stop_run',id:taskId,runId}));
84
84
  run(db, `UPDATE task_runs SET status = ?, outcome = ?, summary = ?, error = ?, metadata = ?, ended_at = ?, claim_lock = NULL, claim_expires = NULL, worker_pid = NULL
85
85
  WHERE id = ? AND ended_at IS NULL`, status ?? outcome, outcome, summary, error, keep, now(), runId);
86
86
  run(db, 'UPDATE tasks SET current_run_id = NULL WHERE id = ?', taskId);
@@ -285,9 +285,9 @@ export function extendClaim(db, id, { ttl, reason, extra = {} }) {
285
285
  * workspaces, or a worktree (its branch stays); never while a child still waits on it, never a `dir`.
286
286
  */
287
287
  function cleanupWorkspace(board, id) {
288
- const task = tx(board, (db) => getTask(db, id));
288
+ const task = view(board, (db) => getTask(db, id));
289
289
  if (!task?.workspace_path || !['scratch', 'worktree'].includes(task.workspace_kind)) return;
290
- const waiting = tx(board, (db) => one(db, "SELECT 1 FROM task_links l JOIN tasks t ON t.id = l.child_id WHERE l.parent_id = ? AND t.status NOT IN ('done', 'archived') LIMIT 1", id));
290
+ const waiting = view(board, (db) => one(db, "SELECT 1 FROM task_links l JOIN tasks t ON t.id = l.child_id WHERE l.parent_id = ? AND t.status NOT IN ('done', 'archived') LIMIT 1", id));
291
291
  if (waiting || !existsSync(task.workspace_path)) return;
292
292
  if (task.workspace_kind === 'worktree') { spawnSync('git', ['-C', task.workspace_path, 'worktree', 'remove', '--force', task.workspace_path], { encoding: 'utf8' }); return; }
293
293
  const managed = resolve(board.slug === 'default' ? join(board.root, 'kanban', 'workspaces') : join(board.root, 'kanban', 'boards', board.slug, 'workspaces'));
@@ -368,7 +368,7 @@ export async function runEffects(board, effects = []) {
368
368
  // a person-visible session and is never killed from the board
369
369
  function stopWorker(board, runId) {
370
370
  if (!runId) return;
371
- const row = tx(board, (db) => one(db, 'SELECT metadata, claim_lock FROM task_runs WHERE id = ?', runId));
371
+ const row = view(board, (db) => one(db, 'SELECT metadata, claim_lock FROM task_runs WHERE id = ?', runId));
372
372
  const worker = json(row?.metadata, {})?.supercode;
373
373
  if (worker?.surface === 'headless' && worker.pid) terminate(Number(worker.pid));
374
374
  }
@@ -443,7 +443,7 @@ export const assign = (board, id, profile, { roles } = {}) => act(board, id, 'as
443
443
  /** A triage card fans out into children it waits on (Hermes's decomposer); answers their ids. */
444
444
  export async function decompose(board, id, children, { author = 'user', roles } = {}) {
445
445
  if (!children.length) throw new Error('decompose names at least one child (--child "<title>[@<assignee>]")');
446
- const task = tx(board, (db) => must(db, id));
446
+ const task = view(board, (db) => must(db, id));
447
447
  const ids = children.map((c) => createCard(board, {
448
448
  title: c.title, body: c.body ?? null, assignee: c.assignee ?? task.assignee, machine: task.machine,
449
449
  workspace: task.workspace_path ? `${task.workspace_kind}:${task.workspace_path}` : task.workspace_kind, tenant: task.tenant, createdBy: author,
@@ -464,7 +464,7 @@ export function link(board, parent, child, { roles } = {}) {
464
464
  }
465
465
 
466
466
  export async function unlink(board, parent, child, { roles } = {}) {
467
- const linked = tx(board, (db) => one(db, 'SELECT 1 FROM task_links WHERE parent_id = ? AND child_id = ?', parent, child));
467
+ const linked = view(board, (db) => one(db, 'SELECT 1 FROM task_links WHERE parent_id = ? AND child_id = ?', parent, child));
468
468
  if (!linked) return false;
469
469
  await act(board, child, 'unlink', { roles, payload: { parent, child } });
470
470
  return true;
package/board/store.mjs CHANGED
@@ -8,7 +8,7 @@
8
8
  //
9
9
  // This module opens a board in its backing and gives its callers one primitive, a write transaction; the transitions
10
10
  // are in `engine.mjs`. Hermes's own tools may write a `kanban.db` at the same time: every write there takes SQLite's
11
- // writer lock (`BEGIN IMMEDIATE`) and retries while the other holds it.
11
+ // native transaction boundary and retries an optimistic conflict.
12
12
  import { randomBytes } from 'node:crypto';
13
13
  import { existsSync, mkdirSync, readdirSync, readFileSync } from 'node:fs';
14
14
  import { hostname } from 'node:os';
@@ -152,7 +152,6 @@ export function openBoard(root, slug = DEFAULT_BOARD, { create = false, backing
152
152
  if (use === 'files') return { ...openFilesBoard(path, { create, init: layOut }), root, slug, path, backing: use };
153
153
  if (!kept) mkdirSync(dirname(path), { recursive: true });
154
154
  const db = new DatabaseSync(path);
155
- db.exec('PRAGMA busy_timeout = 10000');
156
155
  db.exec('PRAGMA foreign_keys = OFF');
157
156
  const fresh = !db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'tasks'").get();
158
157
  if (fresh && !create) { db.close(); throw new Error(`${path} is not a board`); }
@@ -165,26 +164,22 @@ export function openBoard(root, slug = DEFAULT_BOARD, { create = false, backing
165
164
  * A read inside the transaction is the state it writes over.
166
165
  */
167
166
  export const tx = (board, fn) => board.tx(fn);
167
+ export const view = (board, fn) => fn(board.db);
168
168
 
169
- // SQLite's own: `BEGIN IMMEDIATE` takes the writer lock up front, retried while Hermes's writers hold it
169
+ // A deferred native transaction, with the whole operation retried on an optimistic conflict.
170
+ // There is no separate application writer lock or contention timeout.
170
171
  function sqliteTx(db, fn) {
171
- for (let attempt = 0; ; attempt += 1) {
172
+ for (;;) {
172
173
  try {
173
- db.exec('BEGIN IMMEDIATE');
174
- break;
175
- } catch (error) {
176
- if (!/locked|busy/i.test(error.message) || attempt >= 50) throw error;
177
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 100);
174
+ db.exec('BEGIN');
175
+ const out=fn(db);
176
+ db.exec('COMMIT');
177
+ return out;
178
+ } catch(error) {
179
+ try {db.exec('ROLLBACK');} catch {}
180
+ if (!/locked|busy/i.test(error.message))throw error;
178
181
  }
179
182
  }
180
- try {
181
- const out = fn(db);
182
- db.exec('COMMIT');
183
- return out;
184
- } catch (error) {
185
- try { db.exec('ROLLBACK'); } catch { /* already rolled back */ }
186
- throw error;
187
- }
188
183
  }
189
184
 
190
185
  export const now = () => Math.floor(Date.now() / 1000);
@@ -116,7 +116,9 @@ export function refreshCardInputs(board, id, { commits = false } = {}) {
116
116
  if (commits) {
117
117
  const own=task.workspace_kind!=='scratch'?task.workspace_path:null;
118
118
  const parent=!own && task.subtask_of?board.db.prepare('SELECT workspace_path FROM tasks WHERE id=?').get(task.subtask_of)?.workspace_path:null;
119
- observeLastCommit(own??parent??null,id);
119
+ const path=own??parent??null;
120
+ if(path)commitTimes.delete(`${path}:${id}`);
121
+ observeLastCommit(path,id);
120
122
  }
121
123
  }
122
124
 
package/board/ztrack.mjs CHANGED
@@ -39,7 +39,7 @@ class ZtrackBoard {
39
39
  this.codec = require('@volter/ztrack/board-document');
40
40
  this.preset = require('@volter/ztrack/kanban-preset');
41
41
  this.runtime = new DatabaseSync(`${marker}.runtime.db`);
42
- if (!this.runtime.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='state'").get()) this.runtime.exec('CREATE TABLE state (key TEXT PRIMARY KEY, value TEXT NOT NULL)');
42
+ this.readOnly(()=>{ if (!this.runtime.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='state'").get()) this.runtime.exec('CREATE TABLE IF NOT EXISTS state (key TEXT PRIMARY KEY, value TEXT NOT NULL)'); });
43
43
  this.errors = new Map(); this.preserve = new Map();
44
44
  this.mem = null; this.active = false; this.rows = new RuntimeRows(this.runtime);
45
45
  mkdirSync(dirname(this.path), { recursive: true });
@@ -169,7 +169,7 @@ class ZtrackBoard {
169
169
  try { this.runtime.exec('ROLLBACK'); } catch { /* intent already durable */ }
170
170
  this.invalidate();
171
171
  if (/busy|locked|Hermes wrote|document changed during recovery/i.test(error.message)) continue;
172
- if (committed) error.message += ' (the runtime journal retains any unfinished document write)';
172
+ if (committed) error.message += ' (the operation reached its durable intent; inspect its recorded outcome before retrying)';
173
173
  throw error;
174
174
  } finally { this.active = false; this.computing = false; this.pendingValues = null; }
175
175
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/supercode-orchestrator",
3
- "version": "0.4.91",
3
+ "version": "0.4.93",
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": {