@volter/supercode-orchestrator 0.4.96 → 0.4.98

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/rows.mjs CHANGED
@@ -55,6 +55,22 @@ export class RuntimeRows {
55
55
  if(name==='tasks')return {name:'@runtime',key:JSON.parse(key)[0],value:row&&Object.fromEntries(Object.entries(row).filter(([k])=>RUNTIME.has(k))),source:{name,key,row}};
56
56
  if(name==='supercode_cards')return {name:'@clock',key:JSON.parse(key)[0],value:row&&{since:row.since,after_fired:row.after_fired,next_run_at:row.next_run_at},source:{name,key,row}};
57
57
  return {name,key,value:row,source:{name,key,row}};
58
+ }).filter(change=>{
59
+ if(all)return true;
60
+ const {name,key,value,source}=change;
61
+ if(name==='task_links') {
62
+ const [parent,child]=JSON.parse(key);
63
+ const before=json(this.cachedCards[child]?.blocked_by,[]).some(b=>b.kind==='card'&&b.id===parent);
64
+ return before!==Boolean(source.row);
65
+ }
66
+ const before=this.db.prepare('SELECT value FROM runtime_rows WHERE name=? AND key=?').get(name,key)?.value??null;
67
+ if(before!==(value===null?null:JSON.stringify(value)))return true;
68
+ if(name==='@runtime') {
69
+ const fields=row=>row&&Object.fromEntries(Object.entries(row).filter(([k])=>k!=='status'));
70
+ return JSON.stringify(fields(source.row))!==JSON.stringify(fields(this.cachedTasks[key]??null));
71
+ }
72
+ if(name==='@clock')return JSON.stringify(source.row)!==JSON.stringify(this.cachedCards[key]??null);
73
+ return false;
58
74
  });
59
75
  }
