@vijaypjavvadi/bdd2pw 4.3.2 → 4.4.1

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,360 @@
1
+ "use strict";
2
+ /**
3
+ * v4.4.0 Slice 2 — `bdd2pw coverage <repo>`.
4
+ *
5
+ * Reads the same set of artefacts Slice 1's `quarantine-stats`
6
+ * consumes, plus the healing telemetry, and turns "which rules fired
7
+ * in this repo" into a decision surface:
8
+ *
9
+ * - rule hit rate — which rules bound how many steps, ordered
10
+ * by use.
11
+ * - per-pack hit rate — how much each domain rule pack pulled its
12
+ * weight; a pack with no hits is a
13
+ * candidate for `domains: [...]` removal.
14
+ * - dead rules — every rule the current registry knows about
15
+ * that did NOT fire in this repo. Not a
16
+ * verdict on the rule — just a repo-scoped
17
+ * report.
18
+ * - promotion trail — candidate rules proposed from LLM bindings
19
+ * → which became real rules → which of
20
+ * those now fire. The loop closing is the
21
+ * number worth publishing.
22
+ * - heal pressure — locators that heal most often (from
23
+ * `artefacts/heal-events.jsonl`). A rule
24
+ * that binds and then always needs healing
25
+ * is worse than no rule.
26
+ *
27
+ * Report shape (both stdout and `<repo>/artefacts/coverage.json`):
28
+ *
29
+ * {
30
+ * "version": "<bdd2pw version>",
31
+ * "generatedAt": "<ISO-8601>",
32
+ * "repo": "<absolute path>",
33
+ * "sidecars": <int>,
34
+ * "totals": { scenarios: { ... }, steps: { ... } }, // shared with quarantine-stats
35
+ * "rules": {
36
+ * "hit": [{ ruleId, pack?, hitCount, sampleSteps: [ ... ] }, ...],
37
+ * "dead": [{ ruleId, pack? }, ...],
38
+ * },
39
+ * "packs": {
40
+ * "hit": { <packName>: { hitCount, ruleIdsFired: [ ... ] } },
41
+ * "dead": [ <packName>, ... ]
42
+ * },
43
+ * "promotions": { candidateStepsSurveyed, promoted, promotedStepTexts }, // shared with quarantine-stats
44
+ * "healPressure": [{ locatorKey, healCount, page?, name? }, ...],
45
+ * "missingArtefacts": [ "artefacts/heal-events.jsonl", ... ],
46
+ * "warnings": [ ... ]
47
+ * }
48
+ *
49
+ * The reader is best-effort. Missing artefacts are reported, not
50
+ * fatal. Exits non-zero only on a real I/O error against the repo
51
+ * root itself. The output shape is invariant with `quarantine-stats`
52
+ * where the fields overlap (`totals`, `promotions`) so a dashboard
53
+ * can read either without switching schemas.
54
+ */
55
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
56
+ if (k2 === undefined) k2 = k;
57
+ var desc = Object.getOwnPropertyDescriptor(m, k);
58
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
59
+ desc = { enumerable: true, get: function() { return m[k]; } };
60
+ }
61
+ Object.defineProperty(o, k2, desc);
62
+ }) : (function(o, m, k, k2) {
63
+ if (k2 === undefined) k2 = k;
64
+ o[k2] = m[k];
65
+ }));
66
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
67
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
68
+ }) : function(o, v) {
69
+ o["default"] = v;
70
+ });
71
+ var __importStar = (this && this.__importStar) || (function () {
72
+ var ownKeys = function(o) {
73
+ ownKeys = Object.getOwnPropertyNames || function (o) {
74
+ var ar = [];
75
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
76
+ return ar;
77
+ };
78
+ return ownKeys(o);
79
+ };
80
+ return function (mod) {
81
+ if (mod && mod.__esModule) return mod;
82
+ var result = {};
83
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
84
+ __setModuleDefault(result, mod);
85
+ return result;
86
+ };
87
+ })();
88
+ Object.defineProperty(exports, "__esModule", { value: true });
89
+ exports.analyseCoverage = analyseCoverage;
90
+ exports.renderCoverageTable = renderCoverageTable;
91
+ exports.renderSuggestRetire = renderSuggestRetire;
92
+ const fs = __importStar(require("fs-extra"));
93
+ const path = __importStar(require("path"));
94
+ const quarantineStats_1 = require("./quarantineStats");
95
+ async function analyseCoverage(opts) {
96
+ var _a;
97
+ const repo = path.resolve(opts.repo);
98
+ const missingArtefacts = [];
99
+ const warnings = [];
100
+ const sampleCap = opts.sampleStepsPerRule ?? 3;
101
+ const healTopN = opts.healPressureTopN ?? 10;
102
+ // 1) Shared totals + promotions — re-use Slice 1's aggregator so
103
+ // this and quarantine-stats always agree on the shared numbers.
104
+ // Write to a scratch path we discard; only the returned stats
105
+ // object matters here.
106
+ const scratch = path.join(repo, "artefacts", ".coverage-scratch.json");
107
+ const { stats } = await (0, quarantineStats_1.analyseQuarantineStats)({
108
+ repo,
109
+ version: opts.version,
110
+ outputPath: scratch,
111
+ });
112
+ await fs.remove(scratch).catch(() => { });
113
+ for (const w of stats.warnings)
114
+ warnings.push(w);
115
+ // 2) Walk sidecars for rule hits.
116
+ const hitByRule = new Map();
117
+ const testsDir = path.join(repo, "tests");
118
+ const sidecarFiles = [];
119
+ if (await fs.pathExists(testsDir)) {
120
+ await walkForSidecars(testsDir, sidecarFiles);
121
+ }
122
+ else {
123
+ missingArtefacts.push("tests/");
124
+ }
125
+ for (const file of sidecarFiles) {
126
+ let sidecar;
127
+ try {
128
+ sidecar = JSON.parse(await fs.readFile(file, "utf8"));
129
+ }
130
+ catch {
131
+ continue; // Already warned in Slice 1's pass.
132
+ }
133
+ for (const scenario of sidecar.scenarios ?? []) {
134
+ for (const step of scenario.steps ?? []) {
135
+ if (step.rule && typeof step.rule === "object") {
136
+ const ruleId = step.rule.ruleId;
137
+ if (!ruleId)
138
+ continue;
139
+ let hit = hitByRule.get(ruleId);
140
+ if (!hit) {
141
+ hit = {
142
+ ruleId,
143
+ pack: step.rule.pack,
144
+ hitCount: 0,
145
+ sampleSteps: [],
146
+ };
147
+ hitByRule.set(ruleId, hit);
148
+ }
149
+ hit.hitCount += 1;
150
+ if (hit.sampleSteps.length < sampleCap && step.text) {
151
+ hit.sampleSteps.push(step.text);
152
+ }
153
+ }
154
+ }
155
+ }
156
+ }
157
+ // 3) Dead rules = every registry ruleId NOT in hitByRule.
158
+ const hitRuleIds = new Set(hitByRule.keys());
159
+ const deadRules = [];
160
+ for (const r of opts.registryRuleIds) {
161
+ if (!hitRuleIds.has(r.ruleId)) {
162
+ deadRules.push(r.pack ? { ruleId: r.ruleId, pack: r.pack } : { ruleId: r.ruleId });
163
+ }
164
+ }
165
+ // 4) Per-pack roll-up.
166
+ const packHits = {};
167
+ const packsSeen = new Set();
168
+ for (const r of opts.registryRuleIds) {
169
+ if (r.pack)
170
+ packsSeen.add(r.pack);
171
+ }
172
+ for (const [, hit] of hitByRule) {
173
+ if (!hit.pack)
174
+ continue;
175
+ const acc = (packHits[_a = hit.pack] ?? (packHits[_a] = { hitCount: 0, ruleIdsFired: [] }));
176
+ acc.hitCount += hit.hitCount;
177
+ if (!acc.ruleIdsFired.includes(hit.ruleId)) {
178
+ acc.ruleIdsFired.push(hit.ruleId);
179
+ }
180
+ }
181
+ const deadPacks = [];
182
+ for (const p of packsSeen) {
183
+ if (!packHits[p])
184
+ deadPacks.push(p);
185
+ }
186
+ deadPacks.sort();
187
+ // 5) Heal pressure — read artefacts/heal-events.jsonl and count
188
+ // `healed` / `heal_attempt` events per locator.
189
+ const healPath = path.join(repo, "artefacts", "heal-events.jsonl");
190
+ const pwHealPath = path.join(repo, ".pwheal", "heal-events.jsonl");
191
+ const healPressureMap = new Map();
192
+ const activeHealPath = (await fs.pathExists(healPath))
193
+ ? healPath
194
+ : (await fs.pathExists(pwHealPath))
195
+ ? pwHealPath
196
+ : undefined;
197
+ if (!activeHealPath) {
198
+ // Not an error — many repos don't run --self-healing. Note it.
199
+ missingArtefacts.push("artefacts/heal-events.jsonl (or .pwheal/heal-events.jsonl)");
200
+ }
201
+ else {
202
+ try {
203
+ const raw = await fs.readFile(activeHealPath, "utf8");
204
+ for (const line of raw.split(/\r?\n/)) {
205
+ const trimmed = line.trim();
206
+ if (!trimmed)
207
+ continue;
208
+ let ev;
209
+ try {
210
+ ev = JSON.parse(trimmed);
211
+ }
212
+ catch {
213
+ continue;
214
+ }
215
+ if (ev.event !== "healed" && ev.event !== "heal_attempt")
216
+ continue;
217
+ const key = `${ev.page ?? "?"}.${ev.name ?? "?"}`;
218
+ let bucket = healPressureMap.get(key);
219
+ if (!bucket) {
220
+ bucket = { healCount: 0, page: ev.page, name: ev.name };
221
+ healPressureMap.set(key, bucket);
222
+ }
223
+ bucket.healCount += 1;
224
+ }
225
+ }
226
+ catch (e) {
227
+ warnings.push(`Could not read heal-events log: ${e instanceof Error ? e.message : String(e)}`);
228
+ }
229
+ }
230
+ const healPressure = Array.from(healPressureMap.entries())
231
+ .map(([locatorKey, v]) => ({ locatorKey, ...v }))
232
+ .sort((a, b) => b.healCount - a.healCount)
233
+ .slice(0, healTopN);
234
+ // 6) candidate-rules.jsonl awareness — record its absence as a
235
+ // missing artefact when nothing to promote and no file present.
236
+ const candidatesPath = path.join(repo, "artefacts", "candidate-rules.jsonl");
237
+ if (!(await fs.pathExists(candidatesPath))) {
238
+ missingArtefacts.push("artefacts/candidate-rules.jsonl");
239
+ }
240
+ const coverage = {
241
+ version: opts.version,
242
+ generatedAt: new Date().toISOString(),
243
+ repo,
244
+ sidecars: stats.totals.sidecars,
245
+ totals: stats.totals,
246
+ rules: {
247
+ hit: Array.from(hitByRule.values()).sort((a, b) => b.hitCount - a.hitCount),
248
+ dead: deadRules,
249
+ },
250
+ packs: {
251
+ hit: packHits,
252
+ dead: deadPacks,
253
+ },
254
+ promotions: stats.promotions,
255
+ healPressure,
256
+ missingArtefacts,
257
+ warnings,
258
+ };
259
+ const outputPath = opts.outputPath ?? path.join(repo, "artefacts", "coverage.json");
260
+ await fs.ensureDir(path.dirname(outputPath));
261
+ await fs.writeFile(outputPath, JSON.stringify(coverage, null, 2) + "\n", "utf8");
262
+ return { coverage, outputPath };
263
+ }
264
+ /** Depth-first walk for `.spec.meta.json` files. */
265
+ async function walkForSidecars(dir, out) {
266
+ let entries;
267
+ try {
268
+ entries = await fs.readdir(dir, { withFileTypes: true });
269
+ }
270
+ catch {
271
+ return;
272
+ }
273
+ for (const entry of entries) {
274
+ const full = path.join(dir, entry.name);
275
+ if (entry.isDirectory()) {
276
+ await walkForSidecars(full, out);
277
+ }
278
+ else if (entry.isFile() && entry.name.endsWith(".spec.meta.json")) {
279
+ out.push(full);
280
+ }
281
+ }
282
+ }
283
+ /**
284
+ * Human-readable one-page render. Separate from the JSON generator so
285
+ * tests can assert on the JSON shape without parsing text, and callers
286
+ * that want raw JSON pass `--json` at the CLI layer.
287
+ */
288
+ function renderCoverageTable(cov) {
289
+ const lines = [];
290
+ lines.push(`bdd2pw coverage (v${cov.version})`);
291
+ lines.push(`repo: ${cov.repo}`);
292
+ lines.push(`generated: ${cov.generatedAt}`);
293
+ lines.push("");
294
+ lines.push(`sidecars: ${cov.sidecars} scenarios: ${cov.totals.scenarios.total} steps: ${cov.totals.steps.total}`);
295
+ lines.push("");
296
+ lines.push(`rules fired: ${cov.rules.hit.length} dead: ${cov.rules.dead.length}`);
297
+ const topRules = cov.rules.hit.slice(0, 10);
298
+ if (topRules.length > 0) {
299
+ lines.push("");
300
+ lines.push("top firing rules:");
301
+ for (const r of topRules) {
302
+ lines.push(` ${r.ruleId.padEnd(12)} ${String(r.hitCount).padStart(4)} ${r.pack ?? "core"}`);
303
+ }
304
+ }
305
+ const deadPacks = cov.packs.dead;
306
+ if (deadPacks.length > 0) {
307
+ lines.push("");
308
+ lines.push(`dead packs (no rule fired in this repo): ${deadPacks.join(", ")}`);
309
+ lines.push(` → drop from your domains: [...] to shrink the rule table (--suggest-retire prints the stanza).`);
310
+ }
311
+ if (cov.healPressure.length > 0) {
312
+ lines.push("");
313
+ lines.push("heal pressure (most healed locators):");
314
+ for (const h of cov.healPressure) {
315
+ lines.push(` ${h.locatorKey.padEnd(40)} ${h.healCount}`);
316
+ }
317
+ }
318
+ lines.push("");
319
+ lines.push(`promotions: ${cov.promotions.promoted}/${cov.promotions.candidateStepsSurveyed} candidate step(s) now rule-bound`);
320
+ if (cov.missingArtefacts.length > 0) {
321
+ lines.push("");
322
+ lines.push("missing artefacts (report continued with what was present):");
323
+ for (const m of cov.missingArtefacts)
324
+ lines.push(` - ${m}`);
325
+ }
326
+ if (cov.warnings.length > 0) {
327
+ lines.push("");
328
+ lines.push("warnings:");
329
+ for (const w of cov.warnings)
330
+ lines.push(` - ${w}`);
331
+ }
332
+ return lines.join("\n") + "\n";
333
+ }
334
+ /**
335
+ * Render the `--suggest-retire` stanza — a copyable block the user can
336
+ * paste into their bdd2pw config (or bench harness) to shrink the
337
+ * active rule table. Never edits anything itself.
338
+ */
339
+ function renderSuggestRetire(cov) {
340
+ const lines = [];
341
+ lines.push("# `bdd2pw coverage --suggest-retire` — copy the block(s) you want to apply.");
342
+ lines.push("# Each entry is a rule or pack that did NOT fire in this repo. Removing");
343
+ lines.push("# them is safe here; it may not be safe across all your repos.");
344
+ lines.push("");
345
+ if (cov.packs.dead.length > 0) {
346
+ lines.push("# Domain packs to drop from ScaffoldOptions.domains:");
347
+ for (const p of cov.packs.dead) {
348
+ lines.push(`# - "${p}"`);
349
+ }
350
+ lines.push("");
351
+ }
352
+ if (cov.rules.dead.length > 0) {
353
+ lines.push("# Rules that never fired here (repo-scoped only; NOT a global verdict):");
354
+ for (const r of cov.rules.dead) {
355
+ lines.push(`# - ${r.ruleId}${r.pack ? ` (pack: ${r.pack})` : ""}`);
356
+ }
357
+ }
358
+ return lines.join("\n") + "\n";
359
+ }
360
+ //# sourceMappingURL=coverage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coverage.js","sourceRoot":"","sources":["../../src/reports/coverage.ts"],"names":[],"mappings":"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"}
@@ -39,6 +39,27 @@ export interface MetaSidecarStep {
39
39
  locator?: string;
40
40
  /** Matcher name or method name when the binding has one. */
41
41
  assertion?: string;
42
+ /**
43
+ * v4.4.0 — provenance. `null` when this step was bound by the LLM
44
+ * fallback; a `{ ruleId, pack? }` object when bound by the
45
+ * deterministic rule table. Absent only when the binding was
46
+ * emitted before v4.4 (old sidecars remain parseable).
47
+ */
48
+ rule?: null | {
49
+ ruleId: string;
50
+ pack?: string;
51
+ };
52
+ /**
53
+ * v4.4.0 — populated iff this step was bound by the LLM fallback.
54
+ * Consumed by `bdd2pw quarantine-stats` and `bdd2pw coverage`.
55
+ */
56
+ llm?: {
57
+ provider: "anthropic" | "openai" | "gemini";
58
+ model: string;
59
+ cached: boolean;
60
+ governance: "sanitised" | "skipped";
61
+ at: string;
62
+ };
42
63
  }
