@volter/supercode-orchestrator 0.4.95 → 0.4.96

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
@@ -236,7 +236,8 @@ export async function runWorkflow(argv) {
236
236
  const ids = (first = rest) => [...first, ...(args.ids ?? [])];
237
237
 
238
238
  // a caller that is a card's own session (its harness names it; the board recorded which session it launched)
239
- const sid = callerSession();
239
+ const listing = ['list','ls'].includes(verb);
240
+ const sid = listing && !args.mine ? null : callerSession();
240
241
  // what this command records is the calling session's act (its address, as its subscriptions name it)
241
242
  if (sid) setActor(`sc:${localMachine()}:${sid === process.env.CODEX_THREAD_ID ? 'codex' : 'claude-code'}:${sid}`);
242
243
  if (sid && !READS.has(verb) && boardExists(root, slug)) {
@@ -244,7 +245,7 @@ export async function runWorkflow(argv) {
244
245
  }
245
246
  // the board's workflow (the IR's): what each verb does, and who may send it, is its
246
247
  // every verb reads the board through its workflow (a list settles cards too), so every verb loads it
247
- if (verb !== 'help') await loadWorkflow(root).catch((error) => { if (verb !== 'init' && verb !== 'boards') throw error; });
248
+ if (verb !== 'help' && !listing) await loadWorkflow(root).catch((error) => { if (verb !== 'init' && verb !== 'boards') throw error; });
248
249
  engine.setCaller((db, cardId) => rolesFor(db, cardId, sid, workflowFor(root).params));
249
250
  // a verb with no session behind it (a person at the CLI, the board file's sync) is the manager's, as its roles are
250
251
  if (!sid && verb !== 'help') { try { setActor([].concat(workflowFor(root).params?.managers ?? [])[0] ?? null); } catch { /* no workflow */ } }
package/board/rows.mjs CHANGED
@@ -57,6 +57,21 @@ export class RuntimeRows {
57
57
  return {name,key,value:row,source:{name,key,row}};
58
58
  });
59
59
  }
