patchwork-os 1.2.0-beta.2.canary.764 → 1.2.0-beta.2.canary.765

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.
@@ -36,7 +36,6 @@
36
36
  * Rows carry `rv: 1` so a later schema change is distinguishable from an old
37
37
  * row, for the same reason the gate ledger does.
38
38
  */
39
- /** Schema version. A reader must be able to tell an old row from a new one. */
40
39
  export declare const PR_OBSERVATION_RV = 1;
41
40
  export type PrState = "OPEN" | "MERGED" | "CLOSED";
42
41
  /** One pull request as the API described it at one moment. */
@@ -161,3 +160,15 @@ export declare function resolveRepoSlug(run: () => {
161
160
  ok: false;
162
161
  stderr: string;
163
162
  }): RepoResolution;
163
+ /**
164
+ * Read the observation ledger. Missing file ⇒ empty, which is a true state on
165
+ * day one and not an error.
166
+ *
167
+ * Extracted from the `pr-outcomes show` handler so `patchwork sweep` reads the
168
+ * ledger through the same parser the verb uses — a second reader would drift,
169
+ * and a drifting row count is indistinguishable from a quiet week.
170
+ *
171
+ * A corrupt line is skipped, never repaired: rewriting an append-only evidence
172
+ * file to make it parse is how evidence quietly becomes fiction.
173
+ */
174
+ export declare function readObservations(file: string): PrObservation[];
@@ -37,6 +37,7 @@
37
37
  * row, for the same reason the gate ledger does.
38
38
  */
39
39
  /** Schema version. A reader must be able to tell an old row from a new one. */
40
+ import * as nodeFs from "node:fs";
40
41
  export const PR_OBSERVATION_RV = 1;
41
42
  function num(v) {
42
43
  return typeof v === "number" && Number.isFinite(v) ? v : null;
@@ -218,4 +219,31 @@ export function resolveRepoSlug(run) {
218
219
  }
219
220
  return { repo: slug };
220
221
  }
222
+ /**
223
+ * Read the observation ledger. Missing file ⇒ empty, which is a true state on
224
+ * day one and not an error.
225
+ *
226
+ * Extracted from the `pr-outcomes show` handler so `patchwork sweep` reads the
227
+ * ledger through the same parser the verb uses — a second reader would drift,
228
+ * and a drifting row count is indistinguishable from a quiet week.
229
+ *
230
+ * A corrupt line is skipped, never repaired: rewriting an append-only evidence
231
+ * file to make it parse is how evidence quietly becomes fiction.
232
+ */
233
+ export function readObservations(file) {
234
+ if (!nodeFs.existsSync(file))
235
+ return [];
236
+ const out = [];
237
+ for (const line of nodeFs.readFileSync(file, "utf-8").split("\n")) {
238
+ if (line.trim() === "")
239
+ continue;
240
+ try {
241
+ out.push(JSON.parse(line));
242
+ }
243
+ catch {
244
+ // skipped, never repaired
245
+ }
246
+ }
247
+ return out;
248
+ }
221
249
  //# sourceMappingURL=prOutcomeLedger.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"prOutcomeLedger.js","sourceRoot":"","sources":["../../src/maintenance/prOutcomeLedger.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prOutcomeLedger.js","sourceRoot":"","sources":["../../src/maintenance/prOutcomeLedger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * `patchwork sweep` — what CHANGED since the last sweep?