60
76
  mirror(changes, revision) {
package/board/ztrack.mjs CHANGED
@@ -109,6 +109,7 @@ class ZtrackBoard {
109
109
 
110
110
  transaction(fn) {
111
111
  if (this.cachedReader) this.invalidate();
112
+ this.readOnly(()=>{ if(this.runtime.prepare('PRAGMA journal_mode').get().journal_mode!=='wal')this.runtime.exec('PRAGMA journal_mode=WAL'); });
112
113
  for (;;) {
113
114
  try { this.runtime.exec('BEGIN'); }
114
115
  catch (error) { if (!/busy|locked/i.test(error.message)) throw error; continue; }
@@ -126,19 +127,23 @@ class ZtrackBoard {
126
127
  const result = fn(this.db);
127
128
  const changes = this.rows.changes(this.mem, { all: !this.rows.normalized() });
128
129
  const canonical = (row, omitted) => row && Object.fromEntries(Object.entries(row).filter(([key])=>!omitted.has(key)));
129
- const cardDirty = this.seed || changes.some(({source})=>{
130
+ const documentChanges = changes.filter(({source})=>{
130
131
  if(source.name==='tasks')return JSON.stringify(canonical(source.row,new Set([...RUNTIME,'status'])))!==JSON.stringify(canonical(this.rows.cachedTasks[JSON.parse(source.key)[0]],new Set([...RUNTIME,'status'])));
131
132
  if(source.name==='supercode_cards')return JSON.stringify(canonical(source.row,new Set(['since','after_fired','next_run_at'])))!==JSON.stringify(canonical(this.rows.cachedCards[JSON.parse(source.key)[0]],new Set(['since','after_fired','next_run_at'])));
132
- return source.name==='task_links';
133
+ return false;
133
134
  });
134
- const documents = cardDirty ? this.documents() : {open:this.text, archived:this.archiveText};
135
+ const documents = this.seed || documentChanges.length ? this.documents(this.seed ? null : documentChanges) : {open:this.text, archived:this.archiveText};
135
136
  const files = [{ path: this.path, before: hash(this.text), base: this.text, text: documents.open }, { path: this.archive, before: hash(this.archiveText), base: this.archiveText, text: documents.archived }];
136
137
  const snapshot = this.snapshot(documents);
137
138
  this.computing = false;
139
+ const pendingValues=[...this.pendingValues].filter(([key,value])=>JSON.stringify(this.get(key))!==JSON.stringify(value));
140
+ if(!changes.length && !pendingValues.length && files.every(f=>hash(f.text)===f.before)) {
141
+ this.loadedStamp=this.stamp();return result;
142
+ }
138
143
  this.runtime.exec('BEGIN');
139
144
  this.recover();
140
145
  if (this.get('version') !== version || files.some((f) => hash(read(f.path)) !== f.before)) { this.runtime.exec('ROLLBACK'); this.invalidate(); continue; }
141
- for (const [key, value] of this.pendingValues) this.put(key, value);
146
+ for (const [key, value] of pendingValues) this.put(key, value);
142
147
  // A durable intent precedes filesystem writes; recovery completes exactly this version.
143
148
  const intent = randomBytes(16).toString('hex');
144
149
  this.put('pending', { intent, files, snapshot, changes });
@@ -284,12 +289,13 @@ class ZtrackBoard {
284
289
  this.rows.watch(this.mem);
285
290
  this.mem.exec('DELETE FROM tasks; DELETE FROM supercode_cards; DELETE FROM task_links');
286
291
  this.errors.clear(); this.preserve.clear();
287
- const records = [];
292
+ const records = []; this.documentRecords=new Map();
288
293
  for (const [text,path] of [[this.text,this.path],[this.archiveText,this.archive]]) {
289
294
  const result = this.codec.inspectBoardDocument(text,path);
290
295
  const kept = new Set(result.errors.map(e=>e.id));
291
296
  for(const error of result.errors)this.errors.set(nativeId(error.id),error.reason);
292
297
  for(const record of result.records) {
298
+ this.documentRecords.set(nativeId(record.id),{record,path});
293
299
  try { records.push(this.preset.KanbanRootSchema.parse(this.preset.parseKanban([record])).issues[0]); }
294
300
  catch(error) { this.errors.set(nativeId(record.id),error.message); kept.add(record.id); }
295
301
  }
@@ -389,10 +395,11 @@ class ZtrackBoard {
389
395
  committer() {
390
396
  return 'document-editor';
391
397
  }
392
- documents() {
393
- const rows = this.mem.prepare('SELECT * FROM tasks ORDER BY created_at, id').all();
398
+ documents(changes=null) {
399
+ const allRows = this.mem.prepare('SELECT * FROM tasks ORDER BY created_at, id').all();
394
400
  const states = new Map(this.mem.prepare('SELECT * FROM supercode_cards').all().map((c) => [c.task_id, c]));
395
- const byId = new Map(rows.map((r) => [r.id, r]));
401
+ const affected=changes?new Set(changes.map(({source})=>{const id=JSON.parse(source.key)[0];return states.get(id)?.subtask_of??this.rows.cachedCards[id]?.subtask_of??id;})):null;
402
+ const rows=affected?allRows.filter(row=>affected.has(states.get(row.id)?.subtask_of??row.id)):allRows;
396
403
  const cards = new Map();
397
404
  for (const row of rows.filter((r) => !states.get(r.id)?.subtask_of && !this.errors.has(r.id))) {
398
405
  const c = states.get(row.id) ?? {};
@@ -429,12 +436,15 @@ class ZtrackBoard {
429
436
  blockedBy: json(c.blocked_by, []).map((b) => ({ issue: b.id })) });
430
437
  }
431
438
  const open = [], archived = [];
439
+ if(affected)for(const [id,{record,path}] of this.documentRecords??[])if(!affected.has(id)&&!this.errors.has(id))(path===this.path?open:archived).push(record);
432
440
  for (const [id, card] of cards) {
433
441
  const serialized = this.preset.serializeKanbanCard(card);
434
442
  const record = { id: card.id, title: card.title, status: card.status, ...(card.assignee ? { assignee: card.assignee } : {}), body: serialized.body };
435
443
  (states.get(id)?.archived || ['done', 'cancelled'].includes(card.status) ? archived : open).push(record);
436
444
  }
437
- return { open: this.codec.renderBoardDocument(this.text, open, this.path, [...(this.preserve.get(this.path)??[])]), archived: this.codec.renderBoardDocument(this.archiveText, archived, this.archive, [...(this.preserve.get(this.archive)??[])]) };
445
+ const original=this.documentRecords;
446
+ const changedPath=path=>!affected||[...affected].some(id=>original?.get(id)?.path===path||cards.has(id)&&((states.get(id)?.archived||['done','cancelled'].includes(cards.get(id).status))?this.archive:this.path)===path);
447
+ return { open: !changedPath(this.path)?this.text:this.codec.renderBoardDocument(this.text, open, this.path, [...(this.preserve.get(this.path)??[])]), archived: !changedPath(this.archive)?this.archiveText:this.codec.renderBoardDocument(this.archiveText, archived, this.archive, [...(this.preserve.get(this.archive)??[])]) };
438
448
  }
439
449
  snapshot(documents) {
440
450
  return { documentHashes:{open:hash(documents.open),archived:hash(documents.archived)}, errors:[...this.errors], fingerprints: this.fingerprints(), taskIds: Object.fromEntries([...this.taskKeys].map(([id, key]) => [key, id])) };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/supercode-orchestrator",
3
- "version": "0.4.96",
3
+ "version": "0.4.98",
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": {