@volter/supercode-orchestrator 0.4.77 → 0.4.79
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/workflow.mjs +37 -6
- package/board/ztrack.mjs +9 -0
- package/package.json +1 -1
package/board/workflow.mjs
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// the engine's primitives (the rows Hermes's tables hold); the I/O an action asks for (stopping a worker, closing a
|
|
6
6
|
// pane, sending a session a message, cleaning a workspace) is answered as effects the caller runs after commit.
|
|
7
7
|
import { spawnSync } from 'node:child_process';
|
|
8
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
8
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
9
9
|
import { resolve, relative, isAbsolute } from 'node:path';
|
|
10
10
|
import { randomBytes } from 'node:crypto';
|
|
11
11
|
import { actorOf, event as record, json, now } from './store.mjs';
|
|
@@ -78,11 +78,33 @@ function pattern(p) {
|
|
|
78
78
|
|
|
79
79
|
// the time of the latest commit that names `id` in its message, on any ref of the repository at `path` (a worktree's
|
|
80
80
|
// pushes land on the shared remote-tracking refs), or null (asked only when an expression does)
|
|
81
|
+
const commitTimes = new Map();
|
|
81
82
|
function lastCommitAt(path, id) {
|
|
82
83
|
if (!path) return null;
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
|
|
84
|
+
const key = `${path}:${id}`;
|
|
85
|
+
const cached = commitTimes.get(key);
|
|
86
|
+
if (cached && now() - cached.at < 15) return cached.value;
|
|
87
|
+
const readCommit = (repo) => {
|
|
88
|
+
const r = spawnSync('git', ['-C', repo, 'log', '--all', '-1', '--format=%ct', '--fixed-strings', `--grep=${id}`], { encoding: 'utf8', timeout: 10_000 });
|
|
89
|
+
const value = Number(r.stdout?.trim());
|
|
90
|
+
return { repo: r.status === 0, value: Number.isFinite(value) && value > 0 ? value : null };
|
|
91
|
+
};
|
|
92
|
+
const own = readCommit(path);
|
|
93
|
+
let value = own.value;
|
|
94
|
+
if (!own.repo) {
|
|
95
|
+
// A fleet card may span sibling repositories under its declared workspace.
|
|
96
|
+
// Only direct children with a Git door participate; never scan arbitrary home trees.
|
|
97
|
+
let entries = []; try { entries = readdirSync(path, { withFileTypes: true }); } catch {}
|
|
98
|
+
for (const child of entries) {
|
|
99
|
+
if (!child.isDirectory() && !child.isSymbolicLink()) continue;
|
|
100
|
+
const repo = resolve(path, child.name);
|
|
101
|
+
if (!existsSync(resolve(repo, '.git'))) continue;
|
|
102
|
+
const t = readCommit(repo).value;
|
|
103
|
+
if (t != null) value = Math.max(value ?? 0, t);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
commitTimes.set(key, { at: now(), value });
|
|
107
|
+
return value;
|
|
86
108
|
}
|
|
87
109
|
|
|
88
110
|
/** The first answer to message `id` in its sender's mailbox (`supercode message show --replies`), as its id; or null. */
|
|
@@ -490,8 +512,8 @@ const ACTIONS = {
|
|
|
490
512
|
cardField(ctx, 'every', interval == null ? null : String(interval));
|
|
491
513
|
cardField(ctx, 'next_run_at', interval == null ? null : now());
|
|
492
514
|
},
|
|
493
|
-
card_event(ctx, { name, payload = {} }) {
|
|
494
|
-
const result = fire(ctx.db, ctx.board, ctx.entry, ctx.id, name, { roles: [
|
|
515
|
+
card_event(ctx, { name, payload = {}, as = ['platform'] }) {
|
|
516
|
+
const result = fire(ctx.db, ctx.board, ctx.entry, ctx.id, name, { roles: [].concat(as), payload, extra: { lanes: lanesIn(ctx.entry) } });
|
|
495
517
|
ctx.effects.push(...result.effects);
|
|
496
518
|
},
|
|
497
519
|
record(ctx, args, scope) {
|
|
@@ -762,12 +784,21 @@ function settleRelations(db, board, entry, id, effects) {
|
|
|
762
784
|
const row = one(db, 'SELECT blocked_by FROM supercode_cards WHERE task_id = ?', id);
|
|
763
785
|
const blocks = json(row?.blocked_by, []) ?? [];
|
|
764
786
|
for (const block of blocks) {
|
|
787
|
+
if (!json(one(db, 'SELECT blocked_by FROM supercode_cards WHERE task_id = ?', id)?.blocked_by, []).some((b) => b.id === block.id)) continue;
|
|
765
788
|
const dependency = block.kind === 'card' ? one(db, 'SELECT * FROM tasks WHERE id = ?', block.id) : null;
|
|
766
789
|
const status = dependency ? statusOf(db, dependency) : null;
|
|
767
790
|
const answer = block.kind === 'message' ? answerTo(block.id) : null;
|
|
768
791
|
const policy = entry.workflow.relations.blocked_by;
|
|
769
792
|
const name = answer ? policy.answered : status === 'done' ? policy.completed : status === 'cancelled' ? policy.cancelled : null;
|
|
770
793
|
if (name) effects.push(...fire(db, board, entry, id, name, { roles: ['platform'], payload: { on: block.id, reason: answer ? `reply ${answer}` : `${block.id} ${status}` } }).effects);
|
|
794
|
+
else if (policy.aged && policy.age_seconds != null) {
|
|
795
|
+
const seconds = Number(evaluate(String(policy.age_seconds), { params: entry.params }));
|
|
796
|
+
if (seconds > 0 && now() - Number(block.notified_at ?? block.since ?? now()) >= seconds) {
|
|
797
|
+
const current = json(one(db, 'SELECT blocked_by FROM supercode_cards WHERE task_id = ?', id)?.blocked_by, []);
|
|
798
|
+
run(db, 'UPDATE supercode_cards SET blocked_by = ? WHERE task_id = ?', JSON.stringify(current.map((b) => b.id === block.id ? { ...b, notified_at: now() } : b)), id);
|
|
799
|
+
effects.push(...fire(db, board, entry, id, policy.aged, { roles: ['platform'], payload: { on: block.id, reason: block.reason } }).effects);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
771
802
|
}
|
|
772
803
|
}
|
|
773
804
|
|
package/board/ztrack.mjs
CHANGED
|
@@ -228,6 +228,11 @@ class ZtrackBoard {
|
|
|
228
228
|
for (const [id, value] of Object.entries(current)) {
|
|
229
229
|
const old = previous[id];
|
|
230
230
|
if (old?.hash === value.hash) continue;
|
|
231
|
+
let entry; try { entry = workflowFor(this.root); } catch {}
|
|
232
|
+
if (old && entry?.workflow.verbs?.document_changed) {
|
|
233
|
+
const change = fire(this.mem, this.board(), entry, id, 'document_changed', { roles: ['platform'], payload: { previous_status: old.status, by, source: 'document' } });
|
|
234
|
+
for (const effect of change.effects) this.mem.prepare('INSERT INTO supercode_effects (effect) VALUES (?)').run(JSON.stringify(effect));
|
|
235
|
+
}
|
|
231
236
|
const kind = !old ? 'created' : old.status !== value.status ? 'status' : old.assignee !== value.assignee ? 'assigned' : 'specified';
|
|
232
237
|
event(this.mem, id, kind, { by, source: 'document', status: value.status, assignee: value.assignee, author: by });
|
|
233
238
|
if (!old && value.parent) event(this.mem, value.parent, 'subtask_added', { by, subtask: id, title: this.mem.prepare('SELECT title FROM tasks WHERE id = ?').get(id).title });
|
|
@@ -318,6 +323,10 @@ class ZtrackBoard {
|
|
|
318
323
|
else if (row.kind === 'completed') { name = 'complete'; words.summary = payload.summary ?? task.result; }
|
|
319
324
|
else if (row.kind === 'blocked' || row.kind === 'dependency_wait') { name = 'block'; words.on = payload.on ?? payload.parent ?? null; words.hermes = true; }
|
|
320
325
|
else if (row.kind === 'archived' && statusOf(this.mem, task) !== 'done') name = 'cancel';
|
|
326
|
+
if (row.kind === 'unblocked' && entry.workflow.verbs?.hermes_unpause) {
|
|
327
|
+
const resumed = fire(this.mem, this.board(), entry, task.id, 'hermes_unpause', { roles, payload: { hermes: true, reason: 'unblocked in Hermes' } });
|
|
328
|
+
for (const effect of resumed.effects) this.mem.prepare('INSERT INTO supercode_effects (effect) VALUES (?)').run(JSON.stringify(effect));
|
|
329
|
+
}
|
|
321
330
|
if (name) {
|
|
322
331
|
const result = fire(this.mem, this.board(), entry, task.id, name, { roles, payload: words });
|
|
323
332
|
for (const effect of result.effects) this.mem.prepare('INSERT INTO supercode_effects (effect) VALUES (?)').run(JSON.stringify(effect));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-orchestrator",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.79",
|
|
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": {
|