3
+ *
4
+ * Five read-only verbs already answer "what is true now": `doctor`,
5
+ * `workers validate`, `evidence`, `privacy undeclared`, `pr-outcomes show`.
6
+ * Running them by hand answers each question in isolation and answers none of
7
+ * the question an operator actually has on a Monday, which is *what moved*.
8
+ * A denominator that has not shifted in three weeks and a gate that flipped
9
+ * yesterday look identical when every verb is read fresh.
10
+ *
11
+ * ## It is a REPORT with two gates, not five
12
+ *
13
+ * Only two readings here are gates: is the running code the installed code, and
14
+ * does every worker manifest actually govern something. Those are binary, they
15
+ * are currently green, and a flip is unambiguously a regression — so a flip
16
+ * exits 1.
17
+ *
18
+ * Everything else is DRIFT and never fails the command. The evidence-spine
19
+ * ratios fall by construction as ledgers accrue rows faster than runs earn
20
+ * correlation ids; an undeclared agent step is the documented fail-soft default
21
+ * of ADR-0021, not a fault. Wiring those to an exit code would make `sweep`
22
+ * permanently red, and a permanently-red gate is exactly how a real warning
23
+ * gets ignored — the failure this repo has now recorded several times.
24
+ *
25
+ * ## A first run is a BASELINE, never "no changes"
26
+ *
27
+ * With no prior snapshot there is nothing to diff, and saying "nothing changed"
28
+ * would be a claim about a week nobody observed. That state is named
29
+ * `baseline`, printed as such, and carried in the JSON, for the same reason
30
+ * `evidence` reports an absent ledger as ABSENT rather than as zero rows.
31
+ *
32
+ * ## The snapshot holds COUNTS ONLY
33
+ *
34
+ * This is a constraint, not a formality. Two of the five inputs return operator
35
+ * data — `privacy undeclared` names real installed recipes, `doctor` carries
36
+ * workspace paths — and a snapshot that accumulated them would be a brand-new
37
+ * operator-data ledger created by a diagnostic, growing weekly, unnoticed
38
+ * because nobody thinks of a health check as a place secrets collect. So the
39
+ * reading is reduced to integers and booleans at the boundary, and a test
40
+ * asserts the serialised snapshot contains no path- or name-shaped string.
41
+ *
42
+ * `rv` marks the snapshot schema. A later reading with a new counter must stay
43
+ * distinguishable from an old reading that never had one — absence is a fact
44
+ * here as it is in the gate decision log, and is never backfilled.
45
+ */
46
+ /** Snapshot schema version. Bump when a counter's MEANING changes. */
47
+ export declare const SWEEP_RV = 1;
48
+ /** File the snapshots are appended to, relative to `$PATCHWORK_HOME`. */
49
+ export declare const SWEEP_LEDGER = "sweep_snapshots.jsonl";
50
+ /**
51
+ * One sweep reading, reduced to scalars.
52
+ *
53
+ * `gates` are the only fields that can produce a regression verdict. `counts` is
54
+ * a flat integer bag deliberately: adding a metric must not be a schema change
55
+ * that strands every prior snapshot, and a reader that finds a key missing
56
+ * learns "this sweep predates that counter" rather than "it was zero".
57
+ */
58
+ export interface SweepReading {
59
+ rv: number;
60
+ takenAt: number;
61
+ gates: Record<string, boolean>;
62
+ counts: Record<string, number>;
63
+ }
64
+ export interface GateDelta {
65
+ key: string;
66
+ before: boolean;
67
+ after: boolean;
68
+ /** true only for a true -> false flip. A false -> true flip is a recovery. */
69
+ regression: boolean;
70
+ }
71
+ export interface CountDelta {
72
+ key: string;
73
+ /** undefined when the prior snapshot predates this counter. */
74
+ before?: number;
75
+ after: number;
76
+ change: number;
77
+ }
78
+ export interface SweepDelta {
79
+ /** No prior snapshot existed. Never rendered as "nothing changed". */
80
+ baseline: boolean;
81
+ previousAt?: number;
82
+ gates: GateDelta[];
83
+ counts: CountDelta[];
84
+ /** Counters present in the prior reading and absent from this one. */
85
+ disappeared: string[];
86
+ /** True when any gate flipped true -> false. Drives the exit code. */
87
+ regressed: boolean;
88
+ }
89
+ /**
90
+ * Diff two readings. Pure — no clock, no filesystem, so a test can drive it.
91
+ */
92
+ export declare function diffReadings(previous: SweepReading | undefined, current: SweepReading): SweepDelta;
93
+ /** Last snapshot in the ledger, or undefined when there is none. */
94
+ export declare function readLastSnapshot(dir: string): SweepReading | undefined;
95
+ export declare function appendSnapshot(dir: string, reading: SweepReading): void;
96
+ export declare function formatSweep(reading: SweepReading, delta: SweepDelta, opts: {
97
+ wrote: boolean;
98
+ }): string;
@@ -0,0 +1,197 @@
1
+ /**
2
+ * `patchwork sweep` — what CHANGED since the last sweep?
3
+ *
4
+ * Five read-only verbs already answer "what is true now": `doctor`,
5
+ * `workers validate`, `evidence`, `privacy undeclared`, `pr-outcomes show`.
6
+ * Running them by hand answers each question in isolation and answers none of
7
+ * the question an operator actually has on a Monday, which is *what moved*.
8
+ * A denominator that has not shifted in three weeks and a gate that flipped
9
+ * yesterday look identical when every verb is read fresh.
10
+ *
11
+ * ## It is a REPORT with two gates, not five
12
+ *
13
+ * Only two readings here are gates: is the running code the installed code, and
14
+ * does every worker manifest actually govern something. Those are binary, they
15
+ * are currently green, and a flip is unambiguously a regression — so a flip
16
+ * exits 1.
17
+ *
18
+ * Everything else is DRIFT and never fails the command. The evidence-spine
19
+ * ratios fall by construction as ledgers accrue rows faster than runs earn
20
+ * correlation ids; an undeclared agent step is the documented fail-soft default
21
+ * of ADR-0021, not a fault. Wiring those to an exit code would make `sweep`
22
+ * permanently red, and a permanently-red gate is exactly how a real warning
23
+ * gets ignored — the failure this repo has now recorded several times.
24
+ *
25
+ * ## A first run is a BASELINE, never "no changes"
26
+ *
27
+ * With no prior snapshot there is nothing to diff, and saying "nothing changed"
28
+ * would be a claim about a week nobody observed. That state is named
29
+ * `baseline`, printed as such, and carried in the JSON, for the same reason
30
+ * `evidence` reports an absent ledger as ABSENT rather than as zero rows.
31
+ *
32
+ * ## The snapshot holds COUNTS ONLY
33
+ *
34
+ * This is a constraint, not a formality. Two of the five inputs return operator
35
+ * data — `privacy undeclared` names real installed recipes, `doctor` carries
36
+ * workspace paths — and a snapshot that accumulated them would be a brand-new
37
+ * operator-data ledger created by a diagnostic, growing weekly, unnoticed
38
+ * because nobody thinks of a health check as a place secrets collect. So the
39
+ * reading is reduced to integers and booleans at the boundary, and a test
40
+ * asserts the serialised snapshot contains no path- or name-shaped string.
41
+ *
42
+ * `rv` marks the snapshot schema. A later reading with a new counter must stay
43
+ * distinguishable from an old reading that never had one — absence is a fact
44
+ * here as it is in the gate decision log, and is never backfilled.
45
+ */
46
+ import { appendFileSync, existsSync, readFileSync } from "node:fs";
47
+ import path from "node:path";
48
+ /** Snapshot schema version. Bump when a counter's MEANING changes. */
49
+ export const SWEEP_RV = 1;
50
+ /** File the snapshots are appended to, relative to `$PATCHWORK_HOME`. */
51
+ export const SWEEP_LEDGER = "sweep_snapshots.jsonl";
52
+ /**
53
+ * Diff two readings. Pure — no clock, no filesystem, so a test can drive it.
54
+ */
55
+ export function diffReadings(previous, current) {
56
+ if (!previous) {
57
+ return {
58
+ baseline: true,
59
+ gates: [],
60
+ counts: [],
61
+ disappeared: [],
62
+ regressed: false,
63
+ };
64
+ }
65
+ const gates = [];
66
+ for (const [key, after] of Object.entries(current.gates)) {
67
+ const before = previous.gates[key];
68
+ // A gate the previous reading did not carry is not a flip. Treating a
69
+ // missing key as `false` would report every newly-added gate as a
70
+ // regression on its first sweep.
71
+ if (typeof before !== "boolean" || before === after)
72
+ continue;
73
+ gates.push({ key, before, after, regression: before && !after });
74
+ }
75
+ const counts = [];
76
+ for (const [key, after] of Object.entries(current.counts)) {
77
+ const before = previous.counts[key];
78
+ if (typeof before !== "number") {
79
+ counts.push({ key, after, change: after });
80
+ continue;
81
+ }
82
+ if (before === after)
83
+ continue;
84
+ counts.push({ key, before, after, change: after - before });
85
+ }
86
+ const disappeared = Object.keys(previous.counts).filter((k) => !(k in current.counts));
87
+ return {
88
+ baseline: false,
89
+ previousAt: previous.takenAt,
90
+ gates,
91
+ counts,
92
+ disappeared,
93
+ regressed: gates.some((g) => g.regression),
94
+ };
95
+ }
96
+ /** Last snapshot in the ledger, or undefined when there is none. */
97
+ export function readLastSnapshot(dir) {
98
+ const file = path.join(dir, SWEEP_LEDGER);
99
+ if (!existsSync(file))
100
+ return undefined;
101
+ let last;
102
+ for (const line of readFileSync(file, "utf-8").split("\n")) {
103
+ if (!line.trim())
104
+ continue;
105
+ try {
106
+ const row = JSON.parse(line);
107
+ // A row from a future schema is skipped rather than diffed against: its
108
+ // counters may share a name with a different meaning, and a delta built
109
+ // across that boundary would be a confident lie.
110
+ if (typeof row?.takenAt !== "number" || row.rv !== SWEEP_RV)
111
+ continue;
112
+ last = row;
113
+ }
114
+ catch {
115
+ // A corrupt line is skipped, never repaired. Rewriting an append-only
116
+ // evidence file to make it parse is how evidence stops being evidence.
117
+ }
118
+ }
119
+ return last;
120
+ }
121
+ export function appendSnapshot(dir, reading) {
122
+ appendFileSync(path.join(dir, SWEEP_LEDGER), `${JSON.stringify(reading)}\n`, {
123
+ mode: 0o600,
124
+ });
125
+ }
126
+ const CHECK = "✓";
127
+ export function formatSweep(reading, delta, opts) {
128
+ const L = [];
129
+ L.push("[sweep] what moved since the last sweep?");
130
+ L.push("");
131
+ L.push(" gates");
132
+ for (const [key, ok] of Object.entries(reading.gates)) {
133
+ L.push(` ${ok ? `${CHECK} ` : "FAIL"} ${key}`);
134
+ }
135
+ L.push("");
136
+ if (delta.baseline) {
137
+ // Never "no changes". Nothing was observed to change; there was nothing to
138
+ // compare against, and those are different facts.
139
+ L.push(" BASELINE — no prior snapshot, so nothing is compared here.");
140
+ L.push(" Run it again later and this section becomes the deltas.");
141
+ L.push("");
142
+ L.push(` ${Object.keys(reading.counts).length} counter(s) recorded:`);
143
+ for (const [key, v] of Object.entries(reading.counts)) {
144
+ L.push(` ${String(v).padStart(7)} ${key}`);
145
+ }
146
+ L.push("");
147
+ L.push(opts.wrote
148
+ ? ` Snapshot appended to ${SWEEP_LEDGER}.`
149
+ : " Not written (--no-write): the next sweep will still see no baseline.");
150
+ L.push("");
151
+ return `${L.join("\n")}\n`;
152
+ }
153
+ const since = delta.previousAt
154
+ ? new Date(delta.previousAt).toISOString()
155
+ : "unknown";
156
+ L.push(` since ${since}`);
157
+ L.push("");
158
+ if (delta.gates.length === 0) {
159
+ L.push(" No gate changed state.");
160
+ }
161
+ else {
162
+ for (const g of delta.gates) {
163
+ L.push(` ${g.regression ? "REGRESSED" : "recovered "} ${g.key}: ${g.before} -> ${g.after}`);
164
+ }
165
+ }
166
+ L.push("");
167
+ if (delta.counts.length === 0) {
168
+ L.push(" No counter moved.");
169
+ }
170
+ else {
171
+ L.push(" drift (reported, never fatal)");
172
+ for (const c of delta.counts) {
173
+ const before = c.before === undefined ? "new" : String(c.before);
174
+ const sign = c.change > 0 ? "+" : "";
175
+ L.push(` ${before.padStart(7)} -> ${String(c.after).padEnd(7)} ${sign}${c.change} ${c.key}`);
176
+ }
177
+ }
178
+ if (delta.disappeared.length > 0) {
179
+ L.push("");
180
+ L.push(` ${delta.disappeared.length} counter(s) the previous sweep had are absent now:`);
181
+ for (const k of delta.disappeared)
182
+ L.push(` ${k}`);
183
+ L.push(" A counter that stops being reported is not a counter that hit 0.");
184
+ }
185
+ L.push("");
186
+ if (delta.regressed) {
187
+ L.push(" A gate flipped from healthy to unhealthy. Exiting 1.");
188
+ }
189
+ else {
190
+ L.push(" No gate regressed. Drift above is information, not a failure.");
191
+ }
192
+ if (opts.wrote)
193
+ L.push(` Snapshot appended to ${SWEEP_LEDGER}.`);
194
+ L.push("");
195
+ return `${L.join("\n")}\n`;
196
+ }
197
+ //# sourceMappingURL=sweep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sweep.js","sourceRoot":"","sources":["../../src/maintenance/sweep.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Collect one `SweepReading` from the five read-only verbs.
3
+ *
4
+ * Kept apart from `sweep.ts` so the diff stays a pure function a test can drive.
5
+ * This half touches the filesystem and every subsystem; that half decides what a
6
+ * regression is.
7
+ *
8
+ * ## The reduction to scalars happens HERE, on purpose
9
+ *
10
+ * Two of these five inputs return operator data: `scanRecipeDir` names real
11
+ * installed recipes, and a bridge lock carries a workspace path. Reducing at the
12
+ * boundary — rather than storing the reports and projecting later — means there
13
+ * is no version of the snapshot that ever held them. A privacy rule enforced by
14
+ * a downstream formatter is one refactor away from being lost.
15
+ */
16
+ import { type SweepReading } from "./sweep.js";
17
+ export interface CollectOptions {
18
+ /** Ledger directory. Defaults to `$PATCHWORK_HOME`. */
19
+ dir?: string;
20
+ /** How many live bridges ought to be up. Absent ⇒ no expectation. */
21
+ expectRunning?: number;
22
+ /** ms epoch for the reading. Injected so a test is not at the mercy of a clock. */
23
+ now?: number;
24
+ }
25
+ /**
26
+ * Readings that could not be taken are OMITTED, never recorded as zero.
27
+ *
28
+ * A recipes directory that cannot be read and a recipes directory holding zero
29
+ * agent steps are different facts, and the diff already distinguishes an absent
30
+ * counter ("this sweep predates it, or could not take it") from a moved one.
31
+ * Writing a 0 would turn an unavailable reading into a dramatic-looking drop.
32
+ */
33
+ export declare function collectSweep(opts?: CollectOptions): SweepReading;
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Collect one `SweepReading` from the five read-only verbs.
3
+ *
4
+ * Kept apart from `sweep.ts` so the diff stays a pure function a test can drive.
5
+ * This half touches the filesystem and every subsystem; that half decides what a
6
+ * regression is.
7
+ *
8
+ * ## The reduction to scalars happens HERE, on purpose
9
+ *
10
+ * Two of these five inputs return operator data: `scanRecipeDir` names real
11
+ * installed recipes, and a bridge lock carries a workspace path. Reducing at the
12
+ * boundary — rather than storing the reports and projecting later — means there
13
+ * is no version of the snapshot that ever held them. A privacy rule enforced by
14
+ * a downstream formatter is one refactor away from being lost.
15
+ */
16
+ import path from "node:path";
17
+ import { assessDeploymentFreshness, discoverLocks, installedBuildTimeMs, } from "../deploymentFreshness.js";
18
+ import { evidenceCoverage } from "../evidenceCoverage.js";
19
+ import { patchworkHome } from "../patchworkHome.js";
20
+ import { scanRecipeDir } from "../privacy/undeclaredSteps.js";
21
+ import { defaultRecipesDir, defaultWorkersDir, validateWorkers, } from "../workers/workersCli.js";
22
+ import { readObservations, summarise } from "./prOutcomeLedger.js";
23
+ import { SWEEP_RV } from "./sweep.js";
24
+ /**
25
+ * Readings that could not be taken are OMITTED, never recorded as zero.
26
+ *
27
+ * A recipes directory that cannot be read and a recipes directory holding zero
28
+ * agent steps are different facts, and the diff already distinguishes an absent
29
+ * counter ("this sweep predates it, or could not take it") from a moved one.
30
+ * Writing a 0 would turn an unavailable reading into a dramatic-looking drop.
31
+ */
32
+ export function collectSweep(opts = {}) {
33
+ const dir = opts.dir ?? patchworkHome();
34
+ const counts = {};
35
+ const gates = {};
36
+ // --- gate 1: is the running code the installed code? ---
37
+ const buildTimeMs = installedBuildTimeMs();
38
+ if (buildTimeMs !== undefined) {
39
+ const fresh = assessDeploymentFreshness({
40
+ locks: discoverLocks(),
41
+ buildTimeMs,
42
+ isAlive: (pid) => {
43
+ try {
44
+ process.kill(pid, 0);
45
+ return true;
46
+ }
47
+ catch {
48
+ return false;
49
+ }
50
+ },
51
+ ...(opts.expectRunning !== undefined
52
+ ? { expectRunning: opts.expectRunning }
53
+ : {}),
54
+ });
55
+ gates["deployment"] = !fresh.unhealthy;
56
+ counts["bridges.running"] = fresh.running;
57
+ counts["bridges.findings"] = fresh.findings.length;
58
+ }
59
+ // --- gate 2: does every worker manifest actually govern something? ---
60
+ try {
61
+ const v = validateWorkers({
62
+ workersDir: defaultWorkersDir(),
63
+ recipesDir: defaultRecipesDir(),
64
+ });
65
+ gates["workers"] = v.healthy;
66
+ counts["workers.loaded"] = v.counts.loaded;
67
+ counts["workers.broken"] = v.counts.broken;
68
+ counts["workers.findings"] = v.findings.length;
69
+ }
70
+ catch {
71
+ // No workers directory is a legitimate install. Omit rather than zero.
72
+ }
73
+ // --- drift: evidence spine ---
74
+ const ev = evidenceCoverage(dir);
75
+ for (const l of ev.ledgers) {
76
+ // An absent ledger contributes NO counters. `evidence` reports absence as
77
+ // ABSENT rather than as `0 rows` for a reason — permission_exercises is
78
+ // absent because no standing permission has ever been granted, and that is
79
+ // correct, not a gap. A zero here would invite someone to go and fix it.
80
+ if (l.absent)
81
+ continue;
82
+ counts[`evidence.${l.file}.rows`] = l.rows;
83
+ counts[`evidence.${l.file}.joinable`] = l.joinable;
84
+ }
85
+ counts["evidence.runsInMoreThanOneLedger"] = ev.runsInMoreThanOneLedger;
86
+ // --- drift: undeclared agent steps (ADR-0021 fail-soft, never a gate) ---
87
+ try {
88
+ const u = scanRecipeDir(path.join(dir, "recipes"));
89
+ counts["privacy.agentSteps"] = u.agentSteps;
90
+ counts["privacy.declared"] = u.declared;
91
+ counts["privacy.undeclared"] = u.undeclared.length;
92
+ counts["privacy.unreadableRecipes"] = u.unreadable.length;
93
+ }
94
+ catch {
95
+ // Unreadable recipes directory: omit, do not zero.
96
+ }
97
+ // --- drift: pull-request outcome ledger ---
98
+ const pr = summarise(readObservations(path.join(dir, "pr_outcomes.jsonl")));
99
+ counts["prOutcomes.rows"] = pr.rows;
100
+ counts["prOutcomes.distinctPrs"] = pr.distinctPrs;
101
+ counts["prOutcomes.withHistory"] = pr.prsWithHistory;
102
+ return {
103
+ rv: SWEEP_RV,
104
+ takenAt: opts.now ?? Date.now(),
105
+ gates,
106
+ counts,
107
+ };
108
+ }
109
+ //# sourceMappingURL=sweepCollect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sweepCollect.js","sourceRoot":"","sources":["../../src/maintenance/sweepCollect.ts"],"names":[],"mappings":"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"}
@@ -74,3 +74,17 @@ export declare function undeclaredInRecipe(recipeName: string, parsed: unknown):
74
74
  steps: UndeclaredStep[];
