patchwork-os 1.2.0-beta.2.canary.755 → 1.2.0-beta.2.canary.756
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/index.js
CHANGED
|
@@ -220,6 +220,10 @@ const KNOWN_SUBCOMMANDS = [
|
|
|
220
220
|
"reject",
|
|
221
221
|
"gate",
|
|
222
222
|
"outcomes",
|
|
223
|
+
// Raw PR-event ledger. Absent from this array a verb IS dispatched but the
|
|
224
|
+
// suggester calls it unknown — the exact trap the `tools` comment below
|
|
225
|
+
// records, and audit-cli-commands.mjs reads this array as ground truth.
|
|
226
|
+
"pr-outcomes",
|
|
223
227
|
"butler",
|
|
224
228
|
"privacy",
|
|
225
229
|
"doctor",
|
|
@@ -304,6 +308,7 @@ if (process.argv[2] === "--help" ||
|
|
|
304
308
|
`Diagnose\n` +
|
|
305
309
|
` halts [--window 1h|24h|overnight|7d] Morning summary of recent recipe halts\n` +
|
|
306
310
|
` judgments [--window ...] [--recipe N] Recent judge-step verdicts across runs\n` +
|
|
311
|
+
` pr-outcomes <collect|show> Record raw PR events so trust can be derived later\n` +
|
|
307
312
|
` privacy destinations Where prompts may go, and which leave this machine\n` +
|
|
308
313
|
` privacy receipts [--since-days N] What the LIVE information boundary actually decided\n` +
|
|
309
314
|
` privacy shadow [--since-days N] What a candidate privacy policy would have stopped\n` +
|
|
@@ -4161,6 +4166,147 @@ if (process.argv[2] === "shadow-scan") {
|
|
|
4161
4166
|
//
|
|
4162
4167
|
// Reports DENOMINATORS. It is not the reader, and building the reader is
|
|
4163
4168
|
// exactly what the spine's own rule defers until there is evidence to read.
|
|
4169
|
+
// --- pr-outcomes (maintenance roadmap, phase 1) ---
|
|
4170
|
+
//
|
|
4171
|
+
// Built before the workers that will read it, deliberately, and it is the one
|
|
4172
|
+
// item on that roadmap where that is correct: outcome history accrues only
|
|
4173
|
+
// with wall-clock time, and a day not recorded is a day that cannot be
|
|
4174
|
+
// recovered. Everything else can be built whenever.
|
|
4175
|
+
if (process.argv[2] === "pr-outcomes") {
|
|
4176
|
+
const args = process.argv.slice(3);
|
|
4177
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
4178
|
+
process.stdout.write("Usage: patchwork pr-outcomes <collect|show> [--repo owner/name] [--limit N] [--json]\n\n" +
|
|
4179
|
+
"Record raw pull-request events so trust can be derived LATER from evidence\n" +
|
|
4180
|
+
"rather than asserted now. Each row is what the API said at one moment; run\n" +
|
|
4181
|
+
"`collect` regularly and trajectories appear. Run it once and you get final\n" +
|
|
4182
|
+
"states only, which the summary says rather than implying otherwise.\n\n" +
|
|
4183
|
+
" collect query the repo and append anything that changed\n" +
|
|
4184
|
+
" show summarise what the ledger already holds\n\n" +
|
|
4185
|
+
"No score is derived. Whether a worker opened a PR is OMITTED when no roster\n" +
|
|
4186
|
+
"is configured, never recorded as false: unknown and no are different facts.\n\n" +
|
|
4187
|
+
"Rows name real pull requests and authors, so they are operator data like the\n" +
|
|
4188
|
+
"other ledgers - quote a measurement, never paste the rows.\n");
|
|
4189
|
+
process.exit(0);
|
|
4190
|
+
}
|
|
4191
|
+
(async () => {
|
|
4192
|
+
const { readFileSync, appendFileSync, existsSync } = await import("node:fs");
|
|
4193
|
+
const { join } = await import("node:path");
|
|
4194
|
+
const { patchworkPath } = await import("./patchworkHome.js");
|
|
4195
|
+
const { execFileSync } = await import("node:child_process");
|
|
4196
|
+
const { toObservation, dedupeAgainst, summarise, formatLedgerSummary } = await import("./maintenance/prOutcomeLedger.js");
|
|
4197
|
+
const file = join(patchworkPath(), "pr_outcomes.jsonl");
|
|
4198
|
+
const read = () => {
|
|
4199
|
+
if (!existsSync(file))
|
|
4200
|
+
return [];
|
|
4201
|
+
const out = [];
|
|
4202
|
+
for (const line of readFileSync(file, "utf-8").split("\n")) {
|
|
4203
|
+
if (line.trim() === "")
|
|
4204
|
+
continue;
|
|
4205
|
+
try {
|
|
4206
|
+
out.push(JSON.parse(line));
|
|
4207
|
+
}
|
|
4208
|
+
catch {
|
|
4209
|
+
// A corrupt line is skipped, never repaired: rewriting an
|
|
4210
|
+
// append-only evidence file to make it parse is how evidence
|
|
4211
|
+
// quietly becomes fiction.
|
|
4212
|
+
}
|
|
4213
|
+
}
|
|
4214
|
+
return out;
|
|
4215
|
+
};
|
|
4216
|
+
const sub = args[0] ?? "show";
|
|
4217
|
+
if (sub === "show") {
|
|
4218
|
+
const rows = read();
|
|
4219
|
+
const s = summarise(rows);
|
|
4220
|
+
process.stdout.write(args.includes("--json")
|
|
4221
|
+
? `${JSON.stringify(s, null, 2)}\n`
|
|
4222
|
+
: `${formatLedgerSummary(s)}\n`);
|
|
4223
|
+
// Always 0. An empty ledger on day one is a true and expected state.
|
|
4224
|
+
process.exit(0);
|
|
4225
|
+
}
|
|
4226
|
+
if (sub === "collect") {
|
|
4227
|
+
const repoIdx = args.indexOf("--repo");
|
|
4228
|
+
const limIdx = args.indexOf("--limit");
|
|
4229
|
+
const limit = limIdx >= 0 ? Number(args[limIdx + 1]) : 100;
|
|
4230
|
+
let repo = repoIdx >= 0 ? args[repoIdx + 1] : undefined;
|
|
4231
|
+
if (repo === undefined) {
|
|
4232
|
+
try {
|
|
4233
|
+
repo = execFileSync("gh", ["repo", "view", "--json", "nameWithOwner", "-q", ".nameWithOwner"], {
|
|
4234
|
+
encoding: "utf-8",
|
|
4235
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
4236
|
+
}).trim();
|
|
4237
|
+
}
|
|
4238
|
+
catch {
|
|
4239
|
+
process.stderr.write("[pr-outcomes] could not determine the repository. Pass --repo owner/name.\n");
|
|
4240
|
+
process.exit(2);
|
|
4241
|
+
}
|
|
4242
|
+
}
|
|
4243
|
+
let payload = "";
|
|
4244
|
+
try {
|
|
4245
|
+
payload = execFileSync("gh", [
|
|
4246
|
+
"pr",
|
|
4247
|
+
"list",
|
|
4248
|
+
"--repo",
|
|
4249
|
+
repo,
|
|
4250
|
+
"--state",
|
|
4251
|
+
"all",
|
|
4252
|
+
"--limit",
|
|
4253
|
+
String(Number.isFinite(limit) ? limit : 100),
|
|
4254
|
+
"--json",
|
|
4255
|
+
"number,state,createdAt,mergedAt,closedAt,additions,deletions,changedFiles,author,mergeCommit",
|
|
4256
|
+
], {
|
|
4257
|
+
encoding: "utf-8",
|
|
4258
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
4259
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
4260
|
+
});
|
|
4261
|
+
}
|
|
4262
|
+
catch (err) {
|
|
4263
|
+
// Fail-soft and LOUD. A collector that silently reports success on a
|
|
4264
|
+
// failed query would leave a gap indistinguishable from a quiet week.
|
|
4265
|
+
process.stderr.write(`[pr-outcomes] the GitHub query failed, so NOTHING was recorded: ${err instanceof Error ? err.message.split("\n")[0] : String(err)}\n`);
|
|
4266
|
+
process.exit(1);
|
|
4267
|
+
}
|
|
4268
|
+
let parsed;
|
|
4269
|
+
try {
|
|
4270
|
+
parsed = JSON.parse(payload);
|
|
4271
|
+
}
|
|
4272
|
+
catch {
|
|
4273
|
+
process.stderr.write("[pr-outcomes] the GitHub response did not parse; nothing recorded.\n");
|
|
4274
|
+
process.exit(1);
|
|
4275
|
+
}
|
|
4276
|
+
const observedAt = new Date().toISOString();
|
|
4277
|
+
const incoming = [];
|
|
4278
|
+
let unusable = 0;
|
|
4279
|
+
for (const r of Array.isArray(parsed) ? parsed : []) {
|
|
4280
|
+
const o = toObservation(r, {
|
|
4281
|
+
repo: repo,
|
|
4282
|
+
observedAt,
|
|
4283
|
+
});
|
|
4284
|
+
if (o === null)
|
|
4285
|
+
unusable++;
|
|
4286
|
+
else
|
|
4287
|
+
incoming.push(o);
|
|
4288
|
+
}
|
|
4289
|
+
const existing = read();
|
|
4290
|
+
const { toAppend, unchanged, firstSighting } = dedupeAgainst(existing, incoming);
|
|
4291
|
+
for (const o of toAppend)
|
|
4292
|
+
appendFileSync(file, `${JSON.stringify(o)}\n`, { mode: 0o600 });
|
|
4293
|
+
process.stdout.write(`[pr-outcomes] ${repo}: ${incoming.length} pull request(s) queried\n` +
|
|
4294
|
+
` ${toAppend.length} appended (${firstSighting} seen for the first time)\n` +
|
|
4295
|
+
` ${unchanged} unchanged since the last collection\n` +
|
|
4296
|
+
(unusable > 0
|
|
4297
|
+
? ` ${unusable} row(s) lacked the fields needed to key them and were DROPPED\n`
|
|
4298
|
+
: "") +
|
|
4299
|
+
(firstSighting === incoming.length && existing.length === 0
|
|
4300
|
+
? "\n Every row is a first sighting, so this is a snapshot of final\n" +
|
|
4301
|
+
" states rather than a history. Trajectories begin from the next\n" +
|
|
4302
|
+
" collection onward - that part cannot be backfilled.\n"
|
|
4303
|
+
: ""));
|
|
4304
|
+
process.exit(0);
|
|
4305
|
+
}
|
|
4306
|
+
process.stderr.write("Usage: patchwork pr-outcomes <collect|show>\n");
|
|
4307
|
+
process.exit(2);
|
|
4308
|
+
})();
|
|
4309
|
+
}
|
|
4164
4310
|
if (process.argv[2] === "evidence") {
|
|
4165
4311
|
const args = process.argv.slice(3);
|
|
4166
4312
|
if (args.includes("--help") || args.includes("-h")) {
|