@volter/supercode-orchestrator 0.4.70 → 0.4.72

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
@@ -13,7 +13,7 @@ import { holdDispatcherLock } from '../hermes-locks.mjs';
13
13
  import { backingOf, boardExists, boardPath, DEFAULT_BOARD, event, json, listBoards, logsDir, now, openBoard, setActor, tx } from './store.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';
16
- import { loadWorkflow, statusOf, useWorkflow, workflowEntry, workflowFor } from './workflow.mjs';
16
+ import { loadWorkflow, scopeOf, statusOf, useWorkflow, workflowEntry, workflowFor } from './workflow.mjs';
17
17
  import { evaluate } from './expr.mjs';
18
18
 
19
19
  const HELP = `supercode workflow <verb> [--root <home>] [--board <slug>] [--json]
@@ -356,16 +356,20 @@ export async function runWorkflow(argv) {
356
356
  got.runs = engine.runs(b.db, id, { stateType: args['state-type'] ?? null, stateName: args['state-name'] ?? null });
357
357
  got.diagnostics = engine.diagnose(b.db, got.task, { failureLimit: settings.failureLimit });
358
358
  got.stage = statusOf(b.db, got.task);
359
+ got.time = scopeOf(b.db, b, got.task, { params: workflowFor(root).params }).card.time;
359
360
  return got;
360
361
  });
361
362
  const t = card.task;
362
363
  const latest = [...card.runs].reverse().find((r) => r.summary)?.summary ?? null;
363
364
  if (args.json) {
364
- say(args, '', { task: cardDict(t), latest_summary: latest, parents: card.parents.map((p) => p.id), children: card.children, subtasks: card.subtasks, comments: card.comments, events: card.events, runs: card.runs, subscriptions: card.subscriptions, diagnostics: card.diagnostics });
365
+ say(args, '', { task: cardDict(t), latest_summary: latest, parents: card.parents.map((p) => p.id), children: card.children, subtasks: card.subtasks, comments: card.comments, events: card.events, runs: card.runs, time: card.time, subscriptions: card.subscriptions, diagnostics: card.diagnostics });
365
366
  return 0;
366
367
  }
367
368
  const out = [`Task ${t.id}: ${t.title}`, ` status: ${card.stage}${card.stage !== t.status ? ` (${t.status} in kanban.db)` : ''}`, ` assignee: ${t.assignee || '-'}`];
368
369
  if (t.machine) out.push(` machine: ${t.machine}`);
370
+ // where the card's time went: each role's runs, and the rest waiting
371
+ const mins = (sec) => `${Math.round(sec / 60)}m`;
372
+ out.push(` time: ${Object.entries(card.time).filter(([, v]) => v > 0).map(([k, v]) => `${k} ${mins(v)}`).join(', ') || '-'}`);
369
373
  if (t.subtask_of) out.push(` subtask of: ${t.subtask_of}`);
370
374
  if (t.tenant) out.push(` tenant: ${t.tenant}`);
371
375
  out.push(` workspace: ${t.workspace_kind}${t.workspace_path ? ` @ ${t.workspace_path}` : ''}`);
@@ -108,6 +108,22 @@ function onlyTicks(was, body) {
108
108
  });
109
109
  }
110
110
 
111
+ /** Seconds a card's runs spent by role, and the rest of its life as `waiting`. */
112
+ function timeOf(db, task) {
113
+ const t = now();
114
+ const out = { coding: 0, reviewing: 0, auditing: 0, triaging: 0 };
115
+ const key = { implementer: 'coding', reviewer: 'reviewing', auditor: 'auditing', triager: 'triaging' };
116
+ for (const r of all(db, 'SELECT started_at, ended_at, metadata FROM task_runs WHERE task_id = ?', task.id)) {
117
+ const role = json(r.metadata, {})?.supercode?.role ?? 'implementer';
118
+ const spent = Math.max(0, Number(r.ended_at ?? t) - Number(r.started_at ?? t));
119
+ const k = key[role] ?? role;
120
+ out[k] = (out[k] ?? 0) + spent;
121
+ }
122
+ const worked = Object.values(out).reduce((a, b) => a + b, 0);
123
+ out.waiting = Math.max(0, t - Number(task.created_at ?? t) - worked);
124
+ return out;
125
+ }
126
+
111
127
  /** The session message `id` was filed for (`supercode message show`): who was asked; or null. */
112
128
  function askedOf(id) {
113
129
  if (!id) return null;
@@ -155,6 +171,9 @@ export function scopeOf(db, board, task, { params, event = {}, extra = {} } = {}
155
171
  // the message whose answer the standing block waits for (`block --waiting-on`), and that answer's id once it has landed
156
172
  waiting_on: waitingOn,
157
173
  answer: () => answerTo(waitingOn),
174
+ // the seconds its runs spent, by the role each run was for (implementer: coding, reviewer: reviewing, …), an open
175
+ // run counted to now; `waiting` is the rest of its life since it was filed
176
+ time: timeOf(db, task),
158
177
  // whether an edit of its text only ticks outcome lines (the card's own session's door to its acceptance)
159
178
  only_ticks: (body) => onlyTicks(task.body, body),
160
179
  asked_of: () => askedOf(waitingOn),
@@ -534,6 +553,15 @@ const ACTIONS = {
534
553
  return;
535
554
  }
536
555
  },
556
+ // an event sent to other cards (`to`: an expression naming their ids, e.g. a card's open subtasks), as `as` sends it;
557
+ // a card it does not apply to now is passed over
558
+ send(ctx, { event: name, to, as = 'manager', payload = {} }, scope) {
559
+ const ids = [].concat(evaluate(String(to), scope) ?? []).map(String).filter((other) => other && other !== ctx.id);
560
+ for (const other of ids) {
561
+ try { ctx.effects.push(...(fire(ctx.db, ctx.board, ctx.entry, other, name, { roles: [as], payload }).effects ?? [])); }
562
+ catch (error) { if (!error.noTransition && !error.refusal && !/does not apply|there is for/.test(error.message)) throw error; }
563
+ }
564
+ },
537
565
  // a message from the board to a session (`to`: its address), kept on the card as a board_message event until the
538
566
  // dispatcher has sent it: a move settled by any verb (a list settles too) is never lost with its effects
539
567
  tell(ctx, { to, text }) { if (to) record(ctx.db, ctx.id, 'board_message', { to: String(to), text: String(text ?? '') }, null); },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/supercode-orchestrator",
3
- "version": "0.4.70",
3
+ "version": "0.4.72",
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": {