@ia-qa/self-healing 1.6.6 → 1.6.8

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.
Files changed (61) hide show
  1. package/README.md +118 -2
  2. package/TUTORIAL.md +138 -3
  3. package/assets/logoKawaiiGreenTiny.png +0 -0
  4. package/dist/cli/args.js +4 -2
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/cli/diff.d.ts +6 -1
  7. package/dist/cli/diff.js +114 -4
  8. package/dist/cli/diff.js.map +1 -1
  9. package/dist/cli/emit.d.ts +71 -0
  10. package/dist/cli/emit.js +150 -0
  11. package/dist/cli/emit.js.map +1 -0
  12. package/dist/cli/fix.js +36 -0
  13. package/dist/cli/fix.js.map +1 -1
  14. package/dist/cli/history.d.ts +12 -0
  15. package/dist/cli/history.js +95 -0
  16. package/dist/cli/history.js.map +1 -0
  17. package/dist/cli/index.js +53 -1
  18. package/dist/cli/index.js.map +1 -1
  19. package/dist/cli/init.js +32 -0
  20. package/dist/cli/init.js.map +1 -1
  21. package/dist/cli/map.js +60 -0
  22. package/dist/cli/map.js.map +1 -1
  23. package/dist/cli/run.js +77 -7
  24. package/dist/cli/run.js.map +1 -1
  25. package/dist/cli/ui.d.ts +16 -0
  26. package/dist/cli/ui.js +275 -0
  27. package/dist/cli/ui.js.map +1 -0
  28. package/dist/evidence.d.ts +130 -0
  29. package/dist/evidence.js +232 -0
  30. package/dist/evidence.js.map +1 -0
  31. package/dist/history.d.ts +145 -0
  32. package/dist/history.js +230 -0
  33. package/dist/history.js.map +1 -0
  34. package/dist/htmlReport.d.ts +47 -0
  35. package/dist/htmlReport.js +76 -1
  36. package/dist/htmlReport.js.map +1 -1
  37. package/dist/junit.d.ts +53 -0
  38. package/dist/junit.js +150 -0
  39. package/dist/junit.js.map +1 -0
  40. package/dist/mcp/server.js +41 -0
  41. package/dist/mcp/server.js.map +1 -1
  42. package/dist/ui/icon.d.ts +41 -0
  43. package/dist/ui/icon.js +115 -0
  44. package/dist/ui/icon.js.map +1 -0
  45. package/dist/ui/lock.d.ts +40 -0
  46. package/dist/ui/lock.js +138 -0
  47. package/dist/ui/lock.js.map +1 -0
  48. package/dist/ui/page.d.ts +1 -0
  49. package/dist/ui/page.js +584 -0
  50. package/dist/ui/page.js.map +1 -0
  51. package/dist/ui/registry.d.ts +62 -0
  52. package/dist/ui/registry.js +139 -0
  53. package/dist/ui/registry.js.map +1 -0
  54. package/dist/ui/server.d.ts +67 -0
  55. package/dist/ui/server.js +604 -0
  56. package/dist/ui/server.js.map +1 -0
  57. package/dist/ui/shortcut.d.ts +90 -0
  58. package/dist/ui/shortcut.js +176 -0
  59. package/dist/ui/shortcut.js.map +1 -0
  60. package/package.json +2 -1
  61. package/skills/ia-qa-heal/SKILL.md +27 -0
