@volter/supercode-orchestrator 0.4.139 → 0.4.140

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
@@ -9,7 +9,7 @@ import { spawnSync } from 'node:child_process';
9
9
  import { basename, join, resolve } from 'node:path';
10
10
  import { tmpdir } from 'node:os';
11
11
  import { read } from '../orchestration.mjs';
12
- import { view, backingPaths, layOut, backingOf, boardExists, boardPath, DEFAULT_BOARD, event, json, listBoards, logsDir, now, openBoard, setActor, tx } from './store.mjs';
12
+ import { actorOf, view, backingPaths, layOut, backingOf, boardExists, boardPath, DEFAULT_BOARD, event, json, listBoards, logsDir, now, openBoard, setActor, tx } from './store.mjs';
13
13
  import { isLocalWorkspace } from './workspace.mjs';
14
14
  import { openZtrackBoard } from './ztrack.mjs';
15
15
  import * as engine from './engine.mjs';
@@ -1090,7 +1090,8 @@ function rolesFor(db, cardId, sessionId, params = {}) {
1090
1090
  const task = engine.getTask(db, cardId);
1091
1091
  if (!task) return [];
1092
1092
  const roles = [];
1093
- if (completionOwnerOf(db, cardId)?.endsWith(`:${sessionId}`)) roles.push('creator');
1093
+ const creator = completionOwnerOf(db, cardId);
1094
+ if (creator && creator === actorOf()) roles.push('creator');
1094
1095
  const current = task.current_run_id ? json(db.prepare('SELECT metadata FROM task_runs WHERE id = ?').get(task.current_run_id)?.metadata, {})?.supercode : null;
1095
1096
  if (current?.session_id === sessionId) roles.push(current.role ?? 'implementer');
1096
1097
  else if (engine.lastSession(db, cardId)?.session_id === sessionId) roles.push('implementer');
@@ -1111,7 +1112,8 @@ function authorize(db, verb, rest, args, own) {
1111
1112
  if (args.all) refuse(`does not ${verb} every card; only its own`);
1112
1113
  const card = engine.getTask(db, rest[0]);
1113
1114
  if (!card) return; // the verb itself answers "no card"
1114
- if (completionOwnerOf(db, card.id)?.endsWith(`:${callerSession()}`)) return; // its declared creator doors still govern the act
1115
+ const creator = completionOwnerOf(db, card.id);
1116
+ if (creator && creator === actorOf()) return;
1115
1117
  // a card's session writes its own card and its subtasks; what it may do with them is the workflow's (`by`)
1116
1118
  if (card.id !== own.id && card.subtask_of !== own.id) refuse(`writes only its own card and its subtasks, not ${card.id}`);
1117
1119
  for (const other of args.ids ?? []) if (other !== own.id && engine.getTask(db, other)?.subtask_of !== own.id) refuse(`writes only its own card and its subtasks, not ${other}`);
package/board/engine.mjs CHANGED
@@ -205,7 +205,7 @@ export function createCard(board, spec) {
205
205
  tenant, idempotency_key, max_runtime_seconds, skills, max_retries, model_override, provider_override, reasoning_effort, session_id,
206
206
  project_id, goal_mode, goal_max_turns)
207
207
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
208
- id, title, spec.body ?? null, spec.assignee || null, 'todo', Number(spec.priority) || 0, spec.createdBy ?? 'user', t, ws.kind, ws.path,
208
+ id, title, spec.body ?? null, spec.assignee || null, 'todo', Number(spec.priority) || 0, actorOf() ?? spec.createdBy ?? 'user', t, ws.kind, ws.path,
209
209
  spec.branch ?? null, spec.tenant ?? null, spec.idempotencyKey ?? null, spec.maxRuntime ?? null,
210
210
  spec.skills?.length ? JSON.stringify(spec.skills) : null, spec.maxRetries ?? null, spec.model ?? null, spec.provider ?? null,
211
211
  spec.reasoning ?? null, spec.sessionId ?? null, spec.project ?? null, spec.goal ? 1 : 0, spec.goal?.maxTurns ?? null);
@@ -66,20 +66,7 @@ export function creatorOf(db, id) {
66
66
  return payload.source !== 'document' && /^(?:sc:[^:]+:[^:]+:|hermes-run:)/.test(address ?? '') ? address : null;
67
67
  }
68
68
 
69
- /** Open subtasks are the current parent worker's plan. Keep creation history
70
- * immutable while transferring its creator doors when that worker is replaced. */
71
69
  export function completionOwnerOf(db, id) {
72
- const task = one(db, 'SELECT t.status,c.subtask_of FROM tasks t LEFT JOIN supercode_cards c ON c.task_id=t.id WHERE t.id=?', id);
73
- if (task?.subtask_of && !['done', 'archived'].includes(task.status)) {
74
- for (const row of all(db, 'SELECT metadata FROM task_runs WHERE task_id=? ORDER BY id DESC', task.subtask_of)) {
75
- const session = json(row.metadata, {})?.supercode;
76
- if (!session || (session.slot ?? 'worker') !== 'worker') continue;
77
- // An unbound replacement leaves authority with the manager; it never
78
- // revives the previous worker's ability to finish the new worker's plan.
79
- return session.address ?? (session.machine && session.harness && session.session_id
80
- ? `sc:${session.machine}:${session.harness}:${session.session_id}` : null);
81
- }
82
- }
83
70
  return creatorOf(db, id);
84
71
  }
85
72
 
@@ -377,7 +364,12 @@ export function fire(db, board, entry, id, name, { roles = [], payload = {}, ext
377
364
  // `sender`: the roles the event was sent with (a validator can ask who is asking)
378
365
  const scope = scopeOf(db, board, task, { params, event: payload, extra: { lanes: lanesIn(entry), ...extra, sender: roles } });
379
366
  const allowed = candidates.filter((t) => !t.by?.length || t.by.some((r) => roles.includes(r)));
380
- if (!allowed.length) throw new Error(`${id} is ${from}; ${name.replace(/_/g, '-')} there is for ${[...new Set(candidates.flatMap((t) => t.by ?? []))].join(' or ')}. ${candidates.map((t) => t.description).filter(Boolean).join(' ')}`);
367
+ if (!allowed.length) {
368
+ const state = name === 'complete' ? 'done' : name === 'cancel' ? 'cancelled' : null;
369
+ const request = state && candidates.some(transition => transition.by?.includes('creator'))
370
+ ? ` Ask the manager with workflow request-state ${id} --state ${state} --reason <why> --evidence <verdict-and-pointers>.` : '';
371
+ throw new Error(`${id} is ${from}; ${name.replace(/_/g, '-')} there is for ${[...new Set(candidates.flatMap((t) => t.by ?? []))].join(' or ')}. ${candidates.map((t) => t.description).filter(Boolean).join(' ')}${request}`);
372
+ }
381
373
  const taken = allowed.find((t) => holds(t.guard ?? null, scope));
382
374
  if (!taken) {
383
375
  const state = scope.card;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/supercode-orchestrator",
3
- "version": "0.4.139",
3
+ "version": "0.4.140",
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": {