75
75
  };
76
76
  export declare function formatUndeclared(r: UndeclaredReport): string;
77
+ /**
78
+ * Scan a directory of recipe YAML and aggregate `undeclaredInRecipe` across it.
79
+ *
80
+ * Extracted from the `privacy undeclared` handler so `patchwork sweep` reads the
81
+ * same number the verb prints. Two implementations of "how many agent steps
82
+ * declare a data_policy" would drift, and the drift is silent: the sweep would
83
+ * report a delta of zero while the verb reported a regression, or vice versa,
84
+ * and neither would look wrong.
85
+ *
86
+ * Throws only when the directory cannot be read. A recipe that will not parse is
87
+ * REPORTED in `unreadable`, never skipped silently — a file present and ignored
88
+ * is the failure mode this whole family of verbs exists to surface.
89
+ */
90
+ export declare function scanRecipeDir(dir: string): UndeclaredReport;
@@ -34,6 +34,9 @@
34
34
  * Read-only. Output is OPERATOR DATA — it names real installed recipes — so it
35
35
  * is quoted as a measurement and never pasted into an issue or a fixture.
36
36
  */
37
+ import { readdirSync, readFileSync } from "node:fs";
38
+ import path from "node:path";
39
+ import { parse as parseYaml } from "yaml";
37
40
  function isRecord(v) {
38
41
  return typeof v === "object" && v !== null && !Array.isArray(v);
39
42
  }