60
+ mirror(changes, revision) {
61
+ const previous=Number(this.db.prepare("SELECT value FROM state WHERE key='read_revision'").get()?.value??-1);
62
+ for(const name of ['tasks','supercode_cards']) {
63
+ const table=this.tables.get(name); if(!table)return;
64
+ this.db.exec(table.sql.replace(/^CREATE TABLE (?:IF NOT EXISTS )?(?:"[^"]+"|\w+)/,`CREATE TABLE IF NOT EXISTS ${quote('read_'+name)}`));
65
+ }
66
+ const rows=previous===revision-1?changes.filter(c=>['tasks','supercode_cards'].includes(c.source.name)).map(c=>c.source):this.db.prepare("SELECT name,key,value FROM runtime_rows WHERE name IN ('@task_cache','@card_cache')").all().map(r=>({name:r.name==='@task_cache'?'tasks':'supercode_cards',key:JSON.stringify([r.key]),row:json(r.value,null)}));
67
+ for(const {name,key,row} of rows) {
68
+ const table=this.tables.get(name), target=quote('read_'+name);
69
+ if(!row) {this.db.prepare(`DELETE FROM ${target} WHERE ${table.keys.map(k=>`${quote(k)} IS ?`).join(' AND ')}`).run(...JSON.parse(key));continue;}
70
+ const keys=Object.keys(row);
71
+ this.db.prepare(`INSERT OR REPLACE INTO ${target} (${keys.map(quote)}) VALUES (${keys.map(()=>'?')})`).run(...keys.map(k=>row[k]??null));
72
+ }
73
+ this.db.prepare("INSERT INTO state VALUES('read_revision',?) ON CONFLICT(key) DO UPDATE SET value=excluded.value").run(String(revision));
74
+ }
60
75
  write(changes) {
61
76
  this.db.exec(`CREATE TABLE IF NOT EXISTS runtime_schema(name TEXT PRIMARY KEY,sql TEXT NOT NULL); CREATE TABLE IF NOT EXISTS runtime_rows(name TEXT,key TEXT,value TEXT,revision INTEGER NOT NULL,PRIMARY KEY(name,key)); CREATE INDEX IF NOT EXISTS runtime_rows_revision ON runtime_rows(revision); CREATE INDEX IF NOT EXISTS runtime_rows_task ON runtime_rows(name,json_extract(value,'$.task_id'),json_extract(value,'$.id')); CREATE INDEX IF NOT EXISTS runtime_rows_id ON runtime_rows(name,json_extract(value,'$.id'))`);
62
77
  const revision=Number(this.db.prepare("SELECT value FROM state WHERE key='rows_revision'").get()?.value??0)+1;
@@ -77,6 +92,7 @@ export class RuntimeRows {
77
92
  }
78
93
  for(const {name,sql} of this.tables.values())if(!OMIT.has(name))this.db.prepare('INSERT OR IGNORE INTO runtime_schema VALUES(?,?)').run(name,sql);
79
94
  this.db.prepare("INSERT INTO state VALUES('rows_revision',?) ON CONFLICT(key) DO UPDATE SET value=excluded.value").run(String(revision));
95
+ this.mirror(changes,revision);
80
96
  this.revision=revision;
81
97
  }
82
98
  }
@@ -113,7 +113,7 @@ function lastCommitAt(path, id) { return path ? commitTimes.get(`${path}:${id}`)
113
113
  export function refreshCardInputs(board, id, { commits = false } = {}) {
114
114
  const task=board.db.prepare('SELECT t.*,c.subtask_of FROM tasks t LEFT JOIN supercode_cards c ON c.task_id=t.id WHERE t.id=?').get(id);
115
115
  if (!task)return;
116
- if (commits) {
116
+ if (commits && !task.subtask_of) {
117
117
  const own=task.workspace_kind!=='scratch'?task.workspace_path:null;
118
118
  const parent=!own && task.subtask_of?board.db.prepare('SELECT workspace_path FROM tasks WHERE id=?').get(task.subtask_of)?.workspace_path:null;
119
119
  const path=own??parent??null;
package/board/ztrack.mjs CHANGED
@@ -219,12 +219,21 @@ class ZtrackBoard {
219
219
  this.mem.prepare(`INSERT OR REPLACE INTO ${quote(table)} (${keys.map(quote).join(',')}) VALUES (${keys.map(() => '?').join(',')})`).run(...keys.map((k) => row[k] ?? null));
220
220
  }
221
221
  openCachedReader(saved) {
222
+ const native = this.get('read_revision',-1)===this.get('rows_revision',0);
223
+ if(this.cachedReader && native!==this.nativeReader)this.invalidate();
222
224
  if (!this.cachedReader) {
223
225
  this.mem?.close(); this.mem=new DatabaseSync(':memory:'); this.init(this.mem,{fresh:true});
224
226
  for(const row of this.runtime.prepare('SELECT name,sql FROM runtime_schema').all())this.mem.exec(row.sql.replace('CREATE TABLE ','CREATE TABLE IF NOT EXISTS '));
225
227
  this.mem.prepare('ATTACH DATABASE ? AS source').run(this.runtimePath);
226
228
  const tables=this.mem.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'").all();
227
229
  for(const {name} of tables) {
230
+ if (native && name==='tasks') {
231
+ const fields=this.mem.prepare('PRAGMA main.table_info(tasks)').all().map(({name:column})=>column==='status'?'COALESCE(c.stage,t.status) AS status':`t.${quote(column)}`);
232
+ this.mem.exec(`CREATE TEMP VIEW tasks AS SELECT ${fields.join(',')} FROM source.read_tasks t LEFT JOIN source.read_supercode_cards c ON c.task_id=t.id`);
233
+ continue;
234
+ }
235
+ if(native && name==='supercode_cards') {this.mem.exec('CREATE TEMP VIEW supercode_cards AS SELECT * FROM source.read_supercode_cards');continue;}
236
+ if(native && name==='task_links') {this.mem.exec("CREATE TEMP VIEW task_links AS SELECT json_extract(b.value,'$.id') parent_id,r.task_id child_id FROM source.read_supercode_cards r,json_each(r.blocked_by) b WHERE json_extract(b.value,'$.kind')='card'");continue;}
228
237
  if(name==='task_links') {
229
238
  this.mem.exec(`CREATE TEMP VIEW task_links AS SELECT json_extract(b.value,'$.id') parent_id,r.key child_id FROM source.runtime_rows r,json_each(json_extract(r.value,'$.blocked_by')) b WHERE r.name='@card_cache' AND json_extract(b.value,'$.kind')='card'`);
230
239
  continue;
@@ -236,7 +245,7 @@ class ZtrackBoard {
236
245
  this.mem.exec(`CREATE TEMP VIEW ${quote(name)} AS SELECT ${fields.join(',')} FROM source.runtime_rows r${join} WHERE r.name='${recordName}' AND r.value IS NOT NULL`);
237
246
  }
238
247
  this.mem.exec('PRAGMA query_only=ON');
239
- this.cachedReader=true;
248
+ this.cachedReader=true;this.nativeReader=native;
240
249
  }
241
250
  this.saved=saved; this.errors.clear();
242
251
  for(const [id,reason] of saved.errors??[])this.errors.set(id,reason);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/supercode-orchestrator",
3
- "version": "0.4.95",
3
+ "version": "0.4.96",
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": {