@ia-qa/self-healing 1.7.7 → 1.7.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.
@@ -0,0 +1,225 @@
1
+ import { Usage } from './ingest';
2
+ /**
3
+ * The core of `ia-qa-heal explain` — turn a test FAILURE into a drift verdict.
4
+ *
5
+ * Every other verb in this package starts from a *contract*: map, baseline, diff.
6
+ * Nobody arrives holding a contract. They arrive holding a red build and an error
7
+ * message, and until now there was no door for that. `explain` is the door: it takes
8
+ * whatever the runner printed, works out which locator the failure is about, and asks
9
+ * the existing two-moment machinery what happened to it.
10
+ *
11
+ * Two rules shape everything below, and both exist to keep this from becoming the
12
+ * guessing engine it would otherwise be:
13
+ *
14
+ * 1. **It never parses a runner's grammar.** Not `NoSuchElementException`, not
15
+ * `Timed out retrying after 4000ms`, not `waiting for locator`. That list never ends
16
+ * and rots at every upstream release. Instead the failure text is intersected with
17
+ * the locator inventory `ingest` already built by reading the SOURCE — so the
18
+ * question is never "what does this runner's error mean" but "does this text quote a
19
+ * string my suite is known to use as a locator". That is framework-agnostic and
20
+ * language-agnostic for free, and it hands back `file:line` as a side effect.
21
+ *
22
+ * 2. **The contract is the arbiter, never the extractor.** Bending the inventory
23
+ * lookup (see below) means a stray key can now be read; the guard is that a matched
24
+ * string still has to be judged against the mapping before it can produce anything
25
+ * but BLOCK. A bad extraction therefore degrades to "I cannot tell", never to a
26
+ * wrong rewrite.
27
+ *
28
+ * A note on rule 2, because it bends a documented invariant. `ingest.ts` says: *every
29
+ * consumer of `usage.selectors` looks it up by a selector taken from the contract, so a
30
+ * key matching no element is never read* — that is what licenses its permissive Page
31
+ * Object pattern. This file is the first consumer to look up by a string taken from a
32
+ * *message*. False-positive keys become reachable here, and the containment tier below
33
+ * is where that is most likely. It is bounded on purpose: such a candidate finds
34
+ * nothing in the contract and comes back `not-measured` / `not-in-contract` — BLOCK,
35
+ * for a human, which is exactly what "I matched something meaningless" should cost.
36
+ *
37
+ * This file is pure: no fs, no config, no console, no browser. It is handed the
38
+ * failure text, the inventory, and an already-computed diff. `cli/explain.ts` does the
39
+ * I/O, exactly as `checks.ts` / `cli/check.ts` are split.
40
+ */
41
+ /** One failing test, as read from a JUnit file or from raw text. */
42
+ export interface FailureInput {
43
+ /** The test name, when the source gave one. */
44
+ test?: string;
45
+ /** The file the runner attributed it to, when it said. Not the locator's file. */
46
+ file?: string;
47
+ /** message + detail, concatenated — the haystack. */
48
+ text: string;
49
+ }
50
+ /**
51
+ * How a locator was recovered from the failure text.
52
+ *
53
+ * `quoted` is an exact match against a string the message put in quotes — the shape
54
+ * essentially every runner uses, and effectively certain. `contained` is the fallback
55
+ * for messages that quote nothing; it is reported as such, because a reader deciding
56
+ * whether to trust a finding needs to know which of the two produced it.
57
+ */
58
+ export type CandidateSource = 'quoted' | 'contained';
59
+ export interface LocatorCandidate {
60
+ kind: 'selector' | 'name';
61
+ /** The literal as the inventory holds it — for a name, as the test file writes it. */
62
+ literal: string;
63
+ source: CandidateSource;
64
+ sites: Array<{
65
+ file: string;
66
+ line: number;
67
+ }>;
68
+ }
69
+ /**
70
+ * Why a locator got the verdict it got. Exported because the terminal, the JSON and the
71
+ * MCP tool all state it, and a fourth wording would be a fourth thing to drift.
72
+ */
73
+ export type ExplainReason = 'resolves' | 'name-intact' | 'broken-repairable' | 'renamed-repairable' | 'rebound' | 'ambiguous' | 'gone' | 'unattributable' | 'never-bound' | 'not-in-contract' | 'not-measured';
74
+ export interface ExplainFinding {
75
+ kind: 'selector' | 'name';
76
+ literal: string;
77
+ source: CandidateSource;
78
+ verdict: 'PASS' | 'FIX' | 'BLOCK';
79
+ reason: ExplainReason;
80
+ /** Every place the suite writes this literal, from the inventory. */
81
+ sites: Array<{
82
+ file: string;
83
+ line: number;
84
+ }>;
85
+ /** Which mapped page produced the verdict, when one did. */
86
+ page?: string;
87
+ /**
88
+ * The rewrite to PROPOSE. Never applied here — `explain` is read-only, and the
89
+ * apply path stays `fix`, with all of its refusals intact.
90
+ */
91
+ repair?: {
92
+ from: string;
93
+ to: string;
94
+ };
95
+ /** What the locator reaches now — `rebound` only, and the reason it is BLOCK. */
96
+ now?: string;
97
+ }
98
+ export interface ExplainReport {
99
+ verdict: 'PASS' | 'FIX' | 'BLOCK';
100
+ failuresRead: number;
101
+ findings: ExplainFinding[];
102
+ /**
103
+ * Quoted strings in the failure that LOOK like locators but are in no inventory.
104
+ * Named rather than guessed at: it usually means `ingest` has not seen the file,
105
+ * or the locator is built dynamically and no static scan can reach it.
106
+ */
107
+ unmatched: string[];
108
+ pagesCompared: number;
109
+ /** Pairs the diff could not judge. A page left out is never counted as a pass. */
110
+ stalePages: string[];
111
+ }
112
+ export declare function unescapeXml(s: string): string;
113
+ /**
114
+ * Read the failures out of a JUnit XML report.
115
+ *
116
+ * JUnit is the input format for the same reason `--junit` is an output format here:
117
+ * every major runner in every language either emits it or has a reporter that does, so
118
+ * one parser covers pytest, Jest, Playwright, Cypress, JUnit, WebdriverIO, Robot and
119
+ * whatever comes next — without this package knowing any of them exist.
120
+ *
121
+ * Hand-rolled on purpose: this package ships with one runtime dependency and an XML
122
+ * library for four regexes would be a poor trade. There is no official JUnit XSD, so
123
+ * the conservative intersection is all that is safe to rely on anyway — `testcase`
124
+ * carrying `failure` or `error`, either self-closed or with a body.
125
+ */
126
+ export declare function parseJUnitFailures(xml: string): FailureInput[];
127
+ /**
128
+ * Every quoted run in the text, in the three quote styles error messages use.
129
+ *
130
+ * This is the whole of the "parser": runners quote the locator they could not find —
131
+ * `'.btn-primary'`, `"#pay-now"`, `` `Pay now` `` — and that convention is shared far
132
+ * more widely than any message grammar. Newlines terminate a run so an unbalanced quote
133
+ * in a stack trace cannot swallow the rest of the report.
134
+ *
135
+ * The three styles are scanned INDEPENDENTLY, and that is not a detail. One alternating
136
+ * regex scans left to right and lets whichever quote opens first consume everything
137
+ * after it — so WebdriverIO's `Can't call click on element with selector ".btn-primary"
138
+ * because element wasn't found` pairs the apostrophes of *Can't* and *wasn't*, swallows
139
+ * the real locator between them, and the failure comes back with nothing found. English
140
+ * prose is full of apostrophes; a separate pass per style cannot lose a locator to one.
141
+ *
142
+ * The cost is over-generation — that apostrophe pair still yields its nonsense run — and
143
+ * it is free: every run is then looked up in the inventory, and a run that names nothing
144
+ * a test writes matches nothing and disappears.
145
+ */
146
+ export declare function quotedRuns(text: string): string[];
147
+ /**
148
+ * Which inventoried locators does this failure name?
149
+ *
150
+ * Two tiers, and the second is skipped per kind as soon as the first finds anything:
151
+ * a message that quotes its locators has told us which they are, and adding
152
+ * containment hits on top of that only adds noise to an answer we already have.
153
+ */
154
+ export declare function extractLocators(failures: FailureInput[], usage: Usage): {
155
+ candidates: LocatorCandidate[];
156
+ unmatched: string[];
157
+ };
158
+ /**
159
+ * The shapes this file needs out of an already-computed diff, declared structurally
160
+ * rather than imported from `cli/diff.ts`.
161
+ *
162
+ * Same reason `nameDrift.ts` declares `RenamedRowLike`: it keeps the judge a pure
163
+ * function with no import edge into the CLI layer, so a test can hand it two literals
164
+ * and a hand-written report without a filesystem, a config, or a browser.
165
+ */
166
+ export interface ExplainRowLike {
167
+ status: string;
168
+ role: string;
169
+ name: string;
170
+ selector: string;
171
+ newName?: string;
172
+ healedSelector?: string;
173
+ }
174
+ export interface ExplainNameFindingLike {
175
+ oldName: string;
176
+ newName: string;
177
+ fixable: boolean;
178
+ unattributable: Array<{
179
+ file: string;
180
+ line: number;
181
+ }>;
182
+ }
183
+ export interface ExplainBindingLike {
184
+ selector: string;
185
+ was: string;
186
+ status: string;
187
+ now?: string;
188
+ healedSelector?: string;
189
+ }
190
+ export interface ExplainPageLike {
191
+ name: string;
192
+ rows: ExplainRowLike[];
193
+ nameDrift?: ExplainNameFindingLike[];
194
+ bindingDrift?: ExplainBindingLike[];
195
+ }
196
+ /**
197
+ * What the CURRENT capture saw each inventoried selector reach — `mapping/_resolved/`.
198
+ *
199
+ * Needed for the half of the answer a diff cannot give. A diff is silent about a
200
+ * selector that did not drift, and silence has two meanings that must never be
201
+ * conflated: *it still resolves* (PASS, the real product of this verb) and *nobody ever
202
+ * probed it* (not measured, and never a pass). Only the resolution files separate them.
203
+ */
204
+ export type ResolutionIndex = Map<string, Array<{
205
+ page: string;
206
+ status: string;
207
+ }>>;
208
+ export declare function judgeLocators(candidates: LocatorCandidate[], pages: ExplainPageLike[], resolutions: ResolutionIndex): ExplainFinding[];
209
+ /**
210
+ * The verdict of the whole failure — the worst of its locators.
211
+ *
212
+ * PASS only when every locator resolves, and it is the answer this verb exists for:
213
+ * *your selectors are fine, this red build is about something else*. It says nothing
214
+ * about what that something else is. A re-run that passes proves "not reproducible
215
+ * right now", never "flaky", and this tool has not re-run anything at all.
216
+ */
217
+ export declare function explainVerdict(findings: ExplainFinding[]): 'PASS' | 'FIX' | 'BLOCK';
218
+ export declare function explainHeadline(r: Pick<ExplainReport, 'verdict' | 'findings'>): string;
219
+ export declare function explainCounts(r: Pick<ExplainReport, 'failuresRead' | 'pagesCompared' | 'stalePages'>): string[];
220
+ /**
221
+ * The one-line "what do I do now" per reason, shared by every surface that renders a
222
+ * finding. Kept here so the terminal, the JSON and an agent cannot word the same
223
+ * verdict differently — the rule `checkHeadline` and `auditHeadline` already follow.
224
+ */
225
+ export declare const REASON_ADVICE: Record<ExplainReason, string>;
@@ -0,0 +1,373 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.REASON_ADVICE = void 0;
4
+ exports.unescapeXml = unescapeXml;
5
+ exports.parseJUnitFailures = parseJUnitFailures;
6
+ exports.quotedRuns = quotedRuns;
7
+ exports.extractLocators = extractLocators;
8
+ exports.judgeLocators = judgeLocators;
9
+ exports.explainVerdict = explainVerdict;
10
+ exports.explainHeadline = explainHeadline;
11
+ exports.explainCounts = explainCounts;
12
+ const ingest_1 = require("./ingest");
13
+ const nameLocators_1 = require("./nameLocators");
14
+ /* ------------------------------------------------------------------ *
15
+ * JUnit input
16
+ * ------------------------------------------------------------------ */
17
+ const ENTITIES = {
18
+ amp: '&',
19
+ lt: '<',
20
+ gt: '>',
21
+ quot: '"',
22
+ apos: "'",
23
+ };
24
+ function unescapeXml(s) {
25
+ return s.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z]+);/g, (whole, body) => {
26
+ if (body[0] === '#') {
27
+ const code = body[1] === 'x' || body[1] === 'X'
28
+ ? parseInt(body.slice(2), 16)
29
+ : parseInt(body.slice(1), 10);
30
+ return Number.isFinite(code) && code > 0 ? String.fromCodePoint(code) : whole;
31
+ }
32
+ const hit = ENTITIES[body.toLowerCase()];
33
+ return hit === undefined ? whole : hit;
34
+ });
35
+ }
36
+ function attrs(raw) {
37
+ const out = {};
38
+ const re = /([\w:.-]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
39
+ let m;
40
+ while ((m = re.exec(raw)) !== null) {
41
+ out[m[1]] = unescapeXml(m[2] !== undefined ? m[2] : m[3]);
42
+ }
43
+ return out;
44
+ }
45
+ /**
46
+ * Read the failures out of a JUnit XML report.
47
+ *
48
+ * JUnit is the input format for the same reason `--junit` is an output format here:
49
+ * every major runner in every language either emits it or has a reporter that does, so
50
+ * one parser covers pytest, Jest, Playwright, Cypress, JUnit, WebdriverIO, Robot and
51
+ * whatever comes next — without this package knowing any of them exist.
52
+ *
53
+ * Hand-rolled on purpose: this package ships with one runtime dependency and an XML
54
+ * library for four regexes would be a poor trade. There is no official JUnit XSD, so
55
+ * the conservative intersection is all that is safe to rely on anyway — `testcase`
56
+ * carrying `failure` or `error`, either self-closed or with a body.
57
+ */
58
+ function parseJUnitFailures(xml) {
59
+ const out = [];
60
+ const caseRe = /<testcase\b([^>]*?)(?:\/>|>([\s\S]*?)<\/testcase\s*>)/g;
61
+ let c;
62
+ while ((c = caseRe.exec(xml)) !== null) {
63
+ const body = c[2];
64
+ if (!body)
65
+ continue;
66
+ const meta = attrs(c[1]);
67
+ const failRe = /<(failure|error)\b([^>]*?)(?:\/>|>([\s\S]*?)<\/\1\s*>)/g;
68
+ let f;
69
+ while ((f = failRe.exec(body)) !== null) {
70
+ const fa = attrs(f[2]);
71
+ const detail = f[3] ? unescapeXml(stripCdata(f[3])) : '';
72
+ const text = [fa.message ?? '', detail].filter(Boolean).join('\n');
73
+ if (!text.trim())
74
+ continue;
75
+ out.push({
76
+ test: meta.name || undefined,
77
+ file: meta.file || meta.classname || undefined,
78
+ text,
79
+ });
80
+ }
81
+ }
82
+ return out;
83
+ }
84
+ function stripCdata(s) {
85
+ return s.replace(/<!\[CDATA\[([\s\S]*?)\]\]>/g, '$1');
86
+ }
87
+ /* ------------------------------------------------------------------ *
88
+ * Extraction — intersection, not parsing
89
+ * ------------------------------------------------------------------ */
90
+ /**
91
+ * A `contained` match must be at least this long. Short literals turn a long stack
92
+ * trace into a lottery: `.btn` occurs inside a dozen unrelated words, and a false
93
+ * candidate costs a reader more than a missed one — they can always pass `--message`
94
+ * with the locator themselves.
95
+ */
96
+ const MIN_CONTAINED = 5;
97
+ /** Beyond this, the failure is not about one locator and listing more helps nobody. */
98
+ const MAX_CANDIDATES = 25;
99
+ /**
100
+ * Every quoted run in the text, in the three quote styles error messages use.
101
+ *
102
+ * This is the whole of the "parser": runners quote the locator they could not find —
103
+ * `'.btn-primary'`, `"#pay-now"`, `` `Pay now` `` — and that convention is shared far
104
+ * more widely than any message grammar. Newlines terminate a run so an unbalanced quote
105
+ * in a stack trace cannot swallow the rest of the report.
106
+ *
107
+ * The three styles are scanned INDEPENDENTLY, and that is not a detail. One alternating
108
+ * regex scans left to right and lets whichever quote opens first consume everything
109
+ * after it — so WebdriverIO's `Can't call click on element with selector ".btn-primary"
110
+ * because element wasn't found` pairs the apostrophes of *Can't* and *wasn't*, swallows
111
+ * the real locator between them, and the failure comes back with nothing found. English
112
+ * prose is full of apostrophes; a separate pass per style cannot lose a locator to one.
113
+ *
114
+ * The cost is over-generation — that apostrophe pair still yields its nonsense run — and
115
+ * it is free: every run is then looked up in the inventory, and a run that names nothing
116
+ * a test writes matches nothing and disappears.
117
+ */
118
+ function quotedRuns(text) {
119
+ const hits = [];
120
+ for (const re of [/'([^'\n]{1,200})'/g, /"([^"\n]{1,200})"/g, /`([^`\n]{1,200})`/g]) {
121
+ let m;
122
+ while ((m = re.exec(text)) !== null) {
123
+ if (m[1] && m[1].trim())
124
+ hits.push({ at: m.index, value: m[1] });
125
+ }
126
+ }
127
+ return hits.sort((a, b) => a.at - b.at).map((h) => h.value);
128
+ }
129
+ /**
130
+ * Which inventoried locators does this failure name?
131
+ *
132
+ * Two tiers, and the second is skipped per kind as soon as the first finds anything:
133
+ * a message that quotes its locators has told us which they are, and adding
134
+ * containment hits on top of that only adds noise to an answer we already have.
135
+ */
136
+ function extractLocators(failures, usage) {
137
+ const text = failures.map((f) => f.text).join('\n');
138
+ const runs = quotedRuns(text);
139
+ const runSet = new Set(runs);
140
+ const normRuns = new Set(runs.map((r) => (0, nameLocators_1.normalizeName)(r)));
141
+ const selectorKeys = Object.keys(usage.selectors ?? {});
142
+ const nameKeys = Object.keys(usage.names ?? {});
143
+ const selectors = [];
144
+ const names = [];
145
+ for (const key of selectorKeys) {
146
+ if (!runSet.has(key))
147
+ continue;
148
+ selectors.push({
149
+ kind: 'selector',
150
+ literal: key,
151
+ source: 'quoted',
152
+ sites: (usage.selectors[key] ?? []).map((s) => ({ file: s.file, line: s.line })),
153
+ });
154
+ }
155
+ for (const key of nameKeys) {
156
+ if (!normRuns.has(key))
157
+ continue;
158
+ const sites = usage.names?.[key] ?? [];
159
+ names.push({
160
+ kind: 'name',
161
+ // The inventory is keyed normalized, but every message a human reads must quote
162
+ // what is actually in their file — `"save"` when the line says `'Save'` costs a grep.
163
+ literal: sites[0]?.text ?? key,
164
+ source: 'quoted',
165
+ sites: sites.map((s) => ({ file: s.file, line: s.line })),
166
+ });
167
+ }
168
+ if (selectors.length === 0) {
169
+ for (const key of selectorKeys) {
170
+ if (key.length < MIN_CONTAINED || !text.includes(key))
171
+ continue;
172
+ selectors.push({
173
+ kind: 'selector',
174
+ literal: key,
175
+ source: 'contained',
176
+ sites: (usage.selectors[key] ?? []).map((s) => ({ file: s.file, line: s.line })),
177
+ });
178
+ }
179
+ }
180
+ if (names.length === 0) {
181
+ const normText = (0, nameLocators_1.normalizeName)(text);
182
+ for (const key of nameKeys) {
183
+ if (key.length < MIN_CONTAINED || !normText.includes(key))
184
+ continue;
185
+ const sites = usage.names?.[key] ?? [];
186
+ names.push({
187
+ kind: 'name',
188
+ literal: sites[0]?.text ?? key,
189
+ source: 'contained',
190
+ sites: sites.map((s) => ({ file: s.file, line: s.line })),
191
+ });
192
+ }
193
+ }
194
+ const candidates = [...dropSubsumed(selectors), ...dropSubsumed(names)].slice(0, MAX_CANDIDATES);
195
+ // A quoted string that reads like a selector and is in no inventory is worth naming:
196
+ // it is the signature of an un-ingested file or of a dynamically built locator, and
197
+ // both are things the user can act on. Silence there would look like "nothing found".
198
+ const known = new Set(selectorKeys);
199
+ const knownNames = new Set(nameKeys);
200
+ const unmatched = Array.from(new Set(runs)).filter((r) => (0, ingest_1.plausibleSelector)(r) && !known.has(r) && !knownNames.has((0, nameLocators_1.normalizeName)(r)));
201
+ return { candidates, unmatched };
202
+ }
203
+ /**
204
+ * Drop a `contained` literal that sits inside a longer accepted one — `.btn` reported
205
+ * beside `.btn-primary` is one finding pretending to be two. `quoted` literals are
206
+ * never dropped: if the message quoted `.btn`, then `.btn` is what failed, whatever
207
+ * else the inventory happens to contain.
208
+ */
209
+ function dropSubsumed(list) {
210
+ const sorted = [...list].sort((a, b) => b.literal.length - a.literal.length);
211
+ const kept = [];
212
+ for (const c of sorted) {
213
+ if (c.source === 'contained' && kept.some((k) => k.literal.includes(c.literal)))
214
+ continue;
215
+ kept.push(c);
216
+ }
217
+ return kept;
218
+ }
219
+ const HEALTHY_BINDING = new Set(['bound', 'anchored', 'outside']);
220
+ function judgeSelector(c, pages, resolutions) {
221
+ const base = { kind: 'selector', literal: c.literal, source: c.source, sites: c.sites };
222
+ // The two-moment verdict first: it is the only one that can see a binding that broke,
223
+ // and the only one that can see the worst case here — a string that now reaches a
224
+ // DIFFERENT element, where the test goes green and acts on the wrong thing.
225
+ for (const p of pages) {
226
+ const d = (p.bindingDrift ?? []).find((x) => x.selector === c.literal);
227
+ if (!d)
228
+ continue;
229
+ if (d.status === 'broken' && d.healedSelector) {
230
+ return { ...base, verdict: 'FIX', reason: 'broken-repairable', page: p.name,
231
+ repair: { from: c.literal, to: d.healedSelector } };
232
+ }
233
+ if (d.status === 'rebound') {
234
+ return { ...base, verdict: 'BLOCK', reason: 'rebound', page: p.name, now: d.now };
235
+ }
236
+ if (d.status === 'ambiguous') {
237
+ return { ...base, verdict: 'BLOCK', reason: 'ambiguous', page: p.name };
238
+ }
239
+ return { ...base, verdict: 'BLOCK', reason: 'gone', page: p.name };
240
+ }
241
+ // The literal may itself BE a contract selector (a suite written against `map`'s
242
+ // output, or a `data-testid` the extractor also picked). Then the element rows judge it.
243
+ for (const p of pages) {
244
+ const row = p.rows.find((r) => r.selector === c.literal);
245
+ if (!row)
246
+ continue;
247
+ if (row.status === 'healable' && row.healedSelector) {
248
+ return { ...base, verdict: 'FIX', reason: 'broken-repairable', page: p.name,
249
+ repair: { from: c.literal, to: row.healedSelector } };
250
+ }
251
+ if (row.status === 'ok' || row.status === 'renamed') {
252
+ return { ...base, verdict: 'PASS', reason: 'resolves', page: p.name };
253
+ }
254
+ if (row.status === 'rebound')
255
+ return { ...base, verdict: 'BLOCK', reason: 'rebound', page: p.name };
256
+ if (row.status === 'ambiguous')
257
+ return { ...base, verdict: 'BLOCK', reason: 'ambiguous', page: p.name };
258
+ return { ...base, verdict: 'BLOCK', reason: 'gone', page: p.name };
259
+ }
260
+ // No drift row. Did anything actually look at this string?
261
+ const seen = resolutions.get(c.literal);
262
+ if (!seen || seen.length === 0) {
263
+ return { ...base, verdict: 'BLOCK', reason: 'not-measured' };
264
+ }
265
+ const healthy = seen.find((s) => HEALTHY_BINDING.has(s.status));
266
+ if (healthy)
267
+ return { ...base, verdict: 'PASS', reason: 'resolves', page: healthy.page };
268
+ if (seen.some((s) => s.status === 'ambiguous')) {
269
+ return { ...base, verdict: 'BLOCK', reason: 'ambiguous', page: seen[0].page };
270
+ }
271
+ // Probed everywhere, reaches nothing anywhere — and it did not break between the two
272
+ // moments, so there is no surviving element to heal towards. `audit` is the verb that
273
+ // answers "did it ever exist", not this one.
274
+ return { ...base, verdict: 'BLOCK', reason: 'never-bound', page: seen[0].page };
275
+ }
276
+ function judgeName(c, pages) {
277
+ const base = { kind: 'name', literal: c.literal, source: c.source, sites: c.sites };
278
+ const key = (0, nameLocators_1.normalizeName)(c.literal);
279
+ for (const p of pages) {
280
+ const f = (p.nameDrift ?? []).find((x) => (0, nameLocators_1.normalizeName)(x.oldName) === key);
281
+ if (!f)
282
+ continue;
283
+ // A rewrite this tool cannot prove correct is not a rewrite. `getByText` and
284
+ // `cy.contains` name a string without saying what carries it, and the contract holds
285
+ // interactive elements only — editing one could break a test that was passing.
286
+ if (f.unattributable.length > 0) {
287
+ return { ...base, verdict: 'BLOCK', reason: 'unattributable', page: p.name };
288
+ }
289
+ if (f.fixable) {
290
+ return { ...base, verdict: 'FIX', reason: 'renamed-repairable', page: p.name,
291
+ repair: { from: c.literal, to: f.newName } };
292
+ }
293
+ return { ...base, verdict: 'BLOCK', reason: 'ambiguous', page: p.name };
294
+ }
295
+ for (const p of pages) {
296
+ const row = p.rows.find((r) => (0, nameLocators_1.normalizeName)(r.name) === key);
297
+ if (!row)
298
+ continue;
299
+ // `healable` moved the SELECTOR, not the label — a locator that names a role and a
300
+ // name does not care where the element sits in the DOM. For this kind, that is a pass.
301
+ if (row.status === 'ok' || row.status === 'healable') {
302
+ return { ...base, verdict: 'PASS', reason: 'name-intact', page: p.name };
303
+ }
304
+ if (row.status === 'ambiguous')
305
+ return { ...base, verdict: 'BLOCK', reason: 'ambiguous', page: p.name };
306
+ if (row.status === 'rebound')
307
+ return { ...base, verdict: 'BLOCK', reason: 'rebound', page: p.name };
308
+ // `renamed` with no nameDrift finding beside it means the inventory never saw this
309
+ // call site. Reporting the label as intact would be exactly wrong.
310
+ return { ...base, verdict: 'BLOCK', reason: row.status === 'renamed' ? 'unattributable' : 'gone', page: p.name };
311
+ }
312
+ // The label is nowhere in the compared pages. That is not a pass — it may live on a
313
+ // page nobody mapped, which is a coverage answer, not a drift one.
314
+ return { ...base, verdict: 'BLOCK', reason: 'not-in-contract' };
315
+ }
316
+ function judgeLocators(candidates, pages, resolutions) {
317
+ const findings = candidates.map((c) => c.kind === 'selector' ? judgeSelector(c, pages, resolutions) : judgeName(c, pages));
318
+ const rank = { BLOCK: 0, FIX: 1, PASS: 2 };
319
+ return findings.sort((a, b) => rank[a.verdict] - rank[b.verdict]);
320
+ }
321
+ /**
322
+ * The verdict of the whole failure — the worst of its locators.
323
+ *
324
+ * PASS only when every locator resolves, and it is the answer this verb exists for:
325
+ * *your selectors are fine, this red build is about something else*. It says nothing
326
+ * about what that something else is. A re-run that passes proves "not reproducible
327
+ * right now", never "flaky", and this tool has not re-run anything at all.
328
+ */
329
+ function explainVerdict(findings) {
330
+ if (findings.some((f) => f.verdict === 'BLOCK'))
331
+ return 'BLOCK';
332
+ if (findings.some((f) => f.verdict === 'FIX'))
333
+ return 'FIX';
334
+ return 'PASS';
335
+ }
336
+ function explainHeadline(r) {
337
+ const n = r.findings.length;
338
+ const locs = `${n} locator${n === 1 ? '' : 's'}`;
339
+ if (r.verdict === 'PASS')
340
+ return `PASS · ${locs} still resolve${n === 1 ? 's' : ''} — not selector drift`;
341
+ if (r.verdict === 'FIX') {
342
+ const k = r.findings.filter((f) => f.verdict === 'FIX').length;
343
+ return `FIX · ${k} of ${locs} drifted, repair proposed`;
344
+ }
345
+ const k = r.findings.filter((f) => f.verdict === 'BLOCK').length;
346
+ return `BLOCK · ${k} of ${locs} need${k === 1 ? 's' : ''} a human`;
347
+ }
348
+ function explainCounts(r) {
349
+ return [
350
+ `${r.failuresRead} failure${r.failuresRead === 1 ? '' : 's'} read`,
351
+ `${r.pagesCompared} page${r.pagesCompared === 1 ? '' : 's'} compared`,
352
+ r.stalePages.length > 0 ? `${r.stalePages.length} stale` : '',
353
+ ].filter(Boolean);
354
+ }
355
+ /**
356
+ * The one-line "what do I do now" per reason, shared by every surface that renders a
357
+ * finding. Kept here so the terminal, the JSON and an agent cannot word the same
358
+ * verdict differently — the rule `checkHeadline` and `auditHeadline` already follow.
359
+ */
360
+ exports.REASON_ADVICE = {
361
+ resolves: 'This selector still reaches its element. The failure is not drift — look at timing, test data, or an app error.',
362
+ 'name-intact': 'This label still exists on the mapped contract. The failure is not a rename — look elsewhere.',
363
+ 'broken-repairable': 'The string reaches nothing, but its element survived. `ia-qa-heal fix` can rewrite it.',
364
+ 'renamed-repairable': 'The element is the same, its label changed. `ia-qa-heal fix` can rewrite the role-stating calls.',
365
+ rebound: 'WORST CASE: it now reaches a DIFFERENT element. The test can go green while acting on the wrong thing. Never rewritten automatically — read it yourself.',
366
+ ambiguous: 'Two or more elements answer to this. Any rewrite would be a coin flip, so none is offered.',
367
+ gone: 'The element it pointed at is not in the current capture. Nothing to rewrite towards.',
368
+ unattributable: 'The call names a string without saying what carries it (getByText, cy.contains…). Rewriting it could break a test that was passing, so it is only reported.',
369
+ 'never-bound': 'This string reached nothing at either moment. It is not drift — check the selector was ever correct, or that the page is mapped.',
370
+ 'not-in-contract': 'No compared page holds this label. It may live on a page you do not map — `ia-qa-heal discover` lists those.',
371
+ 'not-measured': 'Nothing probed this string. Run `ia-qa-heal ingest` then `ia-qa-heal map` so the binding exists, then ask again.',
372
+ };
373
+ //# sourceMappingURL=explain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"explain.js","sourceRoot":"","sources":["../src/explain.ts"],"names":[],"mappings":"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"}