@@ -163,4 +166,45 @@ export function formatUndeclared(r) {
163
166
  L.push("");
164
167
  return `${L.join("\n")}\n`;
165
168
  }
169
+ /**
170
+ * Scan a directory of recipe YAML and aggregate `undeclaredInRecipe` across it.
171
+ *
172
+ * Extracted from the `privacy undeclared` handler so `patchwork sweep` reads the
173
+ * same number the verb prints. Two implementations of "how many agent steps
174
+ * declare a data_policy" would drift, and the drift is silent: the sweep would
175
+ * report a delta of zero while the verb reported a regression, or vice versa,
176
+ * and neither would look wrong.
177
+ *
178
+ * Throws only when the directory cannot be read. A recipe that will not parse is
179
+ * REPORTED in `unreadable`, never skipped silently — a file present and ignored
180
+ * is the failure mode this whole family of verbs exists to surface.
181
+ */
182
+ export function scanRecipeDir(dir) {
183
+ const entries = readdirSync(dir).sort();
184
+ let recipesScanned = 0;
185
+ let agentSteps = 0;
186
+ let declared = 0;
187
+ const undeclared = [];
188
+ const unreadable = [];
189
+ for (const f of entries) {
190
+ if (!/\.ya?ml$/i.test(f))
191
+ continue;
192
+ recipesScanned++;
193
+ let parsed;
194
+ try {
195
+ parsed = parseYaml(readFileSync(path.join(dir, f), "utf-8"));
196
+ }
197
+ catch {
198
+ unreadable.push(f);
199
+ continue;
200
+ }
201
+ const declaredName = parsed?.name;
202
+ const name = typeof declaredName === "string" ? declaredName : f;
203
+ const r = undeclaredInRecipe(name, parsed);
204
+ agentSteps += r.agentSteps;
205
+ declared += r.declared;
206
+ undeclared.push(...r.steps);
207
+ }
208
+ return { recipesScanned, agentSteps, declared, undeclared, unreadable };
209
+ }
166
210
  //# sourceMappingURL=undeclaredSteps.js.map