@volter/supercode-orchestrator 0.5.74 → 0.5.75

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
@@ -20,6 +20,8 @@ import { takeEpoch } from './fence.mjs';
20
20
  import { dispatcherNotice, holdOwnerHealth, holdRound, holdSwitch } from './owner-health.mjs';
21
21
  import { boardTool } from './tools.mjs';
22
22
  import { recordSlow, takeSteps } from '@volter/supercode-harness-sdk/slow-log';
23
+ // read by name, so an older harness SDK without them still loads: the owner then keeps no per-verb or loop lines
24
+ import * as slowLog from '@volter/supercode-harness-sdk/slow-log';
23
25
  import { basename, dirname, join, resolve } from 'node:path';
24
26
  import { fileURLToPath } from 'node:url';
25
27
  import { homedir, tmpdir, userInfo } from 'node:os';
@@ -1518,8 +1520,20 @@ async function answerVerbs(root, log) {
1518
1520
  return () => { server.close(); try { unlinkSync(path); } catch { /* gone */ } lock.release(); };
1519
1521
  }
1520
1522
 
1521
- /** One request to the owner, answered whole: the verb run in the caller's call, its output kept for the answer. */
1523
+ /** One request to the owner, answered whole: the verb run in the caller's call, its output kept for the answer. A
1524
+ * verb that takes more than a second is a slow line of its own, with the steps that ended while it ran (its own and,
1525
+ * in this one process, any other call's that held it). */
1522
1526
  async function answerOne(root, line) {
1527
+ const began = performance.now();
1528
+ let named = null, by = null;
1529
+ try { return await answerRequest(root, line, (verb, caller) => { named = verb; by = caller; }); }
1530
+ finally {
1531
+ const ms = performance.now() - began;
1532
+ if (named) recordSlow({ kind: 'call', name: `owner verb ${named}`, ms, slowest: null, extra: { caller: by, steps: slowLog.stepsWithin?.(began, performance.now()) ?? null } });
1533
+ }
1534
+ }
1535
+
1536
+ async function answerRequest(root, line, naming) {
1523
1537
  try {
1524
1538
  const request = JSON.parse(line);
1525
1539
  const tool = request?.tool && typeof request.tool.name === 'string' ? request.tool : null;
@@ -1529,6 +1543,7 @@ async function answerOne(root, line) {
1529
1543
  if (BOARD_OPERATION_VERBS.has(verb)) return { code: 1, stdout: '', stderr: `supercode workflow: ${verb} is run on the board's machine as its own act, not sent to the owner\n` };
1530
1544
  const session = request.session && typeof request.session.id === 'string' && ['claude-code', 'codex'].includes(request.session.harness) ? request.session : null;
1531
1545
  const caller = typeof request.caller === 'string' ? request.caller : session ? `sc:${localMachine()}:${session.harness}:${session.id}` : null;
1546
+ naming(tool ? `tool ${tool.name}` : verb, caller);
1532
1547
  const facts = Object.fromEntries(Object.entries(request.facts ?? {}).filter(([key, value]) => CALLER_FACTS.includes(key) && typeof value === 'string'));
1533
1548
  // a caller names its Teams context (the CLI and the daemon always do): one that names none is refused before anything
1534
1549
  // is done, never answered with the owner's
@@ -1630,6 +1645,8 @@ async function serve(root, args = {}) {
1630
1645
  try { (await import('../hermes-cli.mjs')).hermesInstall(); } catch { /* none installed */ }
1631
1646
  let closeVerbs;
1632
1647
  try { closeVerbs = await answerVerbs(root, log); } catch (error) { unfollowHome(); throw error; }
1648
+ // a held event loop is every caller's wait at once: each hold over a second is a slow line naming what held it
1649
+ const unwatchLoop = slowLog.watchEventLoop?.({ name: 'board owner event loop blocked' });
1633
1650
  log(`owning the boards of ${root} on ${localMachine()} (orchestrator ${running ?? 'unknown'}); dispatching where a board's dispatching is on (\`supercode workflow dispatching\`)`);
1634
1651
  events = watchBoard({ root, binary: supercodeBin(), env: commandEnv(), log });
1635
1652
  // The board's events are published by this machine's connector, not by the dispatcher (D137, D138 step 4): the home is
@@ -1718,7 +1735,7 @@ async function serve(root, args = {}) {
1718
1735
  } finally {
1719
1736
  // a fetch in flight lands, and what it brought is merged, while this owner still holds the home's lock
1720
1737
  try { const { settleDocuments } = await import('./git-document.mjs'); await settleDocuments(); } catch { /* nothing in flight */ }
1721
- closeVerbs();
1738
+ unwatchLoop?.(); closeVerbs();
1722
1739
  unfollowHome();
1723
1740
  await closeDoors();
1724
1741
  events?.close();
package/board/store.mjs CHANGED
@@ -217,7 +217,7 @@ export const tx = (board, fn) => {
217
217
  const caller = /at (?:async )?([\w.$<>]+) /.exec(String(new Error().stack).split('\n')[2] ?? '')?.[1] ?? 'unknown';
218
218
  const started = performance.now();
219
219
  try {
220
- const result = board.tx(db => {const result=fn(db);recordCardPublications(db,actorOf());return result;});
220
+ const result = board.tx(db => {const result=fn(db);const began=performance.now();try{recordCardPublications(db,actorOf());}finally{noteStep('board tx publications',performance.now()-began);}return result;});
221
221
  signalBoardWrite(board.root, actorOf());
222
222
  return result;
223
223
  } finally { noteStep(`board tx from ${caller}`, performance.now() - started); }
package/board/ztrack.mjs CHANGED
@@ -17,6 +17,7 @@ import { DatabaseSync } from 'node:sqlite';
17
17
  import { actorOf, contention, event, json, now, principalOf } from './store.mjs';
18
18
  import { GitDocument } from './git-document.mjs';
19
19
  import { noteStep, recordSlow } from '@volter/supercode-harness-sdk/slow-log';
20
+ import * as slowLog from '@volter/supercode-harness-sdk/slow-log';
20
21
 
21
22
  /** Run `fn` as a named step of the running command (the slow log names its slowest step). */
22
23
  const timedStep = (step, fn) => { const started = performance.now(); try { return fn(); } finally { noteStep(step, performance.now() - started); } };
@@ -100,7 +101,7 @@ class ZtrackBoard {
100
101
  const said = current().said;
101
102
  if (this.git && !said.has(`behind:${this.path}`)) {
102
103
  said.add(`behind:${this.path}`);
103
- const late = this.git.behind();
104
+ const late = timedStep('board open git behind', () => this.git.behind());
104
105
  // A push younger than a minute is still on its way.
105
106
  if (late && Date.now() - late.since > 60_000) current().err(`board: warning: ${late.commits} board document commit${late.commits === 1 ? '' : 's'} not published since ${new Date(late.since).toISOString().slice(0, 16)}Z${late.error ? ` (last push: ${late.error})` : ''}; the dispatcher's next round retries\n`);
106
107
  }
@@ -112,12 +113,12 @@ class ZtrackBoard {
112
113
  // `sqlite3 <store> "select …"` made the empty file the next release then opened instead of importing).
113
114
  if (existsSync(this.file) && statSync(this.file).size === 0) unlinkSync(this.file);
114
115
  if (!existsSync(this.file)) this.create(legacy);
115
- this.conn = new DatabaseSync(this.file);
116
+ this.conn = timedStep('board open connection', () => new DatabaseSync(this.file));
116
117
  this.refuseUnimported(legacy);
117
118
  this.errors = new Map(); this.statements = new Map(); this.hashes = new Map();
118
119
  this.active = false; this.overlay = false; this.seen = null; this.refused = new Set();
119
- this.prepareConnection();
120
- this.loadErrors();
120
+ timedStep('board open schema', () => this.prepareConnection());
121
+ timedStep('board open errors', () => this.loadErrors());
121
122
  const self = this;
122
123
  this.db = {
123
124
  boardErrors: self.errors,
@@ -396,8 +397,7 @@ class ZtrackBoard {
396
397
  try {
397
398
  txStarted = performance.now();
398
399
  this.dropOverlay();
399
- this.conn.exec('BEGIN');
400
- fenced(this.conn);
400
+ timedStep('board tx begin', () => { this.conn.exec('BEGIN'); fenced(this.conn); });
401
401
  this.active = true; this.contended = false;
402
402
  this.conn.exec('DELETE FROM temp.board_dirty; DELETE FROM temp.board_touched');
403
403
  this.refused = new Set();
@@ -418,12 +418,12 @@ class ZtrackBoard {
418
418
  this.put('pending', { intent, files });
419
419
  this.put('pending_hashes', Object.fromEntries(files.map((f) => [f.role, { before: f.before, after: f.after }])));
420
420
  }
421
- this.conn.exec('COMMIT');
421
+ timedStep('board tx commit', () => this.conn.exec('COMMIT'));
422
422
  // A long transaction holds every other writer of this board (the dispatcher's tick included), so it is a line
423
423
  // of its own in the slow log, from whichever process ran it.
424
424
  const held = performance.now() - txStarted;
425
425
  noteStep('board transaction', held);
426
- recordSlow({ kind: 'call', name: 'board transaction', ms: held, slowest: null, extra: { board: this.path } });
426
+ recordSlow({ kind: 'call', name: 'board transaction', ms: held, slowest: null, extra: { board: this.path, steps: slowLog.stepsWithin?.(txStarted, performance.now()) ?? null } });
427
427
  outcome = { result, files, intent, recovered };
428
428
  } catch (error) {
429
429
  try { this.conn.exec('ROLLBACK'); } catch { /* not begun */ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/supercode-orchestrator",
3
- "version": "0.5.74",
3
+ "version": "0.5.75",
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": {