patchwork-os 1.2.0-beta.2.canary.733 → 1.2.0-beta.2.canary.735
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/butler/outcomeIngester.d.ts +29 -1
- package/dist/butler/outcomeIngester.js +75 -0
- package/dist/butler/outcomeIngester.js.map +1 -1
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -1
- package/dist/privacy/boundaryReceiptLog.d.ts +51 -0
- package/dist/privacy/boundaryReceiptLog.js +31 -0
- package/dist/privacy/boundaryReceiptLog.js.map +1 -1
- package/dist/privacy/boundaryReceipts.d.ts +8 -0
- package/dist/privacy/boundaryReceipts.js +9 -0
- package/dist/privacy/boundaryReceipts.js.map +1 -1
- package/dist/recipes/chainedRunner.d.ts +12 -0
- package/dist/recipes/chainedRunner.js +7 -0
- package/dist/recipes/chainedRunner.js.map +1 -1
- package/dist/recipes/yamlRunner.js +37 -5
- package/dist/recipes/yamlRunner.js.map +1 -1
- package/package.json +1 -1
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
* rows the grader has not earned the right to be evidence.
|
|
40
40
|
*/
|
|
41
41
|
import { type GradeOptions, type ObservedErrandArtifact } from "./errandOutcomeGrader.js";
|
|
42
|
-
import { type ShadowSummary } from "./outcomeShadowLog.js";
|
|
42
|
+
import { type ShadowOutcomeRow, type ShadowSummary } from "./outcomeShadowLog.js";
|
|
43
43
|
/** One artifact to grade, plus the key it joins on. */
|
|
44
44
|
export interface ErrandObservation extends ObservedErrandArtifact {
|
|
45
45
|
/**
|
|
@@ -92,4 +92,32 @@ export declare function ingestErrandOutcomes(observations: readonly ErrandObserv
|
|
|
92
92
|
* next to the number, because the number's whole purpose is to be weighed
|
|
93
93
|
* against it.
|
|
94
94
|
*/
|
|
95
|
+
/**
|
|
96
|
+
* Render the individual graded rows for review.
|
|
97
|
+
*
|
|
98
|
+
* `formatShadowSummary` ends by telling the operator to check a sample against
|
|
99
|
+
* the real errands before promoting. Until this existed there was no way to do
|
|
100
|
+
* that: `butler shadow` printed the aggregate, `--json` printed the same
|
|
101
|
+
* aggregate, and the only caller of `readShadowRows` in the tree was
|
|
102
|
+
* `promoteShadowOutcomes` — the irreversible step. The single piece of code
|
|
103
|
+
* that read the rows was the one that acts on them.
|
|
104
|
+
*
|
|
105
|
+
* That is worth more than a missing flag because promotion is ONE-WAY: trust
|
|
106
|
+
* replay absorbs a folded row into a checkpoint that deleting the row does not
|
|
107
|
+
* undo. "Review before promoting" is the entire safety property of the shadow
|
|
108
|
+
* phase, and it was advice with no surface behind it.
|
|
109
|
+
*
|
|
110
|
+
* Rows that WOULD become evidence are printed first, because those are the only
|
|
111
|
+
* ones promotion acts on — sorting them under a wall of withheld `unknown` rows
|
|
112
|
+
* (8 of 9 on the real ledger when this was written) buries the decision.
|
|
113
|
+
*
|
|
114
|
+
* ## Output is operator data
|
|
115
|
+
*
|
|
116
|
+
* Same rule as `runstore compare` and `privacy receipts`: rows name real
|
|
117
|
+
* installed recipes and carry external record ids for the operator's own
|
|
118
|
+
* errands. Quote a measurement; never paste the rows.
|
|
119
|
+
*/
|
|
120
|
+
export declare function formatShadowRows(rows: readonly ShadowOutcomeRow[], opts?: {
|
|
121
|
+
limit?: number;
|
|
122
|
+
}): string;
|
|
95
123
|
export declare function formatShadowSummary(s: ShadowSummary): string;
|
|
@@ -99,6 +99,81 @@ export function ingestErrandOutcomes(observations, opts) {
|
|
|
99
99
|
* next to the number, because the number's whole purpose is to be weighed
|
|
100
100
|
* against it.
|
|
101
101
|
*/
|
|
102
|
+
/**
|
|
103
|
+
* Render the individual graded rows for review.
|
|
104
|
+
*
|
|
105
|
+
* `formatShadowSummary` ends by telling the operator to check a sample against
|
|
106
|
+
* the real errands before promoting. Until this existed there was no way to do
|
|
107
|
+
* that: `butler shadow` printed the aggregate, `--json` printed the same
|
|
108
|
+
* aggregate, and the only caller of `readShadowRows` in the tree was
|
|
109
|
+
* `promoteShadowOutcomes` — the irreversible step. The single piece of code
|
|
110
|
+
* that read the rows was the one that acts on them.
|
|
111
|
+
*
|
|
112
|
+
* That is worth more than a missing flag because promotion is ONE-WAY: trust
|
|
113
|
+
* replay absorbs a folded row into a checkpoint that deleting the row does not
|
|
114
|
+
* undo. "Review before promoting" is the entire safety property of the shadow
|
|
115
|
+
* phase, and it was advice with no surface behind it.
|
|
116
|
+
*
|
|
117
|
+
* Rows that WOULD become evidence are printed first, because those are the only
|
|
118
|
+
* ones promotion acts on — sorting them under a wall of withheld `unknown` rows
|
|
119
|
+
* (8 of 9 on the real ledger when this was written) buries the decision.
|
|
120
|
+
*
|
|
121
|
+
* ## Output is operator data
|
|
122
|
+
*
|
|
123
|
+
* Same rule as `runstore compare` and `privacy receipts`: rows name real
|
|
124
|
+
* installed recipes and carry external record ids for the operator's own
|
|
125
|
+
* errands. Quote a measurement; never paste the rows.
|
|
126
|
+
*/
|
|
127
|
+
export function formatShadowRows(rows, opts = {}) {
|
|
128
|
+
if (rows.length === 0) {
|
|
129
|
+
return [
|
|
130
|
+
"[butler-shadow] no graded rows to review.",
|
|
131
|
+
"",
|
|
132
|
+
" Nothing has been measured, so there is nothing to check and nothing",
|
|
133
|
+
" may be promoted.",
|
|
134
|
+
].join("\n");
|
|
135
|
+
}
|
|
136
|
+
// Evidence-bearing rows first, then most recently graded. Within the two
|
|
137
|
+
// groups the order is the ledger's own, so a reviewer can find a row again.
|
|
138
|
+
const ordered = [...rows].sort((a, b) => {
|
|
139
|
+
if (a.wouldCountAsEvidence !== b.wouldCountAsEvidence) {
|
|
140
|
+
return a.wouldCountAsEvidence ? -1 : 1;
|
|
141
|
+
}
|
|
142
|
+
return a.gradedAt - b.gradedAt;
|
|
143
|
+
});
|
|
144
|
+
const limit = opts.limit ?? ordered.length;
|
|
145
|
+
const shown = ordered.slice(0, Math.max(0, limit));
|
|
146
|
+
const hidden = ordered.length - shown.length;
|
|
147
|
+
const evidence = rows.filter((r) => r.wouldCountAsEvidence).length;
|
|
148
|
+
const lines = shown.map((r) => {
|
|
149
|
+
const when = new Date(r.gradedAt).toISOString().replace(".000Z", "Z");
|
|
150
|
+
// In words, not by colour or position alone — a reviewer piping this to a
|
|
151
|
+
// file must still be able to tell the two apart.
|
|
152
|
+
const mark = r.wouldCountAsEvidence
|
|
153
|
+
? "would become evidence"
|
|
154
|
+
: "withheld ";
|
|
155
|
+
// `recipe` is optional. No placeholder that reads like a name: attributing
|
|
156
|
+
// an errand to something that did not file it is worse than saying nothing.
|
|
157
|
+
const who = r.recipe ? ` recipe=${r.recipe}` : "";
|
|
158
|
+
return ` ${when} ${mark} ${r.disposition.padEnd(9)} ${r.reason.padEnd(18)} ${r.ref}${who}`;
|
|
159
|
+
});
|
|
160
|
+
return [
|
|
161
|
+
`[butler-shadow] ${rows.length} graded row(s); ${evidence} would become evidence.`,
|
|
162
|
+
"",
|
|
163
|
+
...lines,
|
|
164
|
+
...(hidden > 0
|
|
165
|
+
? [
|
|
166
|
+
"",
|
|
167
|
+
` ${hidden} more row(s) not shown — raise --rows to see them. A sample`,
|
|
168
|
+
" that looks representative because it was truncated is not one.",
|
|
169
|
+
]
|
|
170
|
+
: []),
|
|
171
|
+
"",
|
|
172
|
+
" These rows are OPERATOR DATA, not a diagnostic blob: they name real",
|
|
173
|
+
" installed recipes and carry external record ids. Quote a measurement if",
|
|
174
|
+
" you need to; never paste the rows into an issue, a PR or a fixture.",
|
|
175
|
+
].join("\n");
|
|
176
|
+
}
|
|
102
177
|
export function formatShadowSummary(s) {
|
|
103
178
|
if (s.total === 0) {
|
|
104
179
|
return [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outcomeIngester.js","sourceRoot":"","sources":["../../src/butler/outcomeIngester.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,EAEL,kBAAkB,GAEnB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,cAAc,
|
|
1
|
+
{"version":3,"file":"outcomeIngester.js","sourceRoot":"","sources":["../../src/butler/outcomeIngester.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -4638,6 +4638,38 @@ if (process.argv[2] === "butler") {
|
|
|
4638
4638
|
const { formatShadowSummary, ingestErrandOutcomes } = await import("./butler/outcomeIngester.js");
|
|
4639
4639
|
const { summariseShadowLog } = await import("./butler/outcomeShadowLog.js");
|
|
4640
4640
|
if (args[0] === "shadow") {
|
|
4641
|
+
// `--rows` is the review surface the summary's own closing advice
|
|
4642
|
+
// requires ("check a sample against the real errands they describe").
|
|
4643
|
+
// Before it existed the only caller of `readShadowRows` was
|
|
4644
|
+
// `promoteShadowOutcomes` — the irreversible step — so the one piece of
|
|
4645
|
+
// code that read the rows was the one that acts on them.
|
|
4646
|
+
const rowsIdx = args.findIndex((a) => a === "--rows");
|
|
4647
|
+
if (rowsIdx !== -1) {
|
|
4648
|
+
const { formatShadowRows } = await import("./butler/outcomeIngester.js");
|
|
4649
|
+
const { readShadowRows } = await import("./butler/outcomeShadowLog.js");
|
|
4650
|
+
const rows = readShadowRows();
|
|
4651
|
+
// `--rows` alone shows every row; `--rows N` caps it. A bad N is
|
|
4652
|
+
// rejected rather than silently treated as "all", which would show
|
|
4653
|
+
// more of the operator's data than they asked for.
|
|
4654
|
+
const raw = args[rowsIdx + 1];
|
|
4655
|
+
let limit;
|
|
4656
|
+
if (raw !== undefined && !raw.startsWith("--")) {
|
|
4657
|
+
const n = Number(raw);
|
|
4658
|
+
if (!Number.isInteger(n) || n <= 0) {
|
|
4659
|
+
process.stderr.write(`[butler-shadow] --rows expects a positive integer, got "${raw}"\n`);
|
|
4660
|
+
process.exit(1);
|
|
4661
|
+
}
|
|
4662
|
+
limit = n;
|
|
4663
|
+
}
|
|
4664
|
+
if (args.includes("--json")) {
|
|
4665
|
+
const out = limit === undefined ? rows : rows.slice(0, limit);
|
|
4666
|
+
process.stdout.write(`${JSON.stringify(out, null, 2)}\n`);
|
|
4667
|
+
}
|
|
4668
|
+
else {
|
|
4669
|
+
process.stdout.write(`${formatShadowRows(rows, limit === undefined ? {} : { limit })}\n`);
|
|
4670
|
+
}
|
|
4671
|
+
process.exit(0);
|
|
4672
|
+
}
|
|
4641
4673
|
const summary = summariseShadowLog();
|
|
4642
4674
|
if (args.includes("--json")) {
|
|
4643
4675
|
process.stdout.write(`${JSON.stringify(summary, null, 2)}\n`);
|