43
64
  export interface MetaSidecarScenario {
44
65
  name: string;
@@ -80,6 +80,26 @@ function bindingsToMetaSteps(bindings) {
80
80
  step.locator = b.assertion.locator;
81
81
  step.assertion = b.assertion.matcher;
82
82
  }
83
+ // v4.4.0 — provenance passthrough.
84
+ // LLM binding → `rule: null` + full `llm` block.
85
+ // Rule binding → `rule: { ruleId, pack? }`.
86
+ // No provenance (legacy binding) → both fields absent.
87
+ if (b.provenance?.source === "llm" && b.provenance.llm) {
88
+ step.rule = null;
89
+ step.llm = {
90
+ provider: b.provenance.llm.provider,
91
+ model: b.provenance.llm.model,
92
+ cached: b.provenance.llm.cached,
93
+ governance: b.provenance.llm.governance,
94
+ at: b.provenance.llm.at,
95
+ };
96
+ }
97
+ else if (b.provenance?.source === "rule" && b.provenance.ruleId) {
98
+ step.rule = {
99
+ ruleId: b.provenance.ruleId,
100
+ ...(b.provenance.pack ? { pack: b.provenance.pack } : {}),
101
+ };
102
+ }
83
103
  return step;
84
104
  });
85
105
  }
