patchwork-os 1.2.0-beta.2.canary.603 → 1.2.0-beta.2.canary.604

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/README.md CHANGED
@@ -62,7 +62,7 @@ patchwork-os init
62
62
 
63
63
  `init` scaffolds `~/.patchwork`, seeds local-only recipes, and registers Patchwork's PreToolUse hook in `~/.claude/settings.json`. Restart Claude Code afterwards — it reads hooks at session start.
64
64
 
65
- **Prereqs:** Node 22+. macOS, Linux, and native Windows (no WSL).
65
+ **Prereqs:** Node 22.5+. macOS, Linux, and native Windows (no WSL).
66
66
 
67
67
  Two things worth knowing before you start:
68
68
 
@@ -0,0 +1,92 @@
1
+ /**
2
+ * The SQLite run store — ADR-0022.
3
+ *
4
+ * Held to the SAME contract as the JSONL incumbent
5
+ * (`describeRunRepositoryContract`), which is the entire point: the migration's
6
+ * safety argument is "this behaves like the store we already trust", and that
7
+ * is only checkable if one set of assertions covers both.
8
+ *
9
+ * ## Why SQLite at all
10
+ *
11
+ * `runs.jsonl` is the autonomy gate's trust evidence and an append-only text
12
+ * file with no integrity properties. It failed three ways in eight weeks, each
13
+ * silently — #1324 (`seq` collides across processes), #1340 (in-flight steps
14
+ * never written), #1341 (a concurrent reader made `completeRun` a no-op). The
15
+ * fixes grew a file lock, byte AND line caps, an archive tier and an upsert
16
+ * reconciliation path into `runLog.ts`: a database, reimplemented, losing.
17
+ * `runLog.ts:753` documents a lost write we chose to live with.
18
+ *
19
+ * ## Identity
20
+ *
21
+ * `task_id` is the PRIMARY KEY, not `seq`. That is the #1324 fix landing as a
22
+ * schema constraint rather than a convention: `seq` is a PER-INSTANCE counter
23
+ * handed out by eight construction sites, so 142 of 145 seqs in the live log
24
+ * were shared by unrelated runs. Here the database refuses to store two runs
25
+ * under one identity, instead of a reader silently discarding one.
26
+ *
27
+ * `seq` is still carried, still per-instance, and `getBySeq` is still
28
+ * ambiguous — preserved deliberately, because a migration that also changes the
29
+ * domain model cannot be verified by comparing old against new. #1360 fixes
30
+ * that separately, once the stores agree.
31
+ *
32
+ * ## What is NOT here
33
+ *
34
+ * No byte cap, no line cap, no rotation, no archive tier. Retention becomes a
35
+ * policy decision against a queryable store rather than a byte budget fighting
36
+ * a 24-hour durability window — which is the collision that starved the trust
37
+ * ledger. Nothing calls this yet; dual-write and shadow-read come next.
38
+ */
39
+ import type { Logger } from "../logger.js";
40
+ import type { RecipeRun, RunQuery, RunStepResult } from "../runLog.js";
41
+ import type { CompleteRunInput, RunRepository, StartRunInput } from "./runRepository.js";
42
+ export interface SqliteRunStoreOptions {
43
+ /** Directory holding runs.db. Created if missing. */
44
+ dir: string;
45
+ logger?: Logger;
46
+ /** Test hook — default Date.now. */
47
+ now?: () => number;
48
+ /** Test seam for liveness. Default: real process check. */
49
+ isAlive?: (pid: number | undefined) => boolean;
50
+ }
51
+ export declare class SqliteRunRepository implements RunRepository {
52
+ private readonly opts;
53
+ private readonly db;
54
+ private readonly now;
55
+ private readonly isAlive;
56
+ private seq;
57
+ private closed;
58
+ constructor(opts: SqliteRunStoreOptions);
59
+ /**
60
+ * Release the database handle. Idempotent, and never throws.
61
+ *
62
+ * Callers close defensively (teardown paths, error paths, a shutdown that
63
+ * may already have run), and a store whose cleanup can itself fail turns
64
+ * "we tidied up" into a new failure mode. On Windows this is not cosmetic:
65
+ * an open handle prevents the file being unlinked at all.
66
+ */
67
+ close(): void;
68
+ private maxSeq;
69
+ /**
70
+ * Flip runs whose owning process is provably gone to `interrupted`, folding
71
+ * in the evidence they managed to persist.
72
+ *
73
+ * ONLY when the owner is gone. Running this unconditionally is exactly
74
+ * #1341: a concurrent reader declared a live sibling's runs dead, and because
75
+ * `completeRun` no-ops on a non-running row, the real completion was never
76
+ * written — a successful run recorded `interrupted, steps: 0`.
77
+ */
78
+ private sweepInterrupted;
79
+ private stepsFor;
80
+ private rowToRun;
81
+ startRun(input: StartRunInput): number;
82
+ /** Resolve a per-instance `seq` to a task id. Ambiguous by construction — see
83
+ * the class docs and #1360. Newest wins, which is the least surprising of
84
+ * several wrong answers. */
85
+ private taskIdForSeq;
86
+ updateRunSteps(seq: number, stepResults: RunStepResult[]): void;
87
+ completeRun(seq: number, input: CompleteRunInput): void;
88
+ query(q?: RunQuery): RecipeRun[];
89
+ getBySeq(seq: number): RecipeRun | null;
90
+ getChildSeqs(parentSeq: number): number[];
91
+ size(): number;
92
+ }
@@ -0,0 +1,350 @@
1
+ /**
2
+ * The SQLite run store — ADR-0022.
3
+ *
4
+ * Held to the SAME contract as the JSONL incumbent
5
+ * (`describeRunRepositoryContract`), which is the entire point: the migration's
6
+ * safety argument is "this behaves like the store we already trust", and that
7
+ * is only checkable if one set of assertions covers both.
8
+ *
9
+ * ## Why SQLite at all
10
+ *
11
+ * `runs.jsonl` is the autonomy gate's trust evidence and an append-only text
12
+ * file with no integrity properties. It failed three ways in eight weeks, each
13
+ * silently — #1324 (`seq` collides across processes), #1340 (in-flight steps
14
+ * never written), #1341 (a concurrent reader made `completeRun` a no-op). The
15
+ * fixes grew a file lock, byte AND line caps, an archive tier and an upsert
16
+ * reconciliation path into `runLog.ts`: a database, reimplemented, losing.
17
+ * `runLog.ts:753` documents a lost write we chose to live with.
18
+ *
19
+ * ## Identity
20
+ *
21
+ * `task_id` is the PRIMARY KEY, not `seq`. That is the #1324 fix landing as a
22
+ * schema constraint rather than a convention: `seq` is a PER-INSTANCE counter
23
+ * handed out by eight construction sites, so 142 of 145 seqs in the live log
24
+ * were shared by unrelated runs. Here the database refuses to store two runs
25
+ * under one identity, instead of a reader silently discarding one.
26
+ *
27
+ * `seq` is still carried, still per-instance, and `getBySeq` is still
28
+ * ambiguous — preserved deliberately, because a migration that also changes the
29
+ * domain model cannot be verified by comparing old against new. #1360 fixes
30
+ * that separately, once the stores agree.
31
+ *
32
+ * ## What is NOT here
33
+ *
34
+ * No byte cap, no line cap, no rotation, no archive tier. Retention becomes a
35
+ * policy decision against a queryable store rather than a byte budget fighting
36
+ * a 24-hour durability window — which is the collision that starved the trust
37
+ * ledger. Nothing calls this yet; dual-write and shadow-read come next.
38
+ */
39
+ import { mkdirSync } from "node:fs";
40
+ import path from "node:path";
41
+ import { DatabaseSync } from "node:sqlite";
42
+ import { isEvidenceBearing } from "../runStepLedger.js";
43
+ /**
44
+ * Is the process that owns a running row still alive?
45
+ *
46
+ * Byte-for-byte the same rule as `runLog.ts`, including its judgement calls:
47
+ * `EPERM` counts as alive (the process exists, it just belongs to someone
48
+ * else), and an ABSENT pid returns false so rows predating `ownerPid` keep
49
+ * being recovered. "We don't know" is not evidence of a live owner.
50
+ *
51
+ * Divergence here would be invisible and would desynchronise the two stores
52
+ * during dual-write in the one area — sweeping live runs — that #1341 proves
53
+ * is dangerous.
54
+ */
55
+ function defaultIsAlive(pid) {
56
+ if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0) {
57
+ return false;
58
+ }
59
+ try {
60
+ process.kill(pid, 0);
61
+ return true;
62
+ }
63
+ catch (err) {
64
+ return err?.code === "EPERM";
65
+ }
66
+ }
67
+ const SCHEMA = `
68
+ CREATE TABLE IF NOT EXISTS runs (
69
+ task_id TEXT PRIMARY KEY,
70
+ seq INTEGER NOT NULL,
71
+ recipe_name TEXT NOT NULL,
72
+ trigger TEXT NOT NULL,
73
+ status TEXT NOT NULL,
74
+ created_at INTEGER NOT NULL,
75
+ started_at INTEGER,
76
+ done_at INTEGER,
77
+ duration_ms INTEGER,
78
+ model TEXT,
79
+ output_tail TEXT,
80
+ error_message TEXT,
81
+ parent_seq INTEGER,
82
+ manual_run_id TEXT,
83
+ owner_pid INTEGER,
84
+ had_step_errors INTEGER,
85
+ step_results TEXT,
86
+ extra TEXT
87
+ );
88
+ CREATE INDEX IF NOT EXISTS runs_created_at ON runs(created_at DESC);
89
+ CREATE INDEX IF NOT EXISTS runs_seq ON runs(seq);
90
+ CREATE INDEX IF NOT EXISTS runs_parent ON runs(parent_seq);
91
+ CREATE INDEX IF NOT EXISTS runs_recipe ON runs(recipe_name);
92
+
93
+ -- In-flight evidence. Separate table for the same reason #1340 used a separate
94
+ -- FILE: these rows arrive per step, and mixing them into the run record made
95
+ -- durability compete with retention.
96
+ CREATE TABLE IF NOT EXISTS run_steps (
97
+ task_id TEXT NOT NULL,
98
+ step_id TEXT NOT NULL,
99
+ at INTEGER NOT NULL,
100
+ step TEXT NOT NULL,
101
+ PRIMARY KEY (task_id, step_id)
102
+ );
103
+ `;
104
+ /** Columns carried verbatim as JSON rather than promoted to columns. Nothing
105
+ * queries them; giving each a column would be schema churn for no reader. */
106
+ const EXTRA_KEYS = [
107
+ "assertionFailures",
108
+ "inboxOutputs",
109
+ "budgetWarnings",
110
+ "tokenTotals",
111
+ "budgetTotals",
112
+ ];
113
+ export class SqliteRunRepository {
114
+ opts;
115
+ db;
116
+ now;
117
+ isAlive;
118
+ seq = 0;
119
+ closed = false;
120
+ constructor(opts) {
121
+ this.opts = opts;
122
+ this.now = opts.now ?? Date.now;
123
+ this.isAlive = opts.isAlive ?? defaultIsAlive;
124
+ mkdirSync(opts.dir, { recursive: true, mode: 0o700 });
125
+ this.db = new DatabaseSync(path.join(opts.dir, "runs.db"));
126
+ // WAL: a reader must not block the writer, and vice versa. The whole
127
+ // reason this store exists is that eight construction sites touch one
128
+ // ledger concurrently.
129
+ this.db.exec("PRAGMA journal_mode=WAL");
130
+ this.db.exec("PRAGMA foreign_keys=ON");
131
+ this.db.exec(SCHEMA);
132
+ this.seq = this.maxSeq();
133
+ this.sweepInterrupted();
134
+ }
135
+ /**
136
+ * Release the database handle. Idempotent, and never throws.
137
+ *
138
+ * Callers close defensively (teardown paths, error paths, a shutdown that
139
+ * may already have run), and a store whose cleanup can itself fail turns
140
+ * "we tidied up" into a new failure mode. On Windows this is not cosmetic:
141
+ * an open handle prevents the file being unlinked at all.
142
+ */
143
+ close() {
144
+ if (this.closed)
145
+ return;
146
+ this.closed = true;
147
+ try {
148
+ this.db.close();
149
+ }
150
+ catch (err) {
151
+ this.opts.logger?.warn?.(`[sqlite-runstore] close failed: ${err instanceof Error ? err.message : String(err)}`);
152
+ }
153
+ }
154
+ maxSeq() {
155
+ const r = this.db.prepare("SELECT MAX(seq) AS m FROM runs").get();
156
+ return typeof r?.m === "number" ? r.m : 0;
157
+ }
158
+ /**
159
+ * Flip runs whose owning process is provably gone to `interrupted`, folding
160
+ * in the evidence they managed to persist.
161
+ *
162
+ * ONLY when the owner is gone. Running this unconditionally is exactly
163
+ * #1341: a concurrent reader declared a live sibling's runs dead, and because
164
+ * `completeRun` no-ops on a non-running row, the real completion was never
165
+ * written — a successful run recorded `interrupted, steps: 0`.
166
+ */
167
+ sweepInterrupted() {
168
+ const rows = this.db
169
+ .prepare("SELECT task_id, owner_pid, created_at FROM runs WHERE status='running'")
170
+ .all();
171
+ const now = this.now();
172
+ const flip = this.db.prepare("UPDATE runs SET status='interrupted', done_at=?, duration_ms=?, step_results=? WHERE task_id=?");
173
+ for (const r of rows) {
174
+ const pid = typeof r.owner_pid === "number" ? r.owner_pid : undefined;
175
+ if (this.isAlive(pid))
176
+ continue;
177
+ const taskId = String(r.task_id);
178
+ const createdAt = Number(r.created_at);
179
+ const recovered = this.stepsFor(taskId);
180
+ flip.run(now, now - createdAt, recovered.length > 0 ? JSON.stringify(recovered) : null, taskId);
181
+ }
182
+ }
183
+ stepsFor(taskId) {
184
+ const rows = this.db
185
+ .prepare("SELECT step FROM run_steps WHERE task_id=? ORDER BY at ASC, rowid ASC")
186
+ .all(taskId);
187
+ const out = [];
188
+ for (const r of rows) {
189
+ try {
190
+ out.push(JSON.parse(String(r.step)));
191
+ }
192
+ catch {
193
+ // A single unparseable row must not destroy the rest of a run's
194
+ // evidence — the failure mode this whole subsystem exists to prevent.
195
+ this.opts.logger?.warn?.(`[sqlite-runstore] skipping unparseable step row for ${taskId}`);
196
+ }
197
+ }
198
+ return out;
199
+ }
200
+ rowToRun(r) {
201
+ const extra = r.extra ? JSON.parse(String(r.extra)) : {};
202
+ const steps = r.step_results
203
+ ? JSON.parse(String(r.step_results))
204
+ : undefined;
205
+ const run = {
206
+ seq: Number(r.seq),
207
+ taskId: String(r.task_id),
208
+ recipeName: String(r.recipe_name),
209
+ trigger: String(r.trigger),
210
+ status: String(r.status),
211
+ createdAt: Number(r.created_at),
212
+ doneAt: Number(r.done_at ?? 0),
213
+ durationMs: Number(r.duration_ms ?? 0),
214
+ ...(r.started_at != null && { startedAt: Number(r.started_at) }),
215
+ ...(r.model != null && { model: String(r.model) }),
216
+ ...(r.output_tail != null && { outputTail: String(r.output_tail) }),
217
+ ...(r.error_message != null && { errorMessage: String(r.error_message) }),
218
+ ...(r.parent_seq != null && { parentSeq: Number(r.parent_seq) }),
219
+ ...(r.manual_run_id != null && { manualRunId: String(r.manual_run_id) }),
220
+ ...(r.owner_pid != null && { ownerPid: Number(r.owner_pid) }),
221
+ ...(r.had_step_errors != null && {
222
+ hadStepErrors: Boolean(r.had_step_errors),
223
+ }),
224
+ ...(steps ? { stepResults: steps } : {}),
225
+ ...extra,
226
+ };
227
+ return run;
228
+ }
229
+ startRun(input) {
230
+ const seq = ++this.seq;
231
+ this.db
232
+ .prepare(`INSERT INTO runs (task_id, seq, recipe_name, trigger, status, created_at,
233
+ started_at, model, parent_seq, manual_run_id, owner_pid)
234
+ VALUES (?,?,?,?,'running',?,?,?,?,?,?)
235
+ ON CONFLICT(task_id) DO UPDATE SET
236
+ seq=excluded.seq, status='running', created_at=excluded.created_at,
237
+ started_at=excluded.started_at, owner_pid=excluded.owner_pid`)
238
+ .run(input.taskId, seq, input.recipeName, input.trigger, input.createdAt, input.startedAt ?? null, input.model ?? null, input.parentSeq ?? null, input.manualRunId ?? null, input.ownerPid ?? process.pid);
239
+ return seq;
240
+ }
241
+ /** Resolve a per-instance `seq` to a task id. Ambiguous by construction — see
242
+ * the class docs and #1360. Newest wins, which is the least surprising of
243
+ * several wrong answers. */
244
+ taskIdForSeq(seq) {
245
+ const r = this.db
246
+ .prepare("SELECT task_id FROM runs WHERE seq=? ORDER BY created_at DESC LIMIT 1")
247
+ .get(seq);
248
+ return r ? String(r.task_id) : null;
249
+ }
250
+ updateRunSteps(seq, stepResults) {
251
+ const taskId = this.taskIdForSeq(seq);
252
+ if (!taskId)
253
+ return;
254
+ const cur = this.db
255
+ .prepare("SELECT status FROM runs WHERE task_id=?")
256
+ .get(taskId);
257
+ if (!cur || cur.status !== "running")
258
+ return;
259
+ const now = this.now();
260
+ const ins = this.db.prepare("INSERT INTO run_steps (task_id, step_id, at, step) VALUES (?,?,?,?) ON CONFLICT(task_id, step_id) DO NOTHING");
261
+ for (const step of stepResults) {
262
+ if (!step?.id)
263
+ continue;
264
+ // Same scope as the JSONL ledger: non-reversible, or any error. A
265
+ // reversible step carries no trust evidence, and persisting everything
266
+ // would buy durability with retention — the trade that starved the
267
+ // ledger in the first place.
268
+ if (!isEvidenceBearing(step))
269
+ continue;
270
+ ins.run(taskId, step.id, now, JSON.stringify(step));
271
+ }
272
+ }
273
+ completeRun(seq, input) {
274
+ const taskId = this.taskIdForSeq(seq);
275
+ if (!taskId)
276
+ return;
277
+ const cur = this.db
278
+ .prepare("SELECT status FROM runs WHERE task_id=?")
279
+ .get(taskId);
280
+ // No-op on an already-terminal row, matching the incumbent.
281
+ if (!cur || cur.status !== "running")
282
+ return;
283
+ const extra = {};
284
+ for (const k of EXTRA_KEYS) {
285
+ const v = input[k];
286
+ if (v !== undefined)
287
+ extra[k] = v;
288
+ }
289
+ const hadStepErrors = input.stepResults.some((s) => s?.status === "error");
290
+ this.db
291
+ .prepare(`UPDATE runs SET status=?, done_at=?, duration_ms=?, step_results=?,
292
+ output_tail=?, error_message=?, had_step_errors=?, extra=?
293
+ WHERE task_id=?`)
294
+ .run(input.status, input.doneAt, input.durationMs, JSON.stringify(input.stepResults), input.outputTail ?? null, input.errorMessage ?? null, hadStepErrors ? 1 : 0, Object.keys(extra).length > 0 ? JSON.stringify(extra) : null, taskId);
295
+ }
296
+ query(q = {}) {
297
+ const where = [];
298
+ const args = [];
299
+ if (q.recipe !== undefined) {
300
+ where.push("recipe_name = ?");
301
+ args.push(q.recipe);
302
+ }
303
+ if (q.status !== undefined) {
304
+ where.push("status = ?");
305
+ args.push(q.status);
306
+ }
307
+ if (q.trigger !== undefined) {
308
+ where.push("trigger = ?");
309
+ args.push(q.trigger);
310
+ }
311
+ if (q.since !== undefined) {
312
+ where.push("created_at >= ?");
313
+ args.push(q.since);
314
+ }
315
+ if (q.after !== undefined) {
316
+ where.push("seq > ?");
317
+ args.push(q.after);
318
+ }
319
+ if (q.manualRunId !== undefined) {
320
+ where.push("manual_run_id = ?");
321
+ args.push(q.manualRunId);
322
+ }
323
+ const sql = `SELECT * FROM runs${where.length ? ` WHERE ${where.join(" AND ")}` : ""}` +
324
+ // seq as tiebreak so equal createdAt still yields a deterministic order —
325
+ // several contract cases share a timestamp.
326
+ " ORDER BY created_at DESC, seq DESC" +
327
+ (q.limit !== undefined ? " LIMIT ?" : "");
328
+ if (q.limit !== undefined)
329
+ args.push(q.limit);
330
+ const rows = this.db.prepare(sql).all(...args);
331
+ return rows.map((r) => this.rowToRun(r));
332
+ }
333
+ getBySeq(seq) {
334
+ const r = this.db
335
+ .prepare("SELECT * FROM runs WHERE seq=? ORDER BY created_at DESC LIMIT 1")
336
+ .get(seq);
337
+ return r ? this.rowToRun(r) : null;
338
+ }
339
+ getChildSeqs(parentSeq) {
340
+ const rows = this.db
341
+ .prepare("SELECT seq FROM runs WHERE parent_seq=? ORDER BY seq ASC")
342
+ .all(parentSeq);
343
+ return rows.map((r) => Number(r.seq));
344
+ }
345
+ size() {
346
+ const r = this.db.prepare("SELECT COUNT(*) AS n FROM runs").get();
347
+ return Number(r?.n ?? 0);
348
+ }
349
+ }
350
+ //# sourceMappingURL=sqliteRunRepository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqliteRunRepository.js","sourceRoot":"","sources":["../../src/runStore/sqliteRunRepository.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchwork-os",
3
- "version": "1.2.0-beta.2.canary.603",
3
+ "version": "1.2.0-beta.2.canary.604",
4
4
  "description": "Your personal AI runtime, local-first. Patchwork OS gives any AI model a consistent set of tools, YAML recipes, a delegation policy with approval queue, and a durable trace memory — all on your machine, all under your policy.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -89,7 +89,7 @@
89
89
  "README.md"
90
90
  ],
91
91
  "engines": {
92
- "node": ">=22.0.0"
92
+ "node": ">=22.5.0"
93
93
  },
94
94
  "scripts": {
95
95
  "build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc && (node scripts/postinstall.mjs || true)",