@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 +16 -0
- package/board/ztrack.mjs +19 -9
- package/package.json +1 -1
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
|
|
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
|
|
133
|
+
return false;
|
|
133
134
|
});
|
|
134
|
-
const documents =
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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.
|
|
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": {
|