@@ -1 +1 @@
1
- {"version":3,"file":"metaSidecar.js","sourceRoot":"","sources":["../../src/reports/metaSidecar.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;;AAwDH,kDAmBC;AAhDD;;;;;;;;;;GAUG;AACH,SAAS,cAAc,CAAC,CAAc;IACpC,IAAI,CAAC,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IAC/B,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,OAAO;QAAE,OAAO,MAAM,CAAC;IAC5E,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;QACd,OAAO,CAAC,CAAC,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,aAAa,CAAC;IACpE,CAAC;IACD,IAAI,CAAC,CAAC,SAAS;QAAE,OAAO,WAAW,CAAC;IACpC,IAAI,CAAC,CAAC,UAAU;QAAE,OAAO,UAAU,CAAC;IACpC,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,mBAAmB,CACjC,QAAuB;IAEvB,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE;QAC7B,MAAM,IAAI,GAAoB;YAC5B,EAAE,EAAE,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC;YACpC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO;YACvB,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI;YACjB,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;SAC1B,CAAC;QACF,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;YACd,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YACvD,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;QACpC,CAAC;aAAM,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC;YACvB,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC;YACnC,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC;QACvC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"metaSidecar.js","sourceRoot":"","sources":["../../src/reports/metaSidecar.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * v4.4.0 Slice 1 — `bdd2pw quarantine-stats <repo>`.
3
+ *
4
+ * Aggregates the LLM-provenance data written by v4.4.0's scaffold path
5
+ * (`*.spec.meta.json` sidecars + `artefacts/candidate-rules.jsonl`)
6
+ * into a per-feature / per-pack report. The point is to make the two
7
+ * lanes measurable:
8
+ *
9
+ * trusted lane — scenarios with NO `@llm-generated` tag; every
10
+ * step bound by the deterministic rule table.
11
+ * review lane — scenarios with the `@llm-generated` tag; at
12
+ * least one step bound by the LLM fallback.
13
+ *
14
+ * Report shape (both stdout and `artefacts/quarantine-stats.json`):
15
+ *
16
+ * {
17
+ * "version": "<bdd2pw version>",
18
+ * "generatedAt": "<ISO-8601>",
19
+ * "repo": "<absolute path>",
20
+ * "totals": {
21
+ * "sidecars": <int>,
22
+ * "scenarios": { "total": N, "llmGenerated": N, "deterministic": N },
23
+ * "steps": { "total": N, "byRule": N, "byLlm": N },
24
+ * "llmCacheHitShare": <0..1 | null>
25
+ * },
26
+ * "byFeature": [{ source, scenarios, steps }, ...],
27
+ * "promotions": {
28
+ * "candidateStepsSurveyed": <int>,
29
+ * "promoted": <int>,
30
+ * "promotedStepTexts": [ ... ]
31
+ * },
32
+ * "sidecarsScanned": ["<path>", ...]
33
+ * }
34
+ *
35
+ * The reader is best-effort — a malformed sidecar is skipped with a
36
+ * warning and does not crash the run. Only I/O failures on the repo
37
+ * root itself surface as non-zero exit codes; the report is not a gate.
38
+ */
39
+ /** Top-level per-repo summary. */
40
+ export interface QuarantineStats {
41
+ version: string;
42
+ generatedAt: string;
43
+ repo: string;
44
+ totals: {
45
+ sidecars: number;
46
+ scenarios: {
47
+ total: number;
48
+ llmGenerated: number;
49
+ deterministic: number;
50
+ };
51
+ steps: {
52
+ total: number;
53
+ byRule: number;
54
+ byLlm: number;
55
+ };
56
+ /** null when no LLM steps were recorded (avoids fake precision). */
57
+ llmCacheHitShare: number | null;
58
+ };
59
+ byFeature: {
60
+ source: string;
61
+ scenarios: {
62
+ total: number;
63
+ llmGenerated: number;
64
+ deterministic: number;
65
+ };
66
+ steps: {
67
+ total: number;
68
+ byRule: number;
69
+ byLlm: number;
70
+ };
71
+ }[];
72
+ promotions: {
73
+ candidateStepsSurveyed: number;
74
+ promoted: number;
75
+ promotedStepTexts: string[];
76
+ };
77
+ sidecarsScanned: string[];
78
+ warnings: string[];
79
+ }
80
+ export interface AnalyseQuarantineStatsOptions {
81
+ /** Repo root containing tests/, artefacts/. */
82
+ repo: string;
83
+ /** Package version to embed. Propagated from the CLI. */
84
+ version: string;
85
+ /** Optional override for the output path. Default: <repo>/artefacts/quarantine-stats.json. */
86
+ outputPath?: string;
87
+ }
88
+ export interface AnalyseQuarantineStatsResult {
89
+ stats: QuarantineStats;
90
+ outputPath: string;
91
+ }
92
+ /**
93
+ * Walk `<repo>/tests/**\/*.spec.meta.json` and produce the aggregate
94
+ * report. Writes the JSON payload alongside the artefacts directory.
95
+ */
96
+ export declare function analyseQuarantineStats(opts: AnalyseQuarantineStatsOptions): Promise<AnalyseQuarantineStatsResult>;
97
+ /**
98
+ * Human-readable one-page render. Kept separate from the report
99
+ * generation so tests can assert on the JSON shape without parsing
100
+ * text.
101
+ */
102
+ export declare function renderQuarantineStatsTable(stats: QuarantineStats): string;