@@ -0,0 +1,232 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.CROP_MAX_WIDTH = exports.CROP_PADDING = void 0;
37
+ exports.loadPageShot = loadPageShot;
38
+ exports.cropWindow = cropWindow;
39
+ exports.ambiguousCandidates = ambiguousCandidates;
40
+ exports.rowEvidence = rowEvidence;
41
+ const fs = __importStar(require("fs"));
42
+ const path = __importStar(require("path"));
43
+ const config_1 = require("./config");
44
+ const match_1 = require("./browser/match");
45
+ /**
46
+ * Visual evidence for the rows a human has to decide — the decision-grade half
47
+ * of SPEC-visual-evidence.md layer 1.
48
+ *
49
+ * The measured problem this exists for: `ambiguous` rows are 10 % of elements on
50
+ * a dogfooded corpus and 20 % in the wild, and all the report could say about
51
+ * one was `div:nth-of-type(4) > … > button`. The human is not missing a
52
+ * description of what they were looking for — the row already says
53
+ * `button "Copy"` — they are missing *which of the N identical candidates* is
54
+ * the one. That question has exactly one cheap answer: show them, highlighted.
55
+ *
56
+ * Three constraints shape everything below.
57
+ *
58
+ * 1. **No pixel is ever an input to a verdict.** This module is called only when
59
+ * rendering HTML. `diffMappings` never sees it, no counter moves, and a
60
+ * report generated with no screenshots on disk is byte-identical to one from
61
+ * before the feature (a test pins that).
62
+ * 2. **No image is decoded in Node.** There is no PNG/JPEG codec here and no
63
+ * dependency to add one. A crop is a *CSS window* onto the page screenshot:
64
+ * the JPEG is emitted once per page as a `background-image`, and every
65
+ * thumbnail is a box with a negative background offset. N crops of one page
66
+ * therefore cost the bytes of one picture.
67
+ * 3. **A crop with no marker on the target is worse than no crop** — study D,
68
+ * measured on real captures. Six identical upvote arrows tell a human
69
+ * nothing about which one drifted. So the highlight is not decoration and is
70
+ * never optional: if the rect is unknown, the crop is not rendered at all.
71
+ */
72
+ /** Pixels of page context kept around the element. 120 is the value study D found legible. */
73
+ exports.CROP_PADDING = 120;
74
+ /** Widest a thumbnail may render; wider windows are scaled down, never cropped further. */
75
+ exports.CROP_MAX_WIDTH = 360;
76
+ /**
77
+ * Load the screenshot and the element positions `map --screenshots` wrote for a
78
+ * page. Either half missing — flag never used, page skipped, old capture — means
79
+ * no evidence for that page and nothing else: every caller degrades to the
80
+ * report as it was.
81
+ */
82
+ function loadPageShot(pageName, dir = safeShotsDir()) {
83
+ if (!dir)
84
+ return null;
85
+ const safe = pageName.replace(/[^a-zA-Z0-9._-]/g, '-');
86
+ const jpg = path.join(dir, `${safe}.jpg`);
87
+ const rectsFile = path.join(dir, `${safe}.rects.json`);
88
+ try {
89
+ if (!fs.existsSync(jpg) || !fs.existsSync(rectsFile))
90
+ return null;
91
+ const parsed = JSON.parse(fs.readFileSync(rectsFile, 'utf8'));
92
+ if (!parsed.rects || !parsed.viewport)
93
+ return null;
94
+ const b64 = fs.readFileSync(jpg).toString('base64');
95
+ return {
96
+ dataUri: `data:image/jpeg;base64,${b64}`,
97
+ viewport: parsed.viewport,
98
+ rects: parsed.rects,
99
+ };
100
+ }
101
+ catch {
102
+ return null;
103
+ }
104
+ }
105
+ function safeShotsDir() {
106
+ try {
107
+ return (0, config_1.shotsDir)();
108
+ }
109
+ catch {
110
+ return '';
111
+ }
112
+ }
113
+ /**
114
+ * The CSS window onto the page image for one element: a box of `rect ± padding`,
115
+ * clamped to the picture's bounds, plus where the target sits inside it.
116
+ *
117
+ * Pure arithmetic — same inputs, same output, no I/O — which is what lets a test
118
+ * pin it without a browser (AC-13's spirit: the crop is a function, not a
119
+ * capture).
120
+ */
121
+ function cropWindow(rect, viewport, padding = exports.CROP_PADDING, maxWidth = exports.CROP_MAX_WIDTH) {
122
+ const [x, y, w, h] = rect;
123
+ const winW = Math.min(viewport.w, w + padding * 2);
124
+ const winH = Math.min(viewport.h, h + padding * 2);
125
+ const offsetX = Math.max(0, Math.min(viewport.w - winW, Math.round(x - (winW - w) / 2)));
126
+ const offsetY = Math.max(0, Math.min(viewport.h - winH, Math.round(y - (winH - h) / 2)));
127
+ const scale = Math.min(1, maxWidth / winW);
128
+ return {
129
+ width: Math.round(winW * scale),
130
+ height: Math.round(winH * scale),
131
+ scale,
132
+ offsetX,
133
+ offsetY,
134
+ hi: { left: x - offsetX, top: y - offsetY, width: w, height: h },
135
+ };
136
+ }
137
+ /**
138
+ * The elements an `ambiguous` row could not be told apart from.
139
+ *
140
+ * `matchElement` returns only a *count* — it decides, or refuses, and the
141
+ * refusal carries no list. Rather than widen its return type, this re-derives
142
+ * the same set from the same primitives on the "after" side. That choice is not
143
+ * stylistic: `src/browser/match.js` ships twice (npm and the web tool on
144
+ * ia-qa.com), so every change to it owes two deploys and risks the two copies
145
+ * disagreeing about a verdict. Reporting is allowed to import *from* the
146
+ * matcher; it is not allowed to change it — the same rule the AI layer follows.
147
+ *
148
+ * The tiers mirror `matchElement` exactly: exact accessible-name matches first,
149
+ * then the fuzzy band within 0.05 of the top Dice score.
150
+ *
151
+ * One case it cannot reproduce, on purpose: a row demoted to `ambiguous` by the
152
+ * many-to-one guard (N baseline elements all healing onto one target) has a
153
+ * `candidateCount` counting *baseline rows*, not live elements. There, this
154
+ * returns the single contested target, which is the useful thing to look at.
155
+ */
156
+ function ambiguousCandidates(row, after) {
157
+ const sameRole = after.filter((c) => c.role === row.role);
158
+ const wanted = (0, match_1.normalize)(row.name);
159
+ const wantedCtx = (0, match_1.normalize)(row.context);
160
+ const pick = (cands) => {
161
+ if (cands.length <= 1)
162
+ return cands;
163
+ if (wantedCtx) {
164
+ const byCtx = cands.filter((c) => (0, match_1.normalize)(c.context) === wantedCtx);
165
+ if (byCtx.length === 1)
166
+ return byCtx;
167
+ }
168
+ return cands;
169
+ };
170
+ const exact = sameRole.filter((c) => (0, match_1.normalize)(c.name) === wanted && wanted !== '');
171
+ if (exact.length > 0)
172
+ return pick(exact).map(toCandidate);
173
+ const scored = sameRole
174
+ .map((c) => ({ c, score: (0, match_1.diceSimilarity)((0, match_1.normalize)(c.name), wanted) }))
175
+ .filter((s) => s.score >= match_1.FUZZY_THRESHOLD)
176
+ .sort((a, b) => b.score - a.score);
177
+ if (scored.length === 0)
178
+ return [];
179
+ const top = scored[0].score;
180
+ const tied = scored.filter((s) => top - s.score < 0.05).map((s) => s.c);
181
+ return pick(tied).map(toCandidate);
182
+ }
183
+ function toCandidate(e) {
184
+ return { selector: e.selector, name: e.name, role: e.role, context: e.context };
185
+ }
186
+ /**
187
+ * Assemble the evidence for one drifted row.
188
+ *
189
+ * Only the statuses where a human is actually stuck get pictures — `ambiguous`,
190
+ * and anything `rebound`. `ok`, `renamed` and `healable` are deliberately left
191
+ * bare: there is no decision to take on them, so a thumbnail would be weight
192
+ * without a question (F1.8).
193
+ */
194
+ function rowEvidence(row, after, shot) {
195
+ if (!shot || !after)
196
+ return null;
197
+ const wants = row.status === 'ambiguous' || row.rebound === true;
198
+ if (!wants)
199
+ return null;
200
+ const locate = (selector, label) => {
201
+ const rect = shot.rects[selector];
202
+ // No rect means the element was below the fold or absent when the picture
203
+ // was taken. Showing the crop anyway would highlight empty space — exactly
204
+ // the misleading thumbnail study D rules out.
205
+ if (!rect)
206
+ return null;
207
+ return { selector, label, crop: cropWindow(rect, shot.viewport) };
208
+ };
209
+ const out = { candidates: [] };
210
+ if (row.rebound) {
211
+ const hit = locate(row.selector, `${row.role} "${row.reboundTo ?? ''}"`);
212
+ if (hit)
213
+ out.nowAt = hit;
214
+ }
215
+ if (row.status === 'ambiguous') {
216
+ for (const c of ambiguousCandidates(row, after.elements)) {
217
+ const located = locate(c.selector, c.name ? `${c.role} "${c.name}"` : `${c.role} (no name)`);
218
+ if (located)
219
+ out.candidates.push(located);
220
+ }
221
+ }
222
+ if (!out.nowAt && out.candidates.length === 0) {
223
+ return {
224
+ candidates: [],
225
+ note: row.status === 'ambiguous'
226
+ ? 'No picture for these candidates — they were below the fold when the page was captured.'
227
+ : 'No picture for this element — it was below the fold when the page was captured.',
228
+ };
229
+ }
230
+ return out;
231
+ }
232
+ //# sourceMappingURL=evidence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evidence.js","sourceRoot":"","sources":["../src/evidence.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Append-only run history — `.ia-qa/history.jsonl`.
3
+ *
4
+ * Every other verb in this package is *stateless*: `diff` compares two moments
5
+ * and forgets. That is the right shape for a gate, and it is also why the tool
6
+ * can never answer the question a QA lead actually asks — "is this getting
7
+ * better or worse?". A trend is not derivable from any single invocation, so it
8
+ * has to be recorded as it happens; unlike a verdict, it cannot be recomputed
9
+ * after the fact. Every run not written down is a data point that will never
10
+ * exist.
11
+ *
12
+ * Three rules keep this from becoming a dashboard that lies:
13
+ *
14
+ * 1. **It never influences a verdict.** Nothing reads history back into a diff.
15
+ * It is written after the exit code is decided, and a failure to write is
16
+ * swallowed — a full disk must not turn a PASS into a crash.
17
+ * 2. **Every entry carries what makes it comparable** — `source` (`map` vs a
18
+ * live `run` capture) and `coverage` (pages measured / pages mapped). Two
19
+ * entries that disagree on either are not two points on one curve, and
20
+ * `trendable()` below is what refuses to draw them as one.
21
+ * 3. **It is small and deterministic.** Verdicts and counters, no pixels, no
22
+ * element contents. Unlike `_shots/`, committing this file is defensible —
23
+ * it is a few kB of text that changes by one line per run.
24
+ */
25
+ export declare const HISTORY_FILENAME = "history.jsonl";
26
+ /** Entries kept on disk. Older ones are dropped on write — a trend does not need a year. */
27
+ export declare const HISTORY_LIMIT = 500;
28
+ export interface HistoryCounts {
29
+ total: number;
30
+ ok: number;
31
+ renamed: number;
32
+ healable: number;
33
+ ambiguous: number;
34
+ lost: number;
35
+ rebound: number;
36
+ added: number;
37
+ }
38
+ export interface HistoryEntry {
39
+ /** ISO-8601, stamped at write time. */
40
+ ts: string;
41
+ verb: 'diff' | 'run' | 'fix' | 'audit' | 'check';
42
+ /** The verb's own verdict word — PASS/FIX/BLOCK for the drift verbs, OK/WARN/FAIL for `check`. */
43
+ verdict: string;
44
+ /** Element-level counters. Absent for verbs that do not diff (`audit`, `check`, bare `fix`). */
45
+ counts?: HistoryCounts;
46
+ /**
47
+ * How the *current* side was captured. `mixed` means the pages did not agree —
48
+ * the same source mismatch `run` warns about, recorded rather than inferred.
49
+ */
50
+ source?: 'map' | 'run' | 'mixed';
51
+ /** Pages that produced a verdict / pages mapped. A green run over 1 of 40 pages is not a green app. */
52
+ coverage?: {
53
+ measured: number;
54
+ total: number;
55
+ };
56
+ nameDrift?: {
57
+ total: number;
58
+ manual: number;
59
+ };
60
+ /** Only pages that actually drifted, so a 200-page app still writes a short line. */
61
+ pages?: Array<{
62
+ name: string;
63
+ drift: number;
64
+ }>;
65
+ baseUrl?: string;
66
+ commit?: string;
67
+ branch?: string;
68
+ /** What `fix`/`run` wrote to the test files. */
69
+ applied?: {
70
+ replacements: number;
71
+ files: number;
72
+ };
73
+ /** The post-fix re-run of the user's suite, when `run` performed one. */
74
+ verify?: 'PASS' | 'FAIL' | 'SKIPPED';
75
+ /** Findings, for the verbs that produce findings rather than diff rows. */
76
+ findings?: number;
77
+ }
78
+ export declare function historyPath(cwd?: string): string;
79
+ /**
80
+ * Append one entry. Best-effort by design: history is an observation of the run,
81
+ * never part of it, so every failure mode here (read-only checkout, missing
82
+ * directory, full disk, concurrent writer) costs the data point and nothing else.
83
+ */
84
+ export declare function appendHistory(entry: Omit<HistoryEntry, 'ts'> & {
85
+ ts?: string;
86
+ }): void;
87
+ /**
88
+ * Read the history, oldest first. A malformed line is skipped rather than fatal:
89
+ * this file is appended to by every run and may be truncated mid-write by a
90
+ * killed process, and one bad line must not cost the other 499.
91
+ */
92
+ export declare function readHistory(limit?: number, cwd?: string): HistoryEntry[];
93
+ /**
94
+ * Which entries may be drawn as one curve.
95
+ *
96
+ * This is the whole reason the store carries `source`. A `map` baseline and a
97
+ * live `run` capture read the same page differently — the CLI already warns
98
+ * about diffing one against the other — so plotting both on one axis manufactures
99
+ * a trend out of a capture artifact. The same applies to coverage: a run that
100
+ * measured 1 of 40 pages and one that measured 40 are not comparable magnitudes,
101
+ * so entries below `minCoverage` of the largest observed coverage are held out.
102
+ *
103
+ * The excluded entries are returned, not discarded, so the caller can *say* what
104
+ * it left out. A chart that silently drops half its data is the dashboard-that-
105
+ * lies failure mode this function exists to prevent.
106
+ */
107
+ export interface Trend {
108
+ series: HistoryEntry[];
109
+ excluded: HistoryEntry[];
110
+ /** Present when anything was held out — render it next to the chart, never swallow it. */
111
+ reason?: string;
112
+ /** The capture source the series is drawn from. */
113
+ source?: 'map' | 'run' | 'mixed';
114
+ }
115
+ export declare function trendable(entries: HistoryEntry[], minCoverage?: number): Trend;
116
+ export interface HistorySummary {
117
+ runs: number;
118
+ first?: string;
119
+ last?: string;
120
+ lastVerdict?: string;
121
+ /** Share of comparable runs that were not PASS. */
122
+ driftRate: number;
123
+ /** Mean measured/total across the comparable series, or 1 when nothing recorded coverage. */
124
+ coverage: number;
125
+ /** Pages ranked by how many runs they drifted in — where the churn actually lives. */
126
+ hotspots: Array<{
127
+ name: string;
128
+ runs: number;
129
+ drift: number;
130
+ }>;
131
+ /** Rewrites the tool applied across the whole history. */
132
+ healed: number;
133
+ trend: Trend;
134
+ }
135
+ export declare function summarize(entries: HistoryEntry[]): HistorySummary;
136
+ /**
137
+ * Best-effort git stamp. A trend of verdicts is far more useful when a spike can
138
+ * be tied to a commit, and every alternative (asking the user to pass it, reading
139
+ * CI env vars per provider) is worse than shelling out once. Never throws, never
140
+ * blocks: no git, no repo, detached HEAD — the entry is written without it.
141
+ */
142
+ export declare function gitStamp(): {
143
+ commit?: string;
144
+ branch?: string;
145
+ };
@@ -0,0 +1,230 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.HISTORY_LIMIT = exports.HISTORY_FILENAME = void 0;
37
+ exports.historyPath = historyPath;
38
+ exports.appendHistory = appendHistory;
39
+ exports.readHistory = readHistory;
40
+ exports.trendable = trendable;
41
+ exports.summarize = summarize;
42
+ exports.gitStamp = gitStamp;
43
+ const fs = __importStar(require("fs"));
44
+ const path = __importStar(require("path"));
45
+ const config_1 = require("./config");
46
+ /**
47
+ * Append-only run history — `.ia-qa/history.jsonl`.
48
+ *
49
+ * Every other verb in this package is *stateless*: `diff` compares two moments
50
+ * and forgets. That is the right shape for a gate, and it is also why the tool
51
+ * can never answer the question a QA lead actually asks — "is this getting
52
+ * better or worse?". A trend is not derivable from any single invocation, so it
53
+ * has to be recorded as it happens; unlike a verdict, it cannot be recomputed
54
+ * after the fact. Every run not written down is a data point that will never
55
+ * exist.
56
+ *
57
+ * Three rules keep this from becoming a dashboard that lies:
58
+ *
59
+ * 1. **It never influences a verdict.** Nothing reads history back into a diff.
60
+ * It is written after the exit code is decided, and a failure to write is
61
+ * swallowed — a full disk must not turn a PASS into a crash.
62
+ * 2. **Every entry carries what makes it comparable** — `source` (`map` vs a
63
+ * live `run` capture) and `coverage` (pages measured / pages mapped). Two
64
+ * entries that disagree on either are not two points on one curve, and
65
+ * `trendable()` below is what refuses to draw them as one.
66
+ * 3. **It is small and deterministic.** Verdicts and counters, no pixels, no
67
+ * element contents. Unlike `_shots/`, committing this file is defensible —
68
+ * it is a few kB of text that changes by one line per run.
69
+ */
70
+ exports.HISTORY_FILENAME = 'history.jsonl';
71
+ /** Entries kept on disk. Older ones are dropped on write — a trend does not need a year. */
72
+ exports.HISTORY_LIMIT = 500;
73
+ function historyPath(cwd = (0, config_1.baseDir)()) {
74
+ return path.join(cwd, config_1.CONFIG_DIR, exports.HISTORY_FILENAME);
75
+ }
76
+ /**
77
+ * Append one entry. Best-effort by design: history is an observation of the run,
78
+ * never part of it, so every failure mode here (read-only checkout, missing
79
+ * directory, full disk, concurrent writer) costs the data point and nothing else.
80
+ */
81
+ function appendHistory(entry) {
82
+ const file = historyPath();
83
+ const full = { ts: entry.ts ?? new Date().toISOString(), ...entry };
84
+ try {
85
+ fs.mkdirSync(path.dirname(file), { recursive: true });
86
+ fs.appendFileSync(file, JSON.stringify(full) + '\n', 'utf8');
87
+ truncate(file);
88
+ }
89
+ catch {
90
+ /* history is a courtesy, never a gate */
91
+ }
92
+ }
93
+ /** Keep the file bounded without a second config knob nobody would ever tune. */
94
+ function truncate(file) {
95
+ try {
96
+ const lines = fs.readFileSync(file, 'utf8').split('\n').filter(Boolean);
97
+ if (lines.length <= exports.HISTORY_LIMIT + 100)
98
+ return;
99
+ fs.writeFileSync(file, lines.slice(-exports.HISTORY_LIMIT).join('\n') + '\n', 'utf8');
100
+ }
101
+ catch {
102
+ /* as above */
103
+ }
104
+ }
105
+ /**
106
+ * Read the history, oldest first. A malformed line is skipped rather than fatal:
107
+ * this file is appended to by every run and may be truncated mid-write by a
108
+ * killed process, and one bad line must not cost the other 499.
109
+ */
110
+ function readHistory(limit = exports.HISTORY_LIMIT, cwd) {
111
+ const file = cwd ? path.join(cwd, config_1.CONFIG_DIR, exports.HISTORY_FILENAME) : historyPath();
112
+ let raw;
113
+ try {
114
+ raw = fs.readFileSync(file, 'utf8');
115
+ }
116
+ catch {
117
+ return [];
118
+ }
119
+ const out = [];
120
+ for (const line of raw.split('\n')) {
121
+ if (!line.trim())
122
+ continue;
123
+ try {
124
+ const parsed = JSON.parse(line);
125
+ if (parsed && typeof parsed.ts === 'string' && typeof parsed.verb === 'string')
126
+ out.push(parsed);
127
+ }
128
+ catch {
129
+ /* skip */
130
+ }
131
+ }
132
+ return out.slice(-limit);
133
+ }
134
+ function trendable(entries, minCoverage = 0.5) {
135
+ const drift = entries.filter((e) => e.counts && (e.verb === 'diff' || e.verb === 'run'));
136
+ if (drift.length === 0)
137
+ return { series: [], excluded: [] };
138
+ // Dominant capture source wins the axis; the minority is held out rather than
139
+ // averaged in. Ties go to the most recent, which is what the user is looking at.
140
+ const bySource = new Map();
141
+ for (const e of drift)
142
+ bySource.set(e.source ?? 'map', (bySource.get(e.source ?? 'map') ?? 0) + 1);
143
+ let source = 'map';
144
+ let best = -1;
145
+ for (const [s, n] of bySource) {
146
+ if (n > best) {
147
+ best = n;
148
+ source = s;
149
+ }
150
+ }
151
+ const sourceKept = drift.filter((e) => (e.source ?? 'map') === source);
152
+ const sourceDropped = drift.filter((e) => (e.source ?? 'map') !== source);
153
+ const maxCov = Math.max(...sourceKept.map((e) => (e.coverage ? e.coverage.measured / Math.max(1, e.coverage.total) : 1)));
154
+ const series = sourceKept.filter((e) => {
155
+ if (!e.coverage)
156
+ return true;
157
+ return e.coverage.measured / Math.max(1, e.coverage.total) >= maxCov * minCoverage;
158
+ });
159
+ const covDropped = sourceKept.filter((e) => !series.includes(e));
160
+ const excluded = [...sourceDropped, ...covDropped];
161
+ const parts = [];
162
+ if (sourceDropped.length > 0) {
163
+ parts.push(`${sourceDropped.length} run${sourceDropped.length === 1 ? '' : 's'} captured a different way ` +
164
+ `(a \`map\` capture and a live suite capture read a page differently)`);
165
+ }
166
+ if (covDropped.length > 0) {
167
+ parts.push(`${covDropped.length} run${covDropped.length === 1 ? '' : 's'} covered too few pages to compare`);
168
+ }
169
+ return {
170
+ series,
171
+ excluded,
172
+ source,
173
+ reason: parts.length > 0 ? `Left out of the trend: ${parts.join('; ')}.` : undefined,
174
+ };
175
+ }
176
+ function summarize(entries) {
177
+ const trend = trendable(entries);
178
+ const s = trend.series;
179
+ const notPass = s.filter((e) => e.verdict !== 'PASS').length;
180
+ const withCov = s.filter((e) => e.coverage);
181
+ const coverage = withCov.length === 0
182
+ ? 1
183
+ : withCov.reduce((n, e) => n + e.coverage.measured / Math.max(1, e.coverage.total), 0) / withCov.length;
184
+ const pageStats = new Map();
185
+ for (const e of s) {
186
+ for (const p of e.pages ?? []) {
187
+ const cur = pageStats.get(p.name) ?? { runs: 0, drift: 0 };
188
+ cur.runs += 1;
189
+ cur.drift += p.drift;
190
+ pageStats.set(p.name, cur);
191
+ }
192
+ }
193
+ const hotspots = [...pageStats.entries()]
194
+ .map(([name, v]) => ({ name, ...v }))
195
+ .sort((a, b) => b.drift - a.drift || b.runs - a.runs)
196
+ .slice(0, 5);
197
+ return {
198
+ runs: entries.length,
199
+ first: entries[0]?.ts,
200
+ last: entries[entries.length - 1]?.ts,
201
+ lastVerdict: entries[entries.length - 1]?.verdict,
202
+ driftRate: s.length === 0 ? 0 : notPass / s.length,
203
+ coverage,
204
+ hotspots,
205
+ healed: entries.reduce((n, e) => n + (e.applied?.replacements ?? 0), 0),
206
+ trend,
207
+ };
208
+ }
209
+ /**
210
+ * Best-effort git stamp. A trend of verdicts is far more useful when a spike can
211
+ * be tied to a commit, and every alternative (asking the user to pass it, reading
212
+ * CI env vars per provider) is worse than shelling out once. Never throws, never
213
+ * blocks: no git, no repo, detached HEAD — the entry is written without it.
214
+ */
215
+ function gitStamp() {
216
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
217
+ const { execFileSync } = require('child_process');
218
+ const run = (args) => {
219
+ try {
220
+ const out = execFileSync('git', args, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 2000 });
221
+ const v = out.trim();
222
+ return v === '' ? undefined : v;
223
+ }
224
+ catch {
225
+ return undefined;
226
+ }
227
+ };
228
+ return { commit: run(['rev-parse', '--short', 'HEAD']), branch: run(['rev-parse', '--abbrev-ref', 'HEAD']) };
229
+ }
230
+ //# sourceMappingURL=history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.js","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":"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"}