@volter/supercode-orchestrator 0.5.58 → 0.5.60

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 +15 -9
  2. package/package.json +1 -1
@@ -206,7 +206,8 @@ function unpublishedAlive(session) {
206
206
  }
207
207
 
208
208
  /** Native activity comes from the machine's harness discovery, not catalogue age. One discovery (up to 2000
209
- * sessions, seconds each) serves a round's every native session on that machine, as paneLists serves its panes. */
209
+ * sessions, seconds each) serves a round's every native session on that machine: taken once per round (tick clears
210
+ * them), however long the round runs. A round of 163 s took it 28 times when a reading lasted 15 s. */
210
211
  const discoveries = new Map();
211
212
  // A machine's pane list and harness discovery are read before a board write opens, never inside one: a door that
212
213
  // takes seconds would hold the board's write lock past a worker's own command's busy wait. A write that needs a
@@ -222,7 +223,7 @@ function readBefore(fleet, session) {
222
223
  }
223
224
  function nativeSessions(machine, harness) {
224
225
  const key = `${normalMachine(machine)}\u0000${harness}`, seen = discoveries.get(key);
225
- if (seen && Date.now() - seen.at <= READING_MS) return seen.sessions;
226
+ if (seen) return seen.sessions;
226
227
  if (readingsOnly) throw unavailable('native liveness is read before a board write, not inside it');
227
228
  const sessions = discoverNative(machine, harness);
228
229
  discoveries.set(key, { at: Date.now(), sessions });
@@ -640,7 +641,7 @@ function startCards(board, fleet, lanes, settings, report, entry, effects, { onl
640
641
  const candidates = [];
641
642
  const waiting = (task, why) => {
642
643
  report.queued.push([task.id, why]);
643
- if (!dry) tx(board, db => noteGuard(db, task.id, why));
644
+ if (!dry && !guardNoted(board.db, task.id, why)) tx(board, db => noteGuard(db, task.id, why));
644
645
  };
645
646
  for (const task of everyCard) {
646
647
  if (only && task.id !== only) continue;
@@ -673,7 +674,7 @@ function startCards(board, fleet, lanes, settings, report, entry, effects, { onl
673
674
  status: c.plan.target, stored: c.plan.target, model: c.task.model_override ?? c.plan.lane?.model ?? held.defaults?.[c.plan.lane?.harness] ?? null, harness: c.plan.lane?.harness ?? null });
674
675
  const waitingEntries = candidates.map(entryOf);
675
676
  // a dry run (dispatch --dry-run) plans the same way and writes nothing: no notes, no moves, no launches
676
- const note = (id, why) => { if (!dry) tx(board, (db) => noteGuard(db, id, why)); };
677
+ const note = (id, why) => { if (!dry && !guardNoted(board.db, id, why)) tx(board, (db) => noteGuard(db, id, why)); };
677
678
  for (const [i, c] of candidates.entries()) {
678
679
  const { task, plan, scope } = c;
679
680
  if (!plan.profile) { report.unassigned.push(task.id); note(task.id, 'no profile runs it (no assignee, and the workflow names none)'); continue; }
@@ -714,7 +715,7 @@ function startCards(board, fleet, lanes, settings, report, entry, effects, { onl
714
715
  } catch (error) {
715
716
  const reason = error.refusal ?? String(error.message).slice(-300);
716
717
  // another round's start is that round's to say; this one writes nothing for it
717
- if (!error.claimedElsewhere) tx(board, (db) => noteGuard(db, task.id, reason));
718
+ if (!error.claimedElsewhere && !guardNoted(board.db, task.id, reason)) tx(board, (db) => noteGuard(db, task.id, reason));
718
719
  report.queued.push([task.id, reason]);
719
720
  continue;
720
721
  }
@@ -1247,12 +1248,16 @@ function memoryPressure() {
1247
1248
 
1248
1249
  /** Record a guard reason once per change, not once per tick. */
1249
1250
  function noteGuard(db, id, reason) {
1250
- // once per reason per entry into the status: a card back in ready (a new `since`) is noted again, so the workflow's
1251
- // after-timers that read the reason (card.queue_reason) see it
1251
+ if (guardNoted(db, id, reason)) return;
1252
+ event(db, id, 'respawn_guarded', { reason });
1253
+ }
1254
+ /** Whether `reason` is already noted: once per reason per entry into the status (a card back in ready, a new `since`, is
1255
+ * noted again, so the workflow's after-timers that read the reason, card.queue_reason, see it). Read before a write
1256
+ * is opened too: a card that waits for the same reason round after round costs no transaction. */
1257
+ function guardNoted(db, id, reason) {
1252
1258
  const since = Number(db.prepare('SELECT since FROM supercode_cards WHERE task_id = ?').get(id)?.since ?? 0);
1253
1259
  const last = db.prepare("SELECT payload FROM task_events WHERE task_id = ? AND kind IN ('respawn_guarded', 'claimed') AND created_at >= ? ORDER BY id DESC LIMIT 1").get(id, since);
1254
- if (json(last?.payload, {})?.reason === reason) return;
1255
- event(db, id, 'respawn_guarded', { reason });
1260
+ return json(last?.payload, {})?.reason === reason;
1256
1261
  }
1257
1262
 
1258
1263
  /** Hermes's emergency stop: the `ESTOP` sentinel in the home or in `~/.hermes` stops new starts (running ones go on). */
@@ -2555,6 +2560,7 @@ export async function deliver({ root, slugs = null }) {
2555
2560
  */
2556
2561
  export async function tick({ root, lanes, settings, only = null, slugs = null, dispatch = true, params = {}, deploymentReceipt = false }) {
2557
2562
  fleetRoot = root;
2563
+ discoveries.clear();
2558
2564
  // a verb's flags (dispatch --max, --failure-limit) are params of this tick, read by the workflow as its own
2559
2565
  const deployment = deploymentReceipt ? workflowRevision(root) : null;
2560
2566
  const loaded = await loadWorkflow(root, { fresh: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/supercode-orchestrator",
3
- "version": "0.5.58",
3
+ "version": "0.5.60",
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": {