@volter/supercode-orchestrator 0.4.35 → 0.4.37
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 +22 -1
- package/board/engine.mjs +13 -1
- package/board/workflow.mjs +18 -0
- package/package.json +1 -1
package/board/cli.mjs
CHANGED
|
@@ -46,6 +46,7 @@ supercode's own:
|
|
|
46
46
|
notify-subscribe <id> --address sc:<machine>:<harness>:<id> [--role creator|worker|dependency]
|
|
47
47
|
Any verb … --at sc:<machine>:board:<home>: sent as mail to a board kept on another machine.
|
|
48
48
|
<event> <id>... [reason] [--<field> value]: any other event the board's workflow declares, its flags the event's fields.
|
|
49
|
+
goto <id> <status> [reason] [--<field> value]: the event the workflow says takes the card to that status, sent.
|
|
49
50
|
|
|
50
51
|
specify/decompose with no spec start a session on the triage card to write it. A card's own session may comment on,
|
|
51
52
|
block (with --kind), heartbeat and request review of its card, specify or decompose it in triage, and file, complete,
|
|
@@ -315,7 +316,21 @@ export async function runWorkflow(argv) {
|
|
|
315
316
|
updated: (a, b) => (b.completed_at ?? b.started_at ?? b.created_at) - (a.completed_at ?? a.started_at ?? a.created_at),
|
|
316
317
|
};
|
|
317
318
|
if (args.sort) { if (!sorters[args.sort]) throw new Error(`--sort is one of ${Object.keys(sorters).join(', ')}`); cards.sort(sorters[args.sort]); }
|
|
318
|
-
if (args.json) {
|
|
319
|
+
if (args.json) {
|
|
320
|
+
// beyond Hermes's fields: the workflow's own status, the cards it waits on and its open run, so a reader of the
|
|
321
|
+
// whole board (a board file's sync) needs no `show` per card
|
|
322
|
+
const extra = withBoard((b) => new Map(cards.map((t) => {
|
|
323
|
+
const run = t.current_run_id ? b.db.prepare('SELECT id, status, started_at, metadata FROM task_runs WHERE id = ?').get(t.current_run_id) : null;
|
|
324
|
+
const session = run ? json(run.metadata, {})?.supercode ?? {} : {};
|
|
325
|
+
return [t.id, {
|
|
326
|
+
workflow_status: t.stage ?? t.status,
|
|
327
|
+
parents: b.db.prepare('SELECT parent_id FROM task_links WHERE child_id = ? ORDER BY parent_id').all(t.id).map((r) => r.parent_id),
|
|
328
|
+
run: run ? { id: run.id, status: run.status, started_at: run.started_at, session: session.address ?? session.session_id ?? null } : null,
|
|
329
|
+
}];
|
|
330
|
+
})));
|
|
331
|
+
say(args, '', cards.map((t) => ({ ...cardDict(t), ...extra.get(t.id) })));
|
|
332
|
+
return 0;
|
|
333
|
+
}
|
|
319
334
|
const others = boardsOf(root, { includeArchived: false }).length - 1;
|
|
320
335
|
if (others > 0) process.stdout.write(`Board: ${slug} (${others} other board${others === 1 ? '' : 's'} — \`supercode workflow boards list\`)\n\n`);
|
|
321
336
|
process.stdout.write(cards.length ? `${cards.map(line).join('\n')}\n` : '(no matching tasks)\n');
|
|
@@ -740,6 +755,12 @@ export async function runWorkflow(argv) {
|
|
|
740
755
|
say(args, `${id} is worked by ${got.address} (pane ${got.pane ?? '?'} in ${got.workspace ?? 'its card\'s directory'})`, { ok: true, ...got });
|
|
741
756
|
return 0;
|
|
742
757
|
}
|
|
758
|
+
case 'goto': {
|
|
759
|
+
need(2, 'a card id and the status to take it to');
|
|
760
|
+
const { _: _words, root: _r, board: _b, json: _j, at: _a, ...fields } = args;
|
|
761
|
+
const moved = await withBoardAsync((b) => engine.goto(b, id, rest[1], { payload: { ...fields, reason: args.reason ?? (rest.slice(2).join(' ') || null) } }));
|
|
762
|
+
say(args, moved.event ? `${id} → ${moved.to} (${moved.event.replace(/_/g, '-')})` : `${id} is already ${moved.to}`, { ok: true, ...moved }); return 0;
|
|
763
|
+
}
|
|
743
764
|
case 'close': {
|
|
744
765
|
need(1, 'a card id');
|
|
745
766
|
const closed = withBoard((b) => closeSession(b, id));
|
package/board/engine.mjs
CHANGED
|
@@ -12,7 +12,7 @@ import { existsSync, rmSync } from 'node:fs';
|
|
|
12
12
|
import { join, resolve } from 'node:path';
|
|
13
13
|
import { DatabaseSync } from 'node:sqlite';
|
|
14
14
|
import { claimer, event, json, newTaskId, now, tx } from './store.mjs';
|
|
15
|
-
import { fire, loadWorkflow, setStatus, settle, startNew, statusOf, workflowFor } from './workflow.mjs';
|
|
15
|
+
import { eventTo, fire, loadWorkflow, setStatus, settle, startNew, statusOf, workflowFor } from './workflow.mjs';
|
|
16
16
|
|
|
17
17
|
export const STATUSES = ['triage', 'todo', 'scheduled', 'ready', 'running', 'blocked', 'review', 'done', 'archived'];
|
|
18
18
|
export const BLOCK_KINDS = ['dependency', 'needs_input', 'capability', 'transient'];
|
|
@@ -334,6 +334,18 @@ export async function act(board, id, name, { roles = null, payload = {}, extra =
|
|
|
334
334
|
return { from: result.from, to: result.to, moved: result.moved };
|
|
335
335
|
}
|
|
336
336
|
|
|
337
|
+
/** Take card `id` to `status` by the event the workflow says goes there for this caller (`goto`). */
|
|
338
|
+
export async function goto(board, id, status, { roles = null, payload = {} } = {}) {
|
|
339
|
+
const entry = await loadWorkflow(board.root);
|
|
340
|
+
const { name, from } = tx(board, (db) => {
|
|
341
|
+
roles ??= callerRoles(db, id);
|
|
342
|
+
return { name: eventTo(db, board, entry, id, status, { roles, payload }), from: statusOf(db, getTask(db, id)) };
|
|
343
|
+
});
|
|
344
|
+
if (from === status) return { from, to: status, moved: false, event: null };
|
|
345
|
+
if (!name) throw new Error(`${id} is ${from}; no event you may send takes it to ${status}`);
|
|
346
|
+
return { ...(await act(board, id, name, { roles, payload })), event: name };
|
|
347
|
+
}
|
|
348
|
+
|
|
337
349
|
/** The I/O a committed move asked for. */
|
|
338
350
|
export async function runEffects(board, effects = []) {
|
|
339
351
|
for (const e of effects) {
|
package/board/workflow.mjs
CHANGED
|
@@ -168,6 +168,24 @@ export function fire(db, board, entry, id, name, { roles = [], payload = {}, ext
|
|
|
168
168
|
return { from, to: statusOf(db, cardOf(db, id)), moved: to !== from, effects };
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
/**
|
|
172
|
+
* The event that takes card `id` to `status` for a sender in `roles`: the first the card's status (then the workflow)
|
|
173
|
+
* declares whose transition for that sender, with `payload`, goes there. Null when none does. Its validators are
|
|
174
|
+
* the event's own, checked when it is sent.
|
|
175
|
+
*/
|
|
176
|
+
export function eventTo(db, board, entry, id, status, { roles = [], payload = {} } = {}) {
|
|
177
|
+
const { workflow, params } = entry;
|
|
178
|
+
if (!workflow.statuses[status]) throw new Error(`the board's workflow declares no status ${status}`);
|
|
179
|
+
const task = cardOf(db, id);
|
|
180
|
+
const from = statusOf(db, task);
|
|
181
|
+
const scope = scopeOf(db, board, task, { params, event: payload, extra: { lanes: lanesIn(entry), sender: roles } });
|
|
182
|
+
for (const name of new Set([...Object.keys(workflow.statuses[from]?.on ?? {}), ...Object.keys(workflow.on ?? {})])) {
|
|
183
|
+
const taken = transitionsFor(workflow, from, name).filter((t) => !t.by?.length || t.by.some((r) => roles.includes(r))).find((t) => holds(t.guard ?? null, scope));
|
|
184
|
+
if (taken?.target && String(templateValue(taken.target, scope) ?? '') === status) return name;
|
|
185
|
+
}
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
|
|
171
189
|
function cardOf(db, id) {
|
|
172
190
|
const row = one(db, 'SELECT * FROM tasks WHERE id = ?', id);
|
|
173
191
|
if (!row) throw new Error(`no card ${id}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-orchestrator",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.37",
|
|
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": {
|