@volter/supercode-orchestrator 0.4.92 → 0.4.94
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 +1 -1
- package/board/workflow.mjs +3 -1
- package/board/ztrack.mjs +41 -9
- package/package.json +1 -1
package/board/rows.mjs
CHANGED
|
@@ -58,7 +58,7 @@ export class RuntimeRows {
|
|
|
58
58
|
});
|
|
59
59
|
}
|
|
60
60
|
write(changes) {
|
|
61
|
-
this.db.exec(
|
|
61
|
+
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
62
|
const revision=Number(this.db.prepare("SELECT value FROM state WHERE key='rows_revision'").get()?.value??0)+1;
|
|
63
63
|
const statement=this.db.prepare('INSERT INTO runtime_rows VALUES(?,?,?,?) ON CONFLICT(name,key) DO UPDATE SET value=excluded.value,revision=excluded.revision');
|
|
64
64
|
for(const {name,key,value,source} of changes) {
|
package/board/workflow.mjs
CHANGED
|
@@ -116,7 +116,9 @@ export function refreshCardInputs(board, id, { commits = false } = {}) {
|
|
|
116
116
|
if (commits) {
|
|
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;
|
|
120
|
+
if(path)commitTimes.delete(`${path}:${id}`);
|
|
121
|
+
observeLastCommit(path,id);
|
|
120
122
|
}
|
|
121
123
|
}
|
|
122
124
|
|
package/board/ztrack.mjs
CHANGED
|
@@ -36,10 +36,9 @@ class ZtrackBoard {
|
|
|
36
36
|
this.archive = `${this.path}.archive.md`;
|
|
37
37
|
this.seed = seed;
|
|
38
38
|
this.root = root; this.slug = slug; this.init = init; this.compatPath = compatPath;
|
|
39
|
-
this.
|
|
40
|
-
this.
|
|
41
|
-
this.runtime =
|
|
42
|
-
if (!this.runtime.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='state'").get()) this.runtime.exec('CREATE TABLE state (key TEXT PRIMARY KEY, value TEXT NOT NULL)');
|
|
39
|
+
this.runtimePath = `${marker}.runtime.db`;
|
|
40
|
+
this.runtime = new DatabaseSync(this.runtimePath);
|
|
41
|
+
this.readOnly(()=>{ if (!this.runtime.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='state'").get()) this.runtime.exec('CREATE TABLE IF NOT EXISTS state (key TEXT PRIMARY KEY, value TEXT NOT NULL)'); });
|
|
43
42
|
this.errors = new Map(); this.preserve = new Map();
|
|
44
43
|
this.mem = null; this.active = false; this.rows = new RuntimeRows(this.runtime);
|
|
45
44
|
mkdirSync(dirname(this.path), { recursive: true });
|
|
@@ -53,6 +52,8 @@ class ZtrackBoard {
|
|
|
53
52
|
if (seed) this.tx(() => {});
|
|
54
53
|
else this.readOnly(()=>{ this.load(); this.loadedStamp = this.stamp(); });
|
|
55
54
|
}
|
|
55
|
+
get codec() { return this._codec ??= require('@volter/ztrack/board-document'); }
|
|
56
|
+
get preset() { return this._preset ??= require('@volter/ztrack/kanban-preset'); }
|
|
56
57
|
get(key, fallback = null) { return this.computing && this.pendingValues.has(key) ? this.pendingValues.get(key) : json(this.runtime.prepare('SELECT value FROM state WHERE key = ?').get(key)?.value, fallback); }
|
|
57
58
|
put(key, value) { if (this.computing) { this.pendingValues.set(key, value); return; } this.runtime.prepare('INSERT INTO state VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value').run(key, JSON.stringify(value)); }
|
|
58
59
|
stamp() {
|
|
@@ -107,6 +108,7 @@ class ZtrackBoard {
|
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
transaction(fn) {
|
|
111
|
+
if (this.cachedReader) this.invalidate();
|
|
110
112
|
for (;;) {
|
|
111
113
|
try { this.runtime.exec('BEGIN'); }
|
|
112
114
|
catch (error) { if (!/busy|locked/i.test(error.message)) throw error; continue; }
|
|
@@ -131,7 +133,7 @@ class ZtrackBoard {
|
|
|
131
133
|
});
|
|
132
134
|
const documents = cardDirty ? this.documents() : {open:this.text, archived:this.archiveText};
|
|
133
135
|
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 }];
|
|
134
|
-
const snapshot = this.snapshot();
|
|
136
|
+
const snapshot = this.snapshot(documents);
|
|
135
137
|
this.computing = false;
|
|
136
138
|
this.runtime.exec('BEGIN');
|
|
137
139
|
this.recover();
|
|
@@ -169,12 +171,12 @@ class ZtrackBoard {
|
|
|
169
171
|
try { this.runtime.exec('ROLLBACK'); } catch { /* intent already durable */ }
|
|
170
172
|
this.invalidate();
|
|
171
173
|
if (/busy|locked|Hermes wrote|document changed during recovery/i.test(error.message)) continue;
|
|
172
|
-
if (committed) error.message += ' (the
|
|
174
|
+
if (committed) error.message += ' (the operation reached its durable intent; inspect its recorded outcome before retrying)';
|
|
173
175
|
throw error;
|
|
174
176
|
} finally { this.active = false; this.computing = false; this.pendingValues = null; }
|
|
175
177
|
}
|
|
176
178
|
}
|
|
177
|
-
invalidate() { this.mem?.close(); this.mem = null; this.rows = new RuntimeRows(this.runtime); this.loadedStamp = null; }
|
|
179
|
+
invalidate() { this.mem?.close(); this.mem = null; this.rows = new RuntimeRows(this.runtime); this.loadedStamp = null; this.cachedReader=false; }
|
|
178
180
|
seedIntoMemory() {
|
|
179
181
|
const source = this.seed;
|
|
180
182
|
for (const table of this.mem.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'").all()) this.mem.exec(`DELETE FROM ${quote(table.name)}`);
|
|
@@ -216,8 +218,38 @@ class ZtrackBoard {
|
|
|
216
218
|
if (!keys.length) return;
|
|
217
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));
|
|
218
220
|
}
|
|
221
|
+
openCachedReader(saved) {
|
|
222
|
+
if (!this.cachedReader) {
|
|
223
|
+
this.mem?.close(); this.mem=new DatabaseSync(':memory:'); this.init(this.mem,{fresh:true});
|
|
224
|
+
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
|
+
this.mem.prepare('ATTACH DATABASE ? AS source').run(`file:${this.runtimePath}?mode=ro`);
|
|
226
|
+
const tables=this.mem.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'").all();
|
|
227
|
+
for(const {name} of tables) {
|
|
228
|
+
if(name==='task_links') {
|
|
229
|
+
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
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
const columns=this.mem.prepare(`PRAGMA main.table_info(${quote(name)})`).all();
|
|
233
|
+
const recordName=name==='tasks'?'@task_cache':name==='supercode_cards'?'@card_cache':name;
|
|
234
|
+
const fields=columns.map(({name:column})=>name==='tasks'&&column==='status'?`COALESCE(json_extract(c.value,'$.stage'),json_extract(r.value,'$.status')) AS status`:`json_extract(r.value,'$.${column}') AS ${quote(column)}`);
|
|
235
|
+
const join=name==='tasks'?" LEFT JOIN source.runtime_rows c ON c.name='@card_cache' AND c.key=r.key":'';
|
|
236
|
+
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
|
+
}
|
|
238
|
+
this.cachedReader=true;
|
|
239
|
+
}
|
|
240
|
+
this.saved=saved; this.errors.clear();
|
|
241
|
+
for(const [id,reason] of saved.errors??[])this.errors.set(id,reason);
|
|
242
|
+
this.mem.boardErrors=this.errors;
|
|
243
|
+
}
|
|
219
244
|
load({ observe = false } = {}) {
|
|
220
245
|
const text = read(this.path), archiveText = read(this.archive);
|
|
246
|
+
const saved=this.get('snapshot', {});
|
|
247
|
+
if (!observe && saved.documentHashes?.open===hash(text) && saved.documentHashes?.archived===hash(archiveText) && this.rows.normalized()) {
|
|
248
|
+
this.openCachedReader(saved);
|
|
249
|
+
this.text=text;this.archiveText=archiveText;
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (this.cachedReader)this.invalidate();
|
|
221
253
|
const fresh = !this.mem;
|
|
222
254
|
const changed = fresh || text !== this.text || archiveText !== this.archiveText;
|
|
223
255
|
this.text = text; this.archiveText = archiveText;
|
|
@@ -394,8 +426,8 @@ class ZtrackBoard {
|
|
|
394
426
|
}
|
|
395
427
|
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)??[])]) };
|
|
396
428
|
}
|
|
397
|
-
snapshot() {
|
|
398
|
-
return { fingerprints: this.fingerprints(), taskIds: Object.fromEntries([...this.taskKeys].map(([id, key]) => [key, id])) };
|
|
429
|
+
snapshot(documents) {
|
|
430
|
+
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])) };
|
|
399
431
|
}
|
|
400
432
|
|
|
401
433
|
lift() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-orchestrator",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.94",
|
|
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": {
|