patchwork-os 1.1.0-beta.4.canary.552 → 1.1.0-beta.4.canary.554

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.
@@ -0,0 +1,72 @@
1
+ /**
2
+ * A stable reference to a single action a worker performed, used as the join
3
+ * key between a run's step and the operator's outcome disposition for it.
4
+ *
5
+ * WHY THIS EXISTS
6
+ * ---------------
7
+ * The outcome join was originally keyed on `step.output.url` — a GitHub issue
8
+ * URL. That works for exactly one tool shape and silently no-ops for every
9
+ * other, because the fold's withhold branch is guarded on the URL being
10
+ * present (`shadowObserver.ts`). A write tool that returns no URL therefore
11
+ * skipped the human-confirmation check entirely and folded as earned trust.
12
+ * `todoist.create_task` is the concrete case: it returns 26 fields, none of
13
+ * them a URL, and the Todoist API exposes no permalink field at all.
14
+ *
15
+ * The rejected alternative was to synthesise a URL inside the connector. That
16
+ * hardcodes a web-app URL scheme we do not control, breaks silently when it
17
+ * changes, and fixes one connector. Instead we key on what every write tool
18
+ * already has and cannot lose: its own name, plus the id of the thing it just
19
+ * created.
20
+ *
21
+ * TWO KEY SHAPES, ON PURPOSE, WITH NO MIGRATION
22
+ * ---------------------------------------------
23
+ * Existing `outcome-log.jsonl` rows are keyed by `issueUrl` and every one of
24
+ * them is a real GitHub URL. Those rows are NOT rewritten. A URL is already a
25
+ * perfectly good stable identifier for the thing it names, the lookup is a map
26
+ * get either way, and rewriting the sole evidence file the autonomy gate rests
27
+ * on — in place, with no rollback — to gain nothing but uniformity is a bad
28
+ * trade. The asymmetry decides it: a missed lookup costs a WITHHOLD (the safe
29
+ * default), a botched migration costs the ledger.
30
+ *
31
+ * The two shapes cannot collide, and that is asserted rather than assumed:
32
+ * `canonicalActionRef` REFUSES to build a key that looks like a URL, so a
33
+ * `tool:id` key can never shadow or be shadowed by a legacy `issueUrl` key.
34
+ */
35
+ /** A tool-scoped reference to one performed action. */
36
+ export interface ActionRef {
37
+ /** The tool that performed the action, e.g. `"todoist.create_task"`. */
38
+ tool: string;
39
+ /** The external id the tool returned for the thing it created. */
40
+ id: string;
41
+ }
42
+ /**
43
+ * Thrown when a caller tries to build a key that would be ambiguous against
44
+ * the legacy URL-shaped keys. Loud on purpose — see the header note. A deny
45
+ * of this kind fails the write rather than coercing it, because a coerced key
46
+ * would silently land in the ledger under a name nothing looks up.
47
+ */
48
+ export declare class AmbiguousActionRefError extends Error {
49
+ constructor(message: string);
50
+ }
51
+ /**
52
+ * The canonical string key for an `ActionRef`: `"<tool>:<id>"`.
53
+ *
54
+ * Throws `AmbiguousActionRefError` when either half is empty/blank, or when
55
+ * the resulting key would be URL-shaped (which would put it in the same
56
+ * namespace as the legacy `issueUrl` keys). Both are programmer errors at a
57
+ * write site, not user input to be tolerated.
58
+ */
59
+ export declare function canonicalActionRef(ref: ActionRef): string;
60
+ /**
61
+ * Derive the outcome-join key for a completed step, or null when the step's
62
+ * output carries nothing usable as an identifier.
63
+ *
64
+ * Returning null is meaningful and must stay distinguishable from returning a
65
+ * key that finds no disposition: null means "this action cannot be referred
66
+ * to", whereas a key with no disposition means "nobody has ruled on it yet".
67
+ * The two justify different fold outcomes.
68
+ *
69
+ * NOTE: a `url`/`html_url` field yields the URL itself as the key — NOT a
70
+ * `tool:url` composite — precisely so legacy rows keep joining.
71
+ */
72
+ export declare function deriveActionKey(tool: string | undefined, output: unknown): string | null;
@@ -0,0 +1,133 @@
1
+ /**
2
+ * A stable reference to a single action a worker performed, used as the join
3
+ * key between a run's step and the operator's outcome disposition for it.
4
+ *
5
+ * WHY THIS EXISTS
6
+ * ---------------
7
+ * The outcome join was originally keyed on `step.output.url` — a GitHub issue
8
+ * URL. That works for exactly one tool shape and silently no-ops for every
9
+ * other, because the fold's withhold branch is guarded on the URL being
10
+ * present (`shadowObserver.ts`). A write tool that returns no URL therefore
11
+ * skipped the human-confirmation check entirely and folded as earned trust.
12
+ * `todoist.create_task` is the concrete case: it returns 26 fields, none of
13
+ * them a URL, and the Todoist API exposes no permalink field at all.
14
+ *
15
+ * The rejected alternative was to synthesise a URL inside the connector. That
16
+ * hardcodes a web-app URL scheme we do not control, breaks silently when it
17
+ * changes, and fixes one connector. Instead we key on what every write tool
18
+ * already has and cannot lose: its own name, plus the id of the thing it just
19
+ * created.
20
+ *
21
+ * TWO KEY SHAPES, ON PURPOSE, WITH NO MIGRATION
22
+ * ---------------------------------------------
23
+ * Existing `outcome-log.jsonl` rows are keyed by `issueUrl` and every one of
24
+ * them is a real GitHub URL. Those rows are NOT rewritten. A URL is already a
25
+ * perfectly good stable identifier for the thing it names, the lookup is a map
26
+ * get either way, and rewriting the sole evidence file the autonomy gate rests
27
+ * on — in place, with no rollback — to gain nothing but uniformity is a bad
28
+ * trade. The asymmetry decides it: a missed lookup costs a WITHHOLD (the safe
29
+ * default), a botched migration costs the ledger.
30
+ *
31
+ * The two shapes cannot collide, and that is asserted rather than assumed:
32
+ * `canonicalActionRef` REFUSES to build a key that looks like a URL, so a
33
+ * `tool:id` key can never shadow or be shadowed by a legacy `issueUrl` key.
34
+ */
35
+ /**
36
+ * Thrown when a caller tries to build a key that would be ambiguous against
37
+ * the legacy URL-shaped keys. Loud on purpose — see the header note. A deny
38
+ * of this kind fails the write rather than coercing it, because a coerced key
39
+ * would silently land in the ledger under a name nothing looks up.
40
+ */
41
+ export class AmbiguousActionRefError extends Error {
42
+ constructor(message) {
43
+ super(message);
44
+ this.name = "AmbiguousActionRefError";
45
+ }
46
+ }
47
+ /** Keys of this shape belong to the legacy URL-keyed rows. */
48
+ const URL_LIKE = /^https?:\/\//i;
49
+ /**
50
+ * The canonical string key for an `ActionRef`: `"<tool>:<id>"`.
51
+ *
52
+ * Throws `AmbiguousActionRefError` when either half is empty/blank, or when
53
+ * the resulting key would be URL-shaped (which would put it in the same
54
+ * namespace as the legacy `issueUrl` keys). Both are programmer errors at a
55
+ * write site, not user input to be tolerated.
56
+ */
57
+ export function canonicalActionRef(ref) {
58
+ const tool = ref.tool?.trim();
59
+ const id = ref.id?.trim();
60
+ if (!tool || !id) {
61
+ throw new AmbiguousActionRefError(`An action ref needs both a tool and an id (got tool=${JSON.stringify(ref.tool)}, id=${JSON.stringify(ref.id)}).`);
62
+ }
63
+ const key = `${tool}:${id}`;
64
+ if (URL_LIKE.test(key)) {
65
+ throw new AmbiguousActionRefError(`Action ref "${key}" is URL-shaped, which collides with the legacy issueUrl key namespace. A tool name must not begin with http(s).`);
66
+ }
67
+ return key;
68
+ }
69
+ /**
70
+ * The id fields write tools actually return, in preference order.
71
+ *
72
+ * Ordered most-specific first so a payload carrying several does not depend on
73
+ * object key order. `url` is LAST and deliberately included: for a tool that
74
+ * does return a URL (github.create_issue), the URL remains the key, which is
75
+ * what keeps the 95 existing `issueUrl` rows joining without a migration.
76
+ */
77
+ const ID_FIELDS = [
78
+ "url",
79
+ "html_url",
80
+ "id",
81
+ "issueNumber",
82
+ "number",
83
+ "key",
84
+ "gid",
85
+ "ts",
86
+ ];
87
+ /**
88
+ * Derive the outcome-join key for a completed step, or null when the step's
89
+ * output carries nothing usable as an identifier.
90
+ *
91
+ * Returning null is meaningful and must stay distinguishable from returning a
92
+ * key that finds no disposition: null means "this action cannot be referred
93
+ * to", whereas a key with no disposition means "nobody has ruled on it yet".
94
+ * The two justify different fold outcomes.
95
+ *
96
+ * NOTE: a `url`/`html_url` field yields the URL itself as the key — NOT a
97
+ * `tool:url` composite — precisely so legacy rows keep joining.
98
+ */
99
+ export function deriveActionKey(tool, output) {
100
+ if (!tool)
101
+ return null;
102
+ if (output === null || typeof output !== "object" || Array.isArray(output)) {
103
+ return null;
104
+ }
105
+ const rec = output;
106
+ for (const field of ID_FIELDS) {
107
+ if (!Object.hasOwn(rec, field))
108
+ continue;
109
+ const raw = rec[field];
110
+ // Numeric ids are as legitimate as string ones (GitHub issue numbers,
111
+ // Slack/Asana numeric ids). Booleans and objects are not identifiers.
112
+ const value = typeof raw === "string"
113
+ ? raw.trim()
114
+ : typeof raw === "number" && Number.isFinite(raw)
115
+ ? String(raw)
116
+ : "";
117
+ if (!value)
118
+ continue;
119
+ // A URL field IS the key (legacy compatibility, see the note above).
120
+ if (URL_LIKE.test(value))
121
+ return value;
122
+ try {
123
+ return canonicalActionRef({ tool, id: value });
124
+ }
125
+ catch {
126
+ // A tool name that would make a URL-shaped key: skip this field rather
127
+ // than throwing on a read path. Read paths must never crash the fold.
128
+ return null;
129
+ }
130
+ }
131
+ return null;
132
+ }
133
+ //# sourceMappingURL=actionRef.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actionRef.js","sourceRoot":"","sources":["../../src/workers/actionRef.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAUH;;;;;GAKG;AACH,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAChD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACxC,CAAC;CACF;AAED,8DAA8D;AAC9D,MAAM,QAAQ,GAAG,eAAe,CAAC;AAEjC;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAc;IAC/C,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC;IAC9B,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,uBAAuB,CAC/B,uDAAuD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAClH,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,EAAE,EAAE,CAAC;IAC5B,IAAI,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,uBAAuB,CAC/B,eAAe,GAAG,kHAAkH,CACrI,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,SAAS,GAAG;IAChB,KAAK;IACL,UAAU;IACV,IAAI;IACJ,aAAa;IACb,QAAQ;IACR,KAAK;IACL,KAAK;IACL,IAAI;CACI,CAAC;AAEX;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAwB,EACxB,MAAe;IAEf,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3E,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,GAAG,GAAG,MAAiC,CAAC;IAC9C,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC9B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC;YAAE,SAAS;QACzC,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;QACvB,sEAAsE;QACtE,sEAAsE;QACtE,MAAM,KAAK,GACT,OAAO,GAAG,KAAK,QAAQ;YACrB,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE;YACZ,CAAC,CAAC,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;gBAC/C,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;gBACb,CAAC,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,qEAAqE;QACrE,IAAI,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACvC,IAAI,CAAC;YACH,OAAO,kBAAkB,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QACjD,CAAC;QAAC,MAAM,CAAC;YACP,uEAAuE;YACvE,sEAAsE;YACtE,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { type ActionRef } from "./actionRef.js";
1
2
  /**
2
3
  * Outcome disposition for a filed issue. Drives the trust signal the ramp
3
4
  * folds in once the durability window has elapsed:
@@ -28,8 +29,24 @@ export type OutcomeDisposition = "confirmed" | "junk" | "unknown";
28
29
  */
29
30
  export declare function resolveOutcomeLogDir(override?: string): string;
30
31
  export interface OutcomeRecord {
31
- /** GitHub issue URL — the lookup key. */
32
- issueUrl: string;
32
+ /**
33
+ * GitHub issue URL — the legacy lookup key, and still the key for any row
34
+ * that has one. Optional ONLY because `ref` is the alternative; exactly one
35
+ * of `issueUrl` / `ref` must be present or the row cannot be keyed at all
36
+ * (see `parseOutcomeLog`, which now REPORTS such rows rather than dropping
37
+ * them silently).
38
+ */
39
+ issueUrl?: string;
40
+ /**
41
+ * Tool-scoped reference for an action that has no URL — the generalisation
42
+ * that lets a worker earn trust from actions that are not GitHub issues.
43
+ * Keyed via `canonicalActionRef` to `"<tool>:<id>"`, a namespace that cannot
44
+ * collide with `issueUrl` (asserted, not assumed — see actionRef.ts).
45
+ *
46
+ * Existing rows are NOT migrated: a URL is already a fine key, and rewriting
47
+ * the file the autonomy gate rests on to gain uniformity is a bad trade.
48
+ */
49
+ ref?: ActionRef;
33
50
  disposition: OutcomeDisposition;
34
51
  /** Epoch ms when this record was written by the ingester. */
35
52
  checkedAt: number;
@@ -54,6 +71,23 @@ export interface OutcomeRecord {
54
71
  */
55
72
  origin?: "manual" | "ingester";
56
73
  }
74
+ /**
75
+ * A row the parser could not turn into a lookup key.
76
+ *
77
+ * These used to be `continue`d silently. That is the quiet-corruption vector
78
+ * this file most needs to avoid: the outcome log is the ONLY record of which
79
+ * worker actions a human actually blessed, so a row that vanishes on read
80
+ * looks exactly like a row nobody ever wrote — and the difference is whether
81
+ * a worker earned its autonomy. They are still skipped (a row with no key is
82
+ * genuinely unusable), but they are now COUNTED and reportable.
83
+ */
84
+ export interface UnkeyableRow {
85
+ /** 1-based line number in the log, so an operator can go find it. */
86
+ line: number;
87
+ reason: "malformed-json" | "no-key" | "no-disposition" | "bad-ref";
88
+ /** First 120 chars of the offending line, for identification. */
89
+ excerpt: string;
90
+ }
57
91
  /**
58
92
  * Persist + query outcome dispositions for filed issues.
59
93
  *
@@ -79,10 +113,28 @@ export declare class OutcomeStore {
79
113
  * evidence). A filing with no recorded disposition can neither raise nor
80
114
  * lower trust. (#1064)
81
115
  */
82
- getDisposition(issueUrl: string): OutcomeDisposition | null;
116
+ getDisposition(key: string): OutcomeDisposition | null;
117
+ /**
118
+ * Disposition for a tool-scoped action reference (the non-URL join). Same
119
+ * null semantics as `getDisposition`. A ref that cannot be canonicalised
120
+ * returns null rather than throwing — this is a read path, and the fold
121
+ * must never crash on a malformed stored ref.
122
+ */
123
+ getDispositionForRef(ref: ActionRef): OutcomeDisposition | null;
83
124
  /**
84
- * Persist a disposition for `issueUrl`. Later calls supersede earlier ones
85
- * (both on disk via append, and in the shared in-memory cache).
125
+ * Rows in the log that carry no usable lookup key (or no disposition, or
126
+ * are not valid JSON). Empty in the healthy case. Surfaced so an operator
127
+ * can be TOLD the ledger has holes instead of a confirmation quietly going
128
+ * missing — the whole point of counting these rather than skipping them.
129
+ */
130
+ unkeyableRows(): UnkeyableRow[];
131
+ /**
132
+ * Persist a disposition. Later calls supersede earlier ones for the same
133
+ * key (both on disk via append, and in the shared in-memory cache).
134
+ *
135
+ * Throws when the record carries neither a usable `ref` nor an `issueUrl`:
136
+ * a row written with no key is invisible to every reader, so accepting it
137
+ * would silently discard an operator's explicit judgment.
86
138
  */
87
139
  upsert(record: OutcomeRecord): void;
88
140
  /** All records (deduped, last-writer-wins). For reporting / ingester diffing. */
@@ -1,6 +1,7 @@
1
1
  import { appendFileSync, existsSync, readFileSync, statSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import path from "node:path";
4
+ import { AmbiguousActionRefError, canonicalActionRef, } from "./actionRef.js";
4
5
  /**
5
6
  * The directory that holds `outcome-log.jsonl`. Honors `PATCHWORK_HOME` (when
6
7
  * set) so the WRITE path — `patchwork outcomes confirm|reject`, the
@@ -16,35 +17,66 @@ export function resolveOutcomeLogDir(override) {
16
17
  return (override ?? process.env.PATCHWORK_HOME ?? path.join(homedir(), ".patchwork"));
17
18
  }
18
19
  const outcomeLogCache = new Map();
20
+ /**
21
+ * The lookup key for a record: its `ref` (canonicalised) when present,
22
+ * otherwise its legacy `issueUrl`. Returns null when neither yields one.
23
+ */
24
+ function keyForRecord(r) {
25
+ if (r.ref) {
26
+ try {
27
+ return canonicalActionRef(r.ref);
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ }
33
+ return r.issueUrl?.trim() || null;
34
+ }
19
35
  function parseOutcomeLog(logPath) {
20
36
  const dispositions = new Map();
21
37
  const records = new Map();
38
+ const unkeyable = [];
22
39
  if (!existsSync(logPath))
23
- return { dispositions, records };
40
+ return { dispositions, records, unkeyable };
24
41
  const text = readFileSync(logPath, "utf-8");
25
- for (const line of text.split("\n")) {
26
- const t = line.trim();
42
+ const lines = text.split("\n");
43
+ for (let i = 0; i < lines.length; i++) {
44
+ const t = (lines[i] ?? "").trim();
27
45
  if (!t)
28
46
  continue;
47
+ const excerpt = t.slice(0, 120);
48
+ let r;
29
49
  try {
30
- const r = JSON.parse(t);
31
- if (!r.issueUrl || !r.disposition)
32
- continue;
33
- // A manual disposition is sticky against a later ingester write for
34
- // the same issueUrl — an automated poll must not silently erase an
35
- // operator's explicit judgment call. A later manual write (the
36
- // operator changing their own mind) always applies normally.
37
- const existing = records.get(r.issueUrl);
38
- if (existing?.origin === "manual" && r.origin !== "manual")
39
- continue;
40
- dispositions.set(r.issueUrl, r.disposition);
41
- records.set(r.issueUrl, r);
50
+ r = JSON.parse(t);
42
51
  }
43
52
  catch {
44
- /* skip malformed lines */
53
+ unkeyable.push({ line: i + 1, reason: "malformed-json", excerpt });
54
+ continue;
55
+ }
56
+ if (!r.disposition) {
57
+ unkeyable.push({ line: i + 1, reason: "no-disposition", excerpt });
58
+ continue;
59
+ }
60
+ const key = keyForRecord(r);
61
+ if (!key) {
62
+ unkeyable.push({
63
+ line: i + 1,
64
+ reason: r.ref ? "bad-ref" : "no-key",
65
+ excerpt,
66
+ });
67
+ continue;
45
68
  }
69
+ // A manual disposition is sticky against a later ingester write for
70
+ // the same key — an automated poll must not silently erase an
71
+ // operator's explicit judgment call. A later manual write (the
72
+ // operator changing their own mind) always applies normally.
73
+ const existing = records.get(key);
74
+ if (existing?.origin === "manual" && r.origin !== "manual")
75
+ continue;
76
+ dispositions.set(key, r.disposition);
77
+ records.set(key, r);
46
78
  }
47
- return { dispositions, records };
79
+ return { dispositions, records, unkeyable };
48
80
  }
49
81
  /** Fresh parse + fresh stat, always (used to (re)seed the shared cache). */
50
82
  function loadOutcomeLogEntry(logPath) {
@@ -58,8 +90,8 @@ function loadOutcomeLogEntry(logPath) {
58
90
  catch {
59
91
  /* file absent — still cacheable at (-1, -1) */
60
92
  }
61
- const { dispositions, records } = parseOutcomeLog(logPath);
62
- return { mtimeMs, size, dispositions, records };
93
+ const { dispositions, records, unkeyable } = parseOutcomeLog(logPath);
94
+ return { mtimeMs, size, dispositions, records, unkeyable };
63
95
  }
64
96
  /** The shared entry for `logPath`, reparsing only if the file actually changed. */
65
97
  function getOutcomeLogEntry(logPath) {
@@ -108,14 +140,46 @@ export class OutcomeStore {
108
140
  * evidence). A filing with no recorded disposition can neither raise nor
109
141
  * lower trust. (#1064)
110
142
  */
111
- getDisposition(issueUrl) {
112
- return getOutcomeLogEntry(this.logPath).dispositions.get(issueUrl) ?? null;
143
+ getDisposition(key) {
144
+ return getOutcomeLogEntry(this.logPath).dispositions.get(key) ?? null;
145
+ }
146
+ /**
147
+ * Disposition for a tool-scoped action reference (the non-URL join). Same
148
+ * null semantics as `getDisposition`. A ref that cannot be canonicalised
149
+ * returns null rather than throwing — this is a read path, and the fold
150
+ * must never crash on a malformed stored ref.
151
+ */
152
+ getDispositionForRef(ref) {
153
+ let key;
154
+ try {
155
+ key = canonicalActionRef(ref);
156
+ }
157
+ catch {
158
+ return null;
159
+ }
160
+ return this.getDisposition(key);
161
+ }
162
+ /**
163
+ * Rows in the log that carry no usable lookup key (or no disposition, or
164
+ * are not valid JSON). Empty in the healthy case. Surfaced so an operator
165
+ * can be TOLD the ledger has holes instead of a confirmation quietly going
166
+ * missing — the whole point of counting these rather than skipping them.
167
+ */
168
+ unkeyableRows() {
169
+ return getOutcomeLogEntry(this.logPath).unkeyable;
113
170
  }
114
171
  /**
115
- * Persist a disposition for `issueUrl`. Later calls supersede earlier ones
116
- * (both on disk via append, and in the shared in-memory cache).
172
+ * Persist a disposition. Later calls supersede earlier ones for the same
173
+ * key (both on disk via append, and in the shared in-memory cache).
174
+ *
175
+ * Throws when the record carries neither a usable `ref` nor an `issueUrl`:
176
+ * a row written with no key is invisible to every reader, so accepting it
177
+ * would silently discard an operator's explicit judgment.
117
178
  */
118
179
  upsert(record) {
180
+ if (!keyForRecord(record)) {
181
+ throw new AmbiguousActionRefError("An outcome record needs a usable key — either `issueUrl` or a `ref` with a tool and an id. Refusing to write a record nothing can look up.");
182
+ }
119
183
  const line = `${JSON.stringify(record)}\n`;
120
184
  appendFileSync(this.logPath, line, "utf-8");
121
185
  // Re-seed the shared cache from the post-write file state so this write
@@ -1 +1 @@
1
- {"version":3,"file":"outcomeStore.js","sourceRoot":"","sources":["../../src/workers/outcomeStore.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"outcomeStore.js","sourceRoot":"","sources":["../../src/workers/outcomeStore.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,8 @@
1
1
  const OUTCOMES_USAGE = `Usage:
2
- patchwork outcomes confirm <issue-url> [--recipe <name>] [--class <actionClass>]
3
- patchwork outcomes reject <issue-url> [--recipe <name>] [--class <actionClass>]
2
+ patchwork outcomes confirm <issue-url> [--recipe <name>] [--class <actionClass>]
3
+ patchwork outcomes confirm --tool <t> --id <id> [--recipe <name>] [--class <actionClass>]
4
+ patchwork outcomes reject <issue-url> [--recipe <name>] [--class <actionClass>]
5
+ patchwork outcomes reject --tool <t> --id <id> [--recipe <name>] [--class <actionClass>]
4
6
  patchwork outcomes list [--json]
5
7
  patchwork outcomes pending [--json]
6
8
 
@@ -14,6 +16,10 @@ cannot self-confirm its own filings.
14
16
  list print all recorded outcomes (--json for raw output)
15
17
  pending list filings still awaiting confirmation + the exact confirm command
16
18
 
19
+ --tool <tool> --id <id> reference an action that returned no URL, by the tool
20
+ that performed it plus the id it returned (e.g.
21
+ --tool todoist.create_task --id AaBbCcDdEeFfGgHh).
22
+ Both are required together; never mixed with a URL.
17
23
  --recipe <name> stamp the filing recipe onto the record (audit context)
18
24
  --class <actionClass> stamp the action class onto the record (audit context)
19
25
  `;
@@ -41,33 +47,69 @@ export function runOutcomesCli(args, deps) {
41
47
  return { stdout: formatOutcomeList(records), exitCode: 0 };
42
48
  }
43
49
  if (sub === "confirm" || sub === "reject") {
44
- const issueUrl = args[1];
45
- if (!issueUrl || issueUrl.startsWith("-")) {
50
+ const positional = args[1];
51
+ const refTool = flagValue(args, "--tool");
52
+ const refId = flagValue(args, "--id");
53
+ const usingRef = refTool !== undefined || refId !== undefined;
54
+ // Never guess which form the operator meant. A bare string that happens
55
+ // to look like neither is an error, not something to coerce into a key
56
+ // nothing will look up.
57
+ if (usingRef && positional && !positional.startsWith("-")) {
46
58
  return {
47
- stderr: `Error: \`outcomes ${sub}\` requires an <issue-url>.\n\n${OUTCOMES_USAGE}`,
59
+ stderr: `Error: pass EITHER an <issue-url> OR --tool/--id, not both (got "${positional}" alongside --tool/--id).\n`,
48
60
  exitCode: 1,
49
61
  };
50
62
  }
51
- if (!/^https?:\/\//.test(issueUrl)) {
52
- return {
53
- stderr: `Error: <issue-url> must be an http(s) URL (got "${issueUrl}"). Pass the issue's GitHub URL exactly as the worker filed it.\n`,
54
- exitCode: 1,
55
- };
63
+ if (usingRef) {
64
+ if (!refTool ||
65
+ !refId ||
66
+ refTool.startsWith("-") ||
67
+ refId.startsWith("-")) {
68
+ return {
69
+ stderr: `Error: --tool and --id must BOTH be given, with values (e.g. --tool todoist.create_task --id AaBbCcDdEeFfGgHh).\n`,
70
+ exitCode: 1,
71
+ };
72
+ }
73
+ }
74
+ else {
75
+ if (!positional || positional.startsWith("-")) {
76
+ return {
77
+ stderr: `Error: \`outcomes ${sub}\` requires an <issue-url>, or --tool <tool> --id <id> for an action with no URL.\n\n${OUTCOMES_USAGE}`,
78
+ exitCode: 1,
79
+ };
80
+ }
81
+ if (!/^https?:\/\//.test(positional)) {
82
+ return {
83
+ stderr: `Error: <issue-url> must be an http(s) URL (got "${positional}"). Pass the issue's URL exactly as the worker filed it, or use --tool <tool> --id <id> for an action that has no URL.\n`,
84
+ exitCode: 1,
85
+ };
86
+ }
56
87
  }
57
88
  const disposition = sub === "confirm" ? "confirmed" : "junk";
58
89
  const recipeName = flagValue(args, "--recipe");
59
90
  const workerClass = flagValue(args, "--class");
60
91
  const record = {
61
- issueUrl,
92
+ ...(usingRef
93
+ ? { ref: { tool: refTool, id: refId } }
94
+ : { issueUrl: positional }),
62
95
  disposition,
63
96
  checkedAt: deps.now,
64
97
  origin: "manual",
65
98
  ...(recipeName ? { recipeName } : {}),
66
99
  ...(workerClass ? { workerClass } : {}),
67
100
  };
68
- deps.store.upsert(record);
101
+ try {
102
+ deps.store.upsert(record);
103
+ }
104
+ catch (err) {
105
+ return {
106
+ stderr: `Error: ${err instanceof Error ? err.message : String(err)}\n`,
107
+ exitCode: 1,
108
+ };
109
+ }
110
+ const what = usingRef ? `${refTool}:${refId}` : positional;
69
111
  return {
70
- stdout: `Recorded ${disposition} for ${issueUrl}\n` +
112
+ stdout: `Recorded ${disposition} for ${what}\n` +
71
113
  "The trust ramp folds this on its next replay — run `patchwork workers shadow` to see the dial move.\n",
72
114
  exitCode: 0,
73
115
  };
@@ -85,7 +127,13 @@ export function formatOutcomeList(records) {
85
127
  const lines = records.map((r) => {
86
128
  const when = new Date(r.checkedAt).toISOString();
87
129
  const ctx = [r.recipeName, r.workerClass].filter(Boolean).join(" · ");
88
- return ` ${r.disposition.padEnd(9)} ${r.issueUrl}${ctx ? ` (${ctx})` : ""} @ ${when}`;
130
+ // Either key shape renders; a row carrying neither cannot be written any
131
+ // more (upsert refuses it) but may exist in an older log, so it is shown
132
+ // as an explicit hole rather than a blank.
133
+ const what = r.ref
134
+ ? `${r.ref.tool}:${r.ref.id}`
135
+ : (r.issueUrl ?? "(no key — unusable row)");
136
+ return ` ${r.disposition.padEnd(9)} ${what}${ctx ? ` (${ctx})` : ""} @ ${when}`;
89
137
  });
90
138
  return `${records.length} recorded outcome(s):\n${lines.join("\n")}\n`;
91
139
  }
@@ -1 +1 @@
1
- {"version":3,"file":"outcomesCli.js","sourceRoot":"","sources":["../../src/workers/outcomesCli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"outcomesCli.js","sourceRoot":"","sources":["../../src/workers/outcomesCli.ts"],"names":[],"mappings":"AA8BA,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;CAwBtB,CAAC;AAEF,sDAAsD;AACtD,SAAS,SAAS,CAAC,IAAc,EAAE,IAAY;IAC7C,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACjE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,IAAc,EACd,IAA0C;IAE1C,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3D,2EAA2E;QAC3E,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3D,CAAC;IAED,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;QACnB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;QAC1E,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,iBAAiB,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IAC7D,CAAC;IAED,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC1C,MAAM,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3B,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC1C,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACtC,MAAM,QAAQ,GAAG,OAAO,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,CAAC;QAE9D,wEAAwE;QACxE,uEAAuE;QACvE,wBAAwB;QACxB,IAAI,QAAQ,IAAI,UAAU,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1D,OAAO;gBACL,MAAM,EAAE,oEAAoE,UAAU,6BAA6B;gBACnH,QAAQ,EAAE,CAAC;aACZ,CAAC;QACJ,CAAC;QACD,IAAI,QAAQ,EAAE,CAAC;YACb,IACE,CAAC,OAAO;gBACR,CAAC,KAAK;gBACN,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;gBACvB,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EACrB,CAAC;gBACD,OAAO;oBACL,MAAM,EAAE,mHAAmH;oBAC3H,QAAQ,EAAE,CAAC;iBACZ,CAAC;YACJ,CAAC;QACH,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC9C,OAAO;oBACL,MAAM,EAAE,qBAAqB,GAAG,wFAAwF,cAAc,EAAE;oBACxI,QAAQ,EAAE,CAAC;iBACZ,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;gBACrC,OAAO;oBACL,MAAM,EAAE,mDAAmD,UAAU,0HAA0H;oBAC/L,QAAQ,EAAE,CAAC;iBACZ,CAAC;YACJ,CAAC;QACH,CAAC;QAED,MAAM,WAAW,GACf,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC;QAC3C,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QAC/C,MAAM,WAAW,GAAG,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAkB;YAC5B,GAAG,CAAC,QAAQ;gBACV,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,OAAiB,EAAE,EAAE,EAAE,KAAe,EAAE,EAAE;gBAC3D,CAAC,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;YAC7B,WAAW;YACX,SAAS,EAAE,IAAI,CAAC,GAAG;YACnB,MAAM,EAAE,QAAQ;YAChB,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACxC,CAAC;QACF,IAAI,CAAC;YACH,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,MAAM,EAAE,UAAU,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI;gBACtE,QAAQ,EAAE,CAAC;aACZ,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC;QAC3D,OAAO;YACL,MAAM,EACJ,YAAY,WAAW,QAAQ,IAAI,IAAI;gBACvC,uGAAuG;YACzG,QAAQ,EAAE,CAAC;SACZ,CAAC;IACJ,CAAC;IAED,OAAO;QACL,MAAM,EAAE,8BAA8B,GAAG,SAAS,cAAc,EAAE;QAClE,QAAQ,EAAE,CAAC;KACZ,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,iBAAiB,CAAC,OAAwB;IACxD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,iFAAiF,CAAC;IAC3F,CAAC;IACD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QAC9B,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;QACjD,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACtE,yEAAyE;QACzE,yEAAyE;QACzE,2CAA2C;QAC3C,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG;YAChB,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE;YAC7B,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,yBAAyB,CAAC,CAAC;QAC9C,OAAO,KAAK,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,EAAE,CAAC;IACrF,CAAC,CAAC,CAAC;IACH,OAAO,GAAG,OAAO,CAAC,MAAM,0BAA0B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;AACzE,CAAC"}
@@ -127,7 +127,29 @@ export declare function foldOutcome(step: FoldStep, runAt: number, opts: {
127
127
  now?: number;
128
128
  windowMs: number;
129
129
  outcomeStore?: OutcomeStore;
130
+ /**
131
+ * Key the outcome join on tool-name + external id rather than on
132
+ * `output.url` alone. Default false = the historical URL-only behaviour.
133
+ * See the note at the join site below for why this is gated.
134
+ */
135
+ strictOutcomeJoin?: boolean;
130
136
  }): FoldDecision;
137
+ /**
138
+ * How `foldOutcome` would label this step under BOTH join rules — the measured
139
+ * blast radius of turning `strictOutcomeJoin` on, without turning it on.
140
+ *
141
+ * Returns null when the two agree (the overwhelmingly common case, and the
142
+ * only case an operator does not need to see).
143
+ */
144
+ export declare function foldJoinDelta(step: FoldStep, runAt: number, opts: {
145
+ now?: number;
146
+ windowMs: number;
147
+ outcomeStore?: OutcomeStore;
148
+ }): {
149
+ tool: string;
150
+ lenient: FoldDecision;
151
+ strict: FoldDecision;
152
+ } | null;
131
153
  export declare class WorkerShadowObserver {
132
154
  private readonly store;
133
155
  private readonly cfg;
@@ -1,5 +1,6 @@
1
1
  import { categoriseHaltReason } from "../recipes/haltCategory.js";
2
2
  import { classifyActionClass } from "./actionClass.js";
3
+ import { deriveActionKey } from "./actionRef.js";
3
4
  import { DEFAULT_GRADUATION_CONFIG, } from "./graduation.js";
4
5
  import { recommend } from "./shadowGate.js";
5
6
  import { ownsAction, ownsClassKey, priorFor, } from "./worker.js";
@@ -40,10 +41,28 @@ export function foldOutcome(step, runAt, opts) {
40
41
  // For a non-reversible filing, read the operator disposition up front (issue /
41
42
  // PR URL). Reversible successes never consult the store — they are always durable.
42
43
  const outcomeStore = ac.reversibility !== "reversible" ? opts.outcomeStore : undefined;
44
+ // The join key for this action. Legacy behaviour keyed on `output.url`
45
+ // alone, which meant any write tool returning no URL skipped the human
46
+ // confirmation check entirely and fell through to good:true below — trust
47
+ // for work nobody ever looked at. `deriveActionKey` generalises this to
48
+ // tool-name + external id (see actionRef.ts), which every write tool has.
49
+ //
50
+ // GATED, on purpose. Turning the generalisation on makes previously-
51
+ // unjoinable successes reachable by the WITHHOLD branch below, which
52
+ // de-rates every worker that earned trust from a non-URL action. That is
53
+ // the correct end state, but it is a live change to the dial the gate reads,
54
+ // so it ships measured rather than assumed: `strictOutcomeJoin` defaults to
55
+ // false (byte-identical to the URL-only behaviour) and the shadow report
56
+ // counts what WOULD change. Flipping the default is a separate change.
43
57
  const url = outcomeStore && step.output && typeof step.output.url === "string"
44
58
  ? step.output.url
45
59
  : null;
46
- const disposition = url ? (outcomeStore?.getDisposition(url) ?? null) : null;
60
+ const key = opts.strictOutcomeJoin
61
+ ? outcomeStore
62
+ ? deriveActionKey(step.tool, step.output)
63
+ : null
64
+ : url;
65
+ const disposition = key ? (outcomeStore?.getDisposition(key) ?? null) : null;
47
66
  // A human REJECTION demotes IMMEDIATELY. Junk is durable evidence of failure
48
67
  // the moment it lands, so — like any outright failure (status ≠ ok above) — it
49
68
  // must NOT sit withheld for the durability window ("demotion is instant", see
@@ -56,10 +75,29 @@ export function foldOutcome(step, runAt, opts) {
56
75
  return { fold: false }; // pending — survives the window before it earns trust
57
76
  // Past the window: an unactioned filing (unknown / no record) is WITHHELD —
58
77
  // unactioned ≠ correct (trust-by-neglect fix). Confirmed / no-url → good:true.
59
- if (url && (disposition === "unknown" || disposition === null))
78
+ if (key && (disposition === "unknown" || disposition === null))
60
79
  return { fold: false }; // withheld — not evidence
61
80
  return { fold: true, good: true };
62
81
  }
82
+ /**
83
+ * How `foldOutcome` would label this step under BOTH join rules — the measured
84
+ * blast radius of turning `strictOutcomeJoin` on, without turning it on.
85
+ *
86
+ * Returns null when the two agree (the overwhelmingly common case, and the
87
+ * only case an operator does not need to see).
88
+ */
89
+ export function foldJoinDelta(step, runAt, opts) {
90
+ const lenient = foldOutcome(step, runAt, {
91
+ ...opts,
92
+ strictOutcomeJoin: false,
93
+ });
94
+ const strict = foldOutcome(step, runAt, { ...opts, strictOutcomeJoin: true });
95
+ const same = lenient.fold === strict.fold &&
96
+ (!lenient.fold || !strict.fold || lenient.good === strict.good);
97
+ if (same)
98
+ return null;
99
+ return { tool: step.tool ?? "(none)", lenient, strict };
100
+ }
63
101
  export class WorkerShadowObserver {
64
102
  store;
65
103
  cfg;
@@ -1 +1 @@
1
- {"version":3,"file":"shadowObserver.js","sourceRoot":"","sources":["../../src/workers/shadowObserver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shadowObserver.js","sourceRoot":"","sources":["../../src/workers/shadowObserver.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.1.0-beta.4.canary.552",
3
+ "version": "1.1.0-beta.4.canary.554",
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",