patchwork-os 0.2.0-beta.13.canary.293 → 0.2.0-beta.13.canary.294
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/dist/recipes/tools/index.d.ts +1 -0
- package/dist/recipes/tools/index.js +1 -0
- package/dist/recipes/tools/index.js.map +1 -1
- package/dist/recipes/tools/outcomes.d.ts +15 -0
- package/dist/recipes/tools/outcomes.js +114 -0
- package/dist/recipes/tools/outcomes.js.map +1 -0
- package/package.json +1 -1
- package/templates/recipes/outcome-ingester.yaml +13 -30
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/recipes/tools/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,aAAa;AACb,OAAO,WAAW,CAAC;AACnB,OAAO,UAAU,CAAC;AAClB,OAAO,kBAAkB,CAAC;AAC1B,OAAO,WAAW,CAAC;AACnB,OAAO,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/recipes/tools/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,aAAa;AACb,OAAO,WAAW,CAAC;AACnB,OAAO,UAAU,CAAC;AAClB,OAAO,kBAAkB,CAAC;AAC1B,OAAO,WAAW,CAAC;AACnB,OAAO,aAAa,CAAC;AACrB,OAAO,eAAe,CAAC;AAEvB,wBAAwB;AACxB,OAAO,YAAY,CAAC;AACpB,OAAO,YAAY,CAAC;AACpB,OAAO,kBAAkB,CAAC;AAC1B,OAAO,aAAa,CAAC;AACrB,OAAO,aAAa,CAAC;AACrB,OAAO,aAAa,CAAC;AACrB,OAAO,eAAe,CAAC;AACvB,OAAO,YAAY,CAAC;AACpB,OAAO,aAAa,CAAC;AACrB,OAAO,iBAAiB,CAAC;AACzB,OAAO,cAAc,CAAC;AACtB,OAAO,eAAe,CAAC;AACvB,OAAO,cAAc,CAAC;AACtB,OAAO,cAAc,CAAC;AACtB,OAAO,cAAc,CAAC;AACtB,OAAO,WAAW,CAAC;AACnB,OAAO,gBAAgB,CAAC;AACxB,OAAO,aAAa,CAAC;AACrB,OAAO,aAAa,CAAC;AACrB,OAAO,WAAW,CAAC;AACnB,OAAO,aAAa,CAAC;AACrB,OAAO,iBAAiB,CAAC;AACzB,OAAO,oBAAoB,CAAC;AAC5B,OAAO,eAAe,CAAC;AACvB,OAAO,YAAY,CAAC;AACpB,OAAO,gBAAgB,CAAC;AACxB,OAAO,cAAc,CAAC;AACtB,OAAO,aAAa,CAAC;AACrB,OAAO,iBAAiB,CAAC;AACzB,OAAO,YAAY,CAAC;AACpB,OAAO,eAAe,CAAC;AACvB,OAAO,eAAe,CAAC;AACvB,OAAO,eAAe,CAAC;AACvB,OAAO,cAAc,CAAC;AACtB,OAAO,kBAAkB,CAAC;AAC1B,OAAO,eAAe,CAAC;AACvB,OAAO,cAAc,CAAC;AACtB,OAAO,gBAAgB,CAAC;AACxB,OAAO,cAAc,CAAC;AACtB,OAAO,cAAc,CAAC;AACtB,OAAO,cAAc,CAAC;AACtB,OAAO,eAAe,CAAC;AACvB,OAAO,aAAa,CAAC;AACrB,OAAO,eAAe,CAAC;AACvB,OAAO,aAAa,CAAC;AACrB,OAAO,aAAa,CAAC;AACrB,OAAO,mBAAmB,CAAC;AAQ3B,qCAAqC;AACrC,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,aAAa,EACb,WAAW,EACX,aAAa,EACb,OAAO,EACP,OAAO,EACP,SAAS,EACT,YAAY,GACb,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* outcomes.classify_issues — deterministic disposition classification for
|
|
3
|
+
* the outcome-ingester recipe, replacing an earlier LLM-judge agent step.
|
|
4
|
+
*
|
|
5
|
+
* The agent step read raw issue JSON and freehanded a disposition +
|
|
6
|
+
* checkedAt epoch per-issue. In production cron runs it flipped the SAME
|
|
7
|
+
* closed/COMPLETED issues between "confirmed" and "unknown" on alternating
|
|
8
|
+
* fires, and hallucinated checkedAt values spanning 2023–2027. Trust-replay
|
|
9
|
+
* reads this log to decide good:true/false, so non-deterministic
|
|
10
|
+
* classification directly corrupts the trust signal.
|
|
11
|
+
*
|
|
12
|
+
* This tool has no LLM in the loop: classifyIssueDisposition() is a pure
|
|
13
|
+
* function of state/labels, and checkedAt is the real clock.
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* outcomes.classify_issues — deterministic disposition classification for
|
|
3
|
+
* the outcome-ingester recipe, replacing an earlier LLM-judge agent step.
|
|
4
|
+
*
|
|
5
|
+
* The agent step read raw issue JSON and freehanded a disposition +
|
|
6
|
+
* checkedAt epoch per-issue. In production cron runs it flipped the SAME
|
|
7
|
+
* closed/COMPLETED issues between "confirmed" and "unknown" on alternating
|
|
8
|
+
* fires, and hallucinated checkedAt values spanning 2023–2027. Trust-replay
|
|
9
|
+
* reads this log to decide good:true/false, so non-deterministic
|
|
10
|
+
* classification directly corrupts the trust signal.
|
|
11
|
+
*
|
|
12
|
+
* This tool has no LLM in the loop: classifyIssueDisposition() is a pure
|
|
13
|
+
* function of state/labels, and checkedAt is the real clock.
|
|
14
|
+
*/
|
|
15
|
+
import os from "node:os";
|
|
16
|
+
import path from "node:path";
|
|
17
|
+
import { assertWriteAllowed } from "../../featureFlags.js";
|
|
18
|
+
import { classifyIssueDisposition, OutcomeStore, } from "../../workers/outcomeStore.js";
|
|
19
|
+
import { registerTool } from "../toolRegistry.js";
|
|
20
|
+
registerTool({
|
|
21
|
+
id: "outcomes.classify_issues",
|
|
22
|
+
namespace: "outcomes",
|
|
23
|
+
description: "Deterministically classify a batch of GitHub issues (from github.list_issues / github.search_issues output) as confirmed/junk/unknown and persist to the outcome-log for trust-replay. No LLM judgment — pure function of state/stateReason/labels.",
|
|
24
|
+
paramsSchema: {
|
|
25
|
+
type: "object",
|
|
26
|
+
properties: {
|
|
27
|
+
issues: {
|
|
28
|
+
type: "string",
|
|
29
|
+
description: "JSON array of issue objects (each with url/state/stateReason/labels), typically piped in as {{issues}} from a prior list_issues/search_issues step.",
|
|
30
|
+
},
|
|
31
|
+
recipeName: {
|
|
32
|
+
type: "string",
|
|
33
|
+
description: "Optional recipe name to stamp on each outcome record.",
|
|
34
|
+
},
|
|
35
|
+
workerClass: {
|
|
36
|
+
type: "string",
|
|
37
|
+
description: "Optional worker action-class to stamp on each record.",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
required: ["issues"],
|
|
41
|
+
},
|
|
42
|
+
outputSchema: {
|
|
43
|
+
type: "object",
|
|
44
|
+
properties: {
|
|
45
|
+
count: { type: "number" },
|
|
46
|
+
confirmed: { type: "number" },
|
|
47
|
+
junk: { type: "number" },
|
|
48
|
+
unknown: { type: "number" },
|
|
49
|
+
error: { type: "string" },
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
riskDefault: "medium",
|
|
53
|
+
isWrite: true,
|
|
54
|
+
execute: async ({ params }) => {
|
|
55
|
+
assertWriteAllowed("outcomes.classify_issues");
|
|
56
|
+
let issues;
|
|
57
|
+
try {
|
|
58
|
+
const raw = params.issues ? String(params.issues) : "[]";
|
|
59
|
+
const parsed = JSON.parse(raw);
|
|
60
|
+
issues = Array.isArray(parsed) ? parsed : [];
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
return JSON.stringify({
|
|
64
|
+
count: 0,
|
|
65
|
+
confirmed: 0,
|
|
66
|
+
junk: 0,
|
|
67
|
+
unknown: 0,
|
|
68
|
+
error: `outcomes.classify_issues: invalid issues JSON — ${err instanceof Error ? err.message : String(err)}`,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
const patchworkDir = process.env.PATCHWORK_HOME ?? path.join(os.homedir(), ".patchwork");
|
|
72
|
+
const store = new OutcomeStore(patchworkDir);
|
|
73
|
+
const recipeName = params.recipeName
|
|
74
|
+
? String(params.recipeName)
|
|
75
|
+
: undefined;
|
|
76
|
+
const workerClass = params.workerClass
|
|
77
|
+
? String(params.workerClass)
|
|
78
|
+
: undefined;
|
|
79
|
+
let confirmed = 0;
|
|
80
|
+
let junk = 0;
|
|
81
|
+
let unknown = 0;
|
|
82
|
+
const checkedAt = Date.now();
|
|
83
|
+
for (const issue of issues) {
|
|
84
|
+
const issueUrl = issue.url ?? issue.html_url;
|
|
85
|
+
if (!issueUrl)
|
|
86
|
+
continue;
|
|
87
|
+
const disposition = classifyIssueDisposition({
|
|
88
|
+
state: issue.state,
|
|
89
|
+
state_reason: issue.stateReason,
|
|
90
|
+
labels: issue.labels,
|
|
91
|
+
});
|
|
92
|
+
if (disposition === "confirmed")
|
|
93
|
+
confirmed++;
|
|
94
|
+
else if (disposition === "junk")
|
|
95
|
+
junk++;
|
|
96
|
+
else
|
|
97
|
+
unknown++;
|
|
98
|
+
store.upsert({
|
|
99
|
+
issueUrl,
|
|
100
|
+
disposition,
|
|
101
|
+
checkedAt,
|
|
102
|
+
...(recipeName && { recipeName }),
|
|
103
|
+
...(workerClass && { workerClass }),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return JSON.stringify({
|
|
107
|
+
count: confirmed + junk + unknown,
|
|
108
|
+
confirmed,
|
|
109
|
+
junk,
|
|
110
|
+
unknown,
|
|
111
|
+
});
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
//# sourceMappingURL=outcomes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outcomes.js","sourceRoot":"","sources":["../../../src/recipes/tools/outcomes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EACL,wBAAwB,EACxB,YAAY,GACb,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAUlD,YAAY,CAAC;IACX,EAAE,EAAE,0BAA0B;IAC9B,SAAS,EAAE,UAAU;IACrB,WAAW,EACT,qPAAqP;IACvP,YAAY,EAAE;QACZ,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,qJAAqJ;aACxJ;YACD,UAAU,EAAE;gBACV,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,uDAAuD;aACrE;YACD,WAAW,EAAE;gBACX,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,uDAAuD;aACrE;SACF;QACD,QAAQ,EAAE,CAAC,QAAQ,CAAC;KACrB;IACD,YAAY,EAAE;QACZ,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC7B,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACxB,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC3B,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;SAC1B;KACF;IACD,WAAW,EAAE,QAAQ;IACrB,OAAO,EAAE,IAAI;IACb,OAAO,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;QAC5B,kBAAkB,CAAC,0BAA0B,CAAC,CAAC;QAC/C,IAAI,MAAuB,CAAC;QAC5B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YACzD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC/B,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,SAAS,CAAC;gBACpB,KAAK,EAAE,CAAC;gBACR,SAAS,EAAE,CAAC;gBACZ,IAAI,EAAE,CAAC;gBACP,OAAO,EAAE,CAAC;gBACV,KAAK,EAAE,mDAAmD,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;aAC7G,CAAC,CAAC;QACL,CAAC;QAED,MAAM,YAAY,GAChB,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;QACtE,MAAM,KAAK,GAAG,IAAI,YAAY,CAAC,YAAY,CAAC,CAAC;QAC7C,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU;YAClC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC;YAC3B,CAAC,CAAC,SAAS,CAAC;QACd,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW;YACpC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC;YAC5B,CAAC,CAAC,SAAS,CAAC;QAEd,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,IAAI,KAAK,CAAC,QAAQ,CAAC;YAC7C,IAAI,CAAC,QAAQ;gBAAE,SAAS;YACxB,MAAM,WAAW,GAAG,wBAAwB,CAAC;gBAC3C,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,YAAY,EAAE,KAAK,CAAC,WAAW;gBAC/B,MAAM,EAAE,KAAK,CAAC,MAAM;aACrB,CAAC,CAAC;YACH,IAAI,WAAW,KAAK,WAAW;gBAAE,SAAS,EAAE,CAAC;iBACxC,IAAI,WAAW,KAAK,MAAM;gBAAE,IAAI,EAAE,CAAC;;gBACnC,OAAO,EAAE,CAAC;YACf,KAAK,CAAC,MAAM,CAAC;gBACX,QAAQ;gBACR,WAAW;gBACX,SAAS;gBACT,GAAG,CAAC,UAAU,IAAI,EAAE,UAAU,EAAE,CAAC;gBACjC,GAAG,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,CAAC;aACpC,CAAC,CAAC;QACL,CAAC;QAED,OAAO,IAAI,CAAC,SAAS,CAAC;YACpB,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO;YACjC,SAAS;YACT,IAAI;YACJ,OAAO;SACR,CAAC,CAAC;IACL,CAAC;CACF,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "patchwork-os",
|
|
3
|
-
"version": "0.2.0-beta.13.canary.
|
|
3
|
+
"version": "0.2.0-beta.13.canary.294",
|
|
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",
|
|
@@ -36,33 +36,16 @@ steps:
|
|
|
36
36
|
max: 50
|
|
37
37
|
into: issues
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
Output ONLY raw JSONL — one JSON object per line, no markdown, no explanation:
|
|
54
|
-
{"issueUrl":"<url>","disposition":"<confirmed|junk|unknown>","checkedAt":<epoch_ms>}
|
|
55
|
-
|
|
56
|
-
Rules:
|
|
57
|
-
- issueUrl is the issue's html_url field
|
|
58
|
-
- checkedAt is the current Unix epoch in milliseconds (approximate — use the issue's updated_at epoch if current time unavailable)
|
|
59
|
-
- Emit one line per issue. No trailing commas, no array brackets, no other text.
|
|
60
|
-
- If there are no issues to classify, output nothing (empty string).
|
|
61
|
-
model: claude-haiku-4-5-20251001
|
|
62
|
-
into: outcome_lines
|
|
63
|
-
|
|
64
|
-
- id: persist
|
|
65
|
-
when: "{{outcome_lines}}"
|
|
66
|
-
tool: file.append
|
|
67
|
-
path: ~/.patchwork/outcome-log.jsonl
|
|
68
|
-
content: "{{outcome_lines}}\n"
|
|
39
|
+
# Deterministic — no LLM in the classification path. An earlier LLM-judge
|
|
40
|
+
# agent step here was non-deterministic in production: the SAME closed/
|
|
41
|
+
# COMPLETED issues flipped between "confirmed" and "unknown" on alternating
|
|
42
|
+
# cron fires, and checkedAt was a hallucinated epoch spanning 2023-2027.
|
|
43
|
+
# Trust-replay reads this log to decide good:true/false, so the
|
|
44
|
+
# classification step MUST be pure data (state/stateReason/labels) plus
|
|
45
|
+
# the real clock, not a model's freehand judgment.
|
|
46
|
+
- id: classify_and_persist
|
|
47
|
+
when: "{{issues.json}}"
|
|
48
|
+
tool: outcomes.classify_issues
|
|
49
|
+
issues: "{{issues.json}}"
|
|
50
|
+
recipeName: outcome-ingester
|
|
51
|
+
into: outcome_result
|