@ia-qa/self-healing 1.7.10 → 1.7.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -2
- package/dist/aom.d.ts +36 -0
- package/dist/aom.js.map +1 -1
- package/dist/browser/match.d.ts +10 -0
- package/dist/browser/match.js +16 -6
- package/dist/browser/match.js.map +1 -1
- package/dist/browser/openables.d.ts +45 -0
- package/dist/browser/openables.js +128 -0
- package/dist/browser/openables.js.map +1 -0
- package/dist/captureState.d.ts +66 -0
- package/dist/captureState.js +149 -0
- package/dist/captureState.js.map +1 -0
- package/dist/cli/args.js +5 -4
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/baseline.js +22 -0
- package/dist/cli/baseline.js.map +1 -1
- package/dist/cli/diff.d.ts +50 -0
- package/dist/cli/diff.js +114 -11
- package/dist/cli/diff.js.map +1 -1
- package/dist/cli/emit.d.ts +17 -0
- package/dist/cli/emit.js +25 -0
- package/dist/cli/emit.js.map +1 -1
- package/dist/cli/exitCodes.d.ts +29 -0
- package/dist/cli/exitCodes.js +41 -0
- package/dist/cli/exitCodes.js.map +1 -0
- package/dist/cli/explain.js +11 -2
- package/dist/cli/explain.js.map +1 -1
- package/dist/cli/fix.d.ts +16 -0
- package/dist/cli/fix.js +85 -11
- package/dist/cli/fix.js.map +1 -1
- package/dist/cli/index.js +56 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.d.ts +39 -1
- package/dist/cli/init.js +216 -12
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/map.d.ts +2 -0
- package/dist/cli/map.js +126 -4
- package/dist/cli/map.js.map +1 -1
- package/dist/cli/run.js +8 -26
- package/dist/cli/run.js.map +1 -1
- package/dist/config.d.ts +45 -0
- package/dist/config.js.map +1 -1
- package/dist/explain.d.ts +27 -0
- package/dist/explain.js +88 -7
- package/dist/explain.js.map +1 -1
- package/dist/explore.d.ts +138 -0
- package/dist/explore.js +224 -0
- package/dist/explore.js.map +1 -0
- package/dist/nextStep.d.ts +79 -0
- package/dist/nextStep.js +107 -0
- package/dist/nextStep.js.map +1 -0
- package/dist/systemicDelta.d.ts +129 -0
- package/dist/systemicDelta.js +164 -0
- package/dist/systemicDelta.js.map +1 -0
- package/dist/ui/server.js +5 -1
- package/dist/ui/server.js.map +1 -1
- package/package.json +1 -1
- package/skills/ia-qa-heal/SKILL.md +56 -2
package/dist/cli/args.js
CHANGED
|
@@ -15,13 +15,13 @@ exports.rejectUnknownFlags = rejectUnknownFlags;
|
|
|
15
15
|
* leading dash is enough to tell a flag from a positional.
|
|
16
16
|
*/
|
|
17
17
|
exports.KNOWN_FLAGS = {
|
|
18
|
-
init: [],
|
|
18
|
+
init: ['--yes', '--base-url', '--pages', '--discover', '--tests', '--test-command', '--bootstrap'],
|
|
19
19
|
discover: ['--sitemap', '--crawl', '--no-reveal', '--strict-host', '--apply', '--max', '--depth', '--session'],
|
|
20
20
|
login: ['--url', '--session'],
|
|
21
|
-
map: ['--screenshots', '--session'],
|
|
22
|
-
baseline: [],
|
|
21
|
+
map: ['--screenshots', '--session', '--deep', '--depth', '--deep-budget'],
|
|
22
|
+
baseline: ['--dry-run'],
|
|
23
23
|
diff: ['--strict', '--json', '--junit', '--dir', '--report', '--open'],
|
|
24
|
-
fix: ['--dry-run', '--locators', '--dir'],
|
|
24
|
+
fix: ['--dry-run', '--locators', '--dir', '--json'],
|
|
25
25
|
ingest: ['--json'],
|
|
26
26
|
audit: ['--strict', '--json'],
|
|
27
27
|
explain: ['--junit', '--message', '--strict', '--json'],
|
|
@@ -30,6 +30,7 @@ exports.KNOWN_FLAGS = {
|
|
|
30
30
|
run: ['--yes', '--no-verify', '--locators', '--report', '--junit', '--json', '--open'],
|
|
31
31
|
skill: ['--install', '--user', '--dir', '--print', '--force'],
|
|
32
32
|
history: ['--json', '--limit'],
|
|
33
|
+
'exit-codes': ['--json'],
|
|
33
34
|
ui: ['--port', '--no-open', '--shortcut', '--keep-alive', '--forget', '--offline'],
|
|
34
35
|
};
|
|
35
36
|
function rejectUnknownFlags(command, args) {
|
package/dist/cli/args.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":";;;AAgCA,gDAeC;AA/CD;;;;;;;;;;;GAWG;AACU,QAAA,WAAW,GAAsC;IAC5D,IAAI,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,aAAa,CAAC;IAClG,QAAQ,EAAE,CAAC,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,eAAe,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,CAAC;IAC9G,KAAK,EAAE,CAAC,OAAO,EAAE,WAAW,CAAC;IAC7B,GAAG,EAAE,CAAC,eAAe,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,eAAe,CAAC;IACzE,QAAQ,EAAE,CAAC,WAAW,CAAC;IACvB,IAAI,EAAE,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC;IACtE,GAAG,EAAE,CAAC,WAAW,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,CAAC;IACnD,MAAM,EAAE,CAAC,QAAQ,CAAC;IAClB,KAAK,EAAE,CAAC,UAAU,EAAE,QAAQ,CAAC;IAC7B,OAAO,EAAE,CAAC,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,CAAC;IACvD,KAAK,EAAE,CAAC,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,eAAe,CAAC;IAC3D,KAAK,EAAE,CAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC;IAChD,GAAG,EAAE,CAAC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC;IACtF,KAAK,EAAE,CAAC,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC;IAC7D,OAAO,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC9B,YAAY,EAAE,CAAC,QAAQ,CAAC;IACxB,EAAE,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,CAAC;CACnF,CAAC;AAEF,SAAgB,kBAAkB,CAAC,OAAe,EAAE,IAAc;IAChE,MAAM,KAAK,GAAG,mBAAW,CAAC,OAAO,CAAC,CAAC;IACnC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO;IAChC,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QACjC,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,SAAS;QAChC,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC1C,MAAM,IAAI,KAAK,CACb,eAAe,OAAO,0FAA0F;gBAC9G,4GAA4G;gBAC5G,8FAA8F,CACjG,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,qBAAqB,OAAO,8CAA8C,CAAC,CAAC;IAChH,CAAC;AACH,CAAC"}
|
package/dist/cli/baseline.js
CHANGED
|
@@ -60,6 +60,11 @@ const summary_1 = require("./summary");
|
|
|
60
60
|
* silently goes stale, and twice the diff to review on every promotion.
|
|
61
61
|
*/
|
|
62
62
|
async function runBaseline(args = []) {
|
|
63
|
+
// The one destructive thing this verb does is DELETE: a baseline file the new mapping no
|
|
64
|
+
// longer produces is removed, on purpose, because keeping it diffs against nothing
|
|
65
|
+
// forever. That is right and it is also the step worth being able to preview — a page
|
|
66
|
+
// dropped from config.json by accident looks exactly like one dropped on purpose.
|
|
67
|
+
const dryRun = args.includes('--dry-run');
|
|
63
68
|
(0, captureMerge_1.mergeCapturesAnnounced)();
|
|
64
69
|
const from = (0, config_1.mappingDir)();
|
|
65
70
|
const to = (0, config_1.baselineDir)();
|
|
@@ -87,6 +92,23 @@ async function runBaseline(args = []) {
|
|
|
87
92
|
const staleFiles = jsonFilesIn(to).filter((f) => !pages.includes(f));
|
|
88
93
|
const staleLayouts = jsonFilesIn(path.join(to, config_1.LAYOUT_DIRNAME)).filter((f) => !layouts.includes(f));
|
|
89
94
|
const staleResolved = jsonFilesIn(path.join(to, config_1.RESOLVED_DIRNAME)).filter((f) => !resolved.includes(f));
|
|
95
|
+
if (dryRun) {
|
|
96
|
+
const doomed = [...staleFiles, ...staleLayouts, ...staleResolved];
|
|
97
|
+
console.log('\n📌 Baseline promotion — dry run, nothing written.');
|
|
98
|
+
console.log(` would promote ${pages.length} page${pages.length === 1 ? '' : 's'}` +
|
|
99
|
+
(layouts.length > 0 ? ` · ${layouts.length} layout${layouts.length === 1 ? '' : 's'}` : '') +
|
|
100
|
+
(resolved.length > 0 ? ` · ${resolved.length} with selector bindings` : ''));
|
|
101
|
+
if (doomed.length > 0) {
|
|
102
|
+
console.log(` would DELETE ${doomed.length} baseline file${doomed.length === 1 ? '' : 's'} the current mapping no longer produces:`);
|
|
103
|
+
for (const f of doomed.slice(0, 10))
|
|
104
|
+
console.log(` ${f}`);
|
|
105
|
+
if (doomed.length > 10)
|
|
106
|
+
console.log(` +${doomed.length - 10} more`);
|
|
107
|
+
console.log(' A page dropped from config.json by accident looks exactly like one dropped on purpose.');
|
|
108
|
+
}
|
|
109
|
+
(0, summary_1.summaryLine)('baseline', 'info', `dry run · ${pages.length + layouts.length} would be promoted`, [doomed.length > 0 ? `${doomed.length} would be deleted` : '']);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
90
112
|
fs.mkdirSync(to, { recursive: true });
|
|
91
113
|
for (const file of pages) {
|
|
92
114
|
fs.copyFileSync(path.join(from, file), path.join(to, file));
|
package/dist/cli/baseline.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"baseline.js","sourceRoot":"","sources":["../../src/cli/baseline.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,
|
|
1
|
+
{"version":3,"file":"baseline.js","sourceRoot":"","sources":["../../src/cli/baseline.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/diff.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { BindingDrift } from '../resolution';
|
|
|
3
3
|
import { Usage } from '../ingest';
|
|
4
4
|
import { NameDriftFinding } from '../nameDrift';
|
|
5
5
|
import { FixOutcome, ReportEvidence } from '../htmlReport';
|
|
6
|
+
import { SystemicOutcome } from '../systemicDelta';
|
|
6
7
|
/**
|
|
7
8
|
* `ia-qa-heal diff [baseline] [current] [--strict] [--json] [--dir]`
|
|
8
9
|
*
|
|
@@ -77,6 +78,20 @@ export declare function runDiff(args: string[]): Promise<void>;
|
|
|
77
78
|
* its author believes it does — and in the `referenced-by-tests` case, believing it
|
|
78
79
|
* would mean believing a green gate over a suite that is about to go red.
|
|
79
80
|
*/
|
|
81
|
+
/**
|
|
82
|
+
* The cross-page section: what moved everywhere at once.
|
|
83
|
+
*
|
|
84
|
+
* It leads with the shape of the fact ("2 elements, on all 142 pages") because that
|
|
85
|
+
* shape is the diagnosis — an app does not usually delete the same control from every
|
|
86
|
+
* page between two runs, and a reader who sees the count immediately stops reading it
|
|
87
|
+
* as 288 separate problems.
|
|
88
|
+
*
|
|
89
|
+
* The remedy is always printed, and it differs by policy: when the group is held out
|
|
90
|
+
* the reader needs to know a decision was made *for* them and how to see it; when it
|
|
91
|
+
* is still in the verdict they need the two candidate causes, in the order that costs
|
|
92
|
+
* least to check.
|
|
93
|
+
*/
|
|
94
|
+
export declare function printSystemic(outcome: SystemicOutcome): void;
|
|
80
95
|
export declare function printNameMasks(outcome: MaskOutcome | undefined): void;
|
|
81
96
|
/**
|
|
82
97
|
* The name-drift section of a human verdict.
|
|
@@ -101,6 +116,27 @@ export interface DirDiffCollection {
|
|
|
101
116
|
}>;
|
|
102
117
|
/** Pairs where both sides came out of the same `map` run — incapable of showing drift. */
|
|
103
118
|
stale: string[];
|
|
119
|
+
/**
|
|
120
|
+
* Elements that moved on every page at once — one fact about the capture, not one
|
|
121
|
+
* per page. Present on every cross-page walk; `groups` is empty when nothing
|
|
122
|
+
* qualifies, which is the ordinary case and costs nothing.
|
|
123
|
+
*/
|
|
124
|
+
systemic: SystemicOutcome;
|
|
125
|
+
/**
|
|
126
|
+
* The two sides were explored to different depths (`map --deep`), when either declares one.
|
|
127
|
+
*
|
|
128
|
+
* A deep baseline diffed against a shallow capture reports every element behind a menu as
|
|
129
|
+
* `lost` — a BLOCK over an app where nothing changed, manufactured entirely by how the two
|
|
130
|
+
* contracts were captured. It is the same class as `run`'s source mismatch, and it is worse:
|
|
131
|
+
* a deep contract can hold more elements behind interaction than in front of it, so the
|
|
132
|
+
* false verdict is not a handful of rows but most of the page.
|
|
133
|
+
*
|
|
134
|
+
* `null` when both sides agree (including both absent — two shallow contracts).
|
|
135
|
+
*/
|
|
136
|
+
depthMismatch: {
|
|
137
|
+
baseline: number;
|
|
138
|
+
current: number;
|
|
139
|
+
} | null;
|
|
104
140
|
}
|
|
105
141
|
/**
|
|
106
142
|
* A broken binding whose element survived is repairable — FIX. Everything else is a
|
|
@@ -138,6 +174,20 @@ export interface DirReport {
|
|
|
138
174
|
nameDrift?: NameDriftFinding[];
|
|
139
175
|
/** `config.nameMask`'s effect across the walk — applied patterns and refusals, unioned. */
|
|
140
176
|
nameMask?: MaskOutcome;
|
|
177
|
+
/**
|
|
178
|
+
* What to run next, or `null` when the next step is not a command this caller should
|
|
179
|
+
* run. See `nextStep.ts` — BLOCK is deliberately null.
|
|
180
|
+
*/
|
|
181
|
+
next_command?: string | null;
|
|
182
|
+
/** Why, in one sentence. */
|
|
183
|
+
next?: string;
|
|
184
|
+
/**
|
|
185
|
+
* Elements that moved on every page at once. Set only when a group was found, so a
|
|
186
|
+
* consumer that never meets one sees the document it always saw. When `policy` is
|
|
187
|
+
* `held-out` the counts above no longer include these rows — the group is the record
|
|
188
|
+
* of what left, which is why it is emitted rather than merely printed.
|
|
189
|
+
*/
|
|
190
|
+
systemic?: SystemicOutcome;
|
|
141
191
|
/**
|
|
142
192
|
* Selector strings from the suite whose binding to a contracted element broke.
|
|
143
193
|
* Present only once `ingest` has inventoried the tests **and** `map` has resolved
|
package/dist/cli/diff.js
CHANGED
|
@@ -41,6 +41,7 @@ exports.pageShotMap = pageShotMap;
|
|
|
41
41
|
exports.buildEvidence = buildEvidence;
|
|
42
42
|
exports.writeDriftReport = writeDriftReport;
|
|
43
43
|
exports.runDiff = runDiff;
|
|
44
|
+
exports.printSystemic = printSystemic;
|
|
44
45
|
exports.printNameMasks = printNameMasks;
|
|
45
46
|
exports.printNameDrift = printNameDrift;
|
|
46
47
|
exports.driftSummaryLine = driftSummaryLine;
|
|
@@ -63,6 +64,8 @@ const htmlReport_1 = require("../htmlReport");
|
|
|
63
64
|
const evidence_1 = require("../evidence");
|
|
64
65
|
const summary_1 = require("./summary");
|
|
65
66
|
const emit_1 = require("./emit");
|
|
67
|
+
const systemicDelta_1 = require("../systemicDelta");
|
|
68
|
+
const nextStep_1 = require("../nextStep");
|
|
66
69
|
/**
|
|
67
70
|
* `ia-qa-heal diff [baseline] [current] [--strict] [--json] [--dir]`
|
|
68
71
|
*
|
|
@@ -408,6 +411,50 @@ async function runDiff(args) {
|
|
|
408
411
|
* its author believes it does — and in the `referenced-by-tests` case, believing it
|
|
409
412
|
* would mean believing a green gate over a suite that is about to go red.
|
|
410
413
|
*/
|
|
414
|
+
/**
|
|
415
|
+
* The cross-page section: what moved everywhere at once.
|
|
416
|
+
*
|
|
417
|
+
* It leads with the shape of the fact ("2 elements, on all 142 pages") because that
|
|
418
|
+
* shape is the diagnosis — an app does not usually delete the same control from every
|
|
419
|
+
* page between two runs, and a reader who sees the count immediately stops reading it
|
|
420
|
+
* as 288 separate problems.
|
|
421
|
+
*
|
|
422
|
+
* The remedy is always printed, and it differs by policy: when the group is held out
|
|
423
|
+
* the reader needs to know a decision was made *for* them and how to see it; when it
|
|
424
|
+
* is still in the verdict they need the two candidate causes, in the order that costs
|
|
425
|
+
* least to check.
|
|
426
|
+
*/
|
|
427
|
+
function printSystemic(outcome) {
|
|
428
|
+
if (outcome.groups.length === 0)
|
|
429
|
+
return;
|
|
430
|
+
const widest = outcome.groups[0].pages.length;
|
|
431
|
+
const n = outcome.groups.length;
|
|
432
|
+
const icon = outcome.policy === 'held-out' ? '🧭' : '⛔';
|
|
433
|
+
console.log(`\n ${icon} ${n} element${n === 1 ? '' : 's'} moved on every page at once ` +
|
|
434
|
+
`(${widest} page${widest === 1 ? '' : 's'}) — one fact, not ${n * widest} findings:`);
|
|
435
|
+
for (const g of outcome.groups) {
|
|
436
|
+
const verb = g.kind === 'lost' ? 'gone from' : 'new on';
|
|
437
|
+
console.log(` ${g.role} ${JSON.stringify(g.name)} · ${verb} ${g.pages.length}/${g.seenOn} pages`);
|
|
438
|
+
}
|
|
439
|
+
if (outcome.policy === 'held-out') {
|
|
440
|
+
console.log(`\n Held out of the verdict: the two captures ran under different browser state\n` +
|
|
441
|
+
` (baseline ${outcome.baselineState?.summary ?? 'unknown'} → current ${outcome.currentState?.summary ?? 'unknown'}).\n` +
|
|
442
|
+
` A banner dismissed, a flag stored, a tour completed — the app did not move,\n` +
|
|
443
|
+
` the state it was read under did. The elements stay in the contract.`);
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
// Still red, and the reader has to arbitrate. Both causes are named because the
|
|
447
|
+
// rows cannot tell them apart, and saying only one would be a guess wearing a
|
|
448
|
+
// verdict's clothes.
|
|
449
|
+
console.log(outcome.state === 'unknown'
|
|
450
|
+
? `\n Still in the verdict — the baseline predates capture-state fingerprints, so\n` +
|
|
451
|
+
` "your app removed these" and "these captures ran in different browser state"\n` +
|
|
452
|
+
` cannot be told apart here. Two ways out:\n` +
|
|
453
|
+
` · they really are gone → fix or delete the tests that name them\n` +
|
|
454
|
+
` · a banner/flag/tour differs → re-map and re-baseline; the next diff can tell`
|
|
455
|
+
: `\n Still in the verdict — both captures ran under the same browser state, so the\n` +
|
|
456
|
+
` app itself no longer renders these. Fix or delete the tests that name them.`);
|
|
457
|
+
}
|
|
411
458
|
function printNameMasks(outcome) {
|
|
412
459
|
if (!outcome)
|
|
413
460
|
return;
|
|
@@ -472,7 +519,7 @@ function driftSummaryLine(verb, verdict, c, nameDrift = [], bindingDrift = []) {
|
|
|
472
519
|
? `${bindingFixable} test selector${bindingFixable === 1 ? '' : 's'} to rewrite`
|
|
473
520
|
: c.healable === 0 && nameFixable > 0
|
|
474
521
|
? `${nameFixable} renamed label${nameFixable === 1 ? '' : 's'}`
|
|
475
|
-
: `${c.healable}
|
|
522
|
+
: `${c.healable} drifted`;
|
|
476
523
|
const headline = verdict === 'PASS'
|
|
477
524
|
? 'PASS · no drift, suite safe'
|
|
478
525
|
: verdict === 'FIX'
|
|
@@ -500,7 +547,7 @@ async function runDirDiff(baselineDir, currentDir, opts) {
|
|
|
500
547
|
console.error(`❌ "${currentDir}" is not a directory.`);
|
|
501
548
|
process.exit(2);
|
|
502
549
|
}
|
|
503
|
-
const { reports, stale } = collectDirDiff(baselineDir, currentDir);
|
|
550
|
+
const { reports, stale, systemic, depthMismatch } = collectDirDiff(baselineDir, currentDir);
|
|
504
551
|
if (reports.length === 0 && stale.length > 0) {
|
|
505
552
|
refuseToCompare('same-capture', `❌ Nothing to compare: ${stale.length === 1 ? 'the only pair is' : `all ${stale.length} pairs are`} ` +
|
|
506
553
|
`the same capture on both sides.\n\n` +
|
|
@@ -515,16 +562,37 @@ async function runDirDiff(baselineDir, currentDir, opts) {
|
|
|
515
562
|
refuseToCompare('no-pairs', `❌ No matching mapping pairs found between ${rel(baselineDir)} and ${rel(currentDir)}.`, 'Mapping files are paired by filename — run `ia-qa-heal map` so both directories hold the same pages.', opts.json);
|
|
516
563
|
return;
|
|
517
564
|
}
|
|
565
|
+
// Printed BEFORE the verdict, not after it. A depth mismatch does not colour a few rows —
|
|
566
|
+
// it can invent the entire BLOCK, because everything the deep side found behind a menu is
|
|
567
|
+
// simply absent from the shallow side and reads as `lost`. Someone who meets the verdict
|
|
568
|
+
// first will start hand-repairing drift that never happened.
|
|
569
|
+
if (depthMismatch) {
|
|
570
|
+
const deeper = depthMismatch.baseline > depthMismatch.current ? 'baseline' : 'current capture';
|
|
571
|
+
const shallower = deeper === 'baseline' ? 'current capture' : 'baseline';
|
|
572
|
+
console.error(`\n⚠️ Exploration depth mismatch — the ${deeper} was captured with \`map --deep\`, the ` +
|
|
573
|
+
`${shallower} was not\n (depth ${depthMismatch.baseline} vs ${depthMismatch.current}). ` +
|
|
574
|
+
`A deep contract holds the elements behind menus, tabs and dialogs;\n a shallow one ` +
|
|
575
|
+
`cannot, so those elements read as \`lost\` here. Most of what follows is probably that,\n` +
|
|
576
|
+
` not drift in your app.\n` +
|
|
577
|
+
` Capture both sides the same way: \`ia-qa-heal map --deep\` before diffing, or re-baseline` +
|
|
578
|
+
` without it.`);
|
|
579
|
+
}
|
|
518
580
|
// Aggregate verdict
|
|
519
581
|
const aggregate = aggregateReports(reports);
|
|
520
582
|
if (stale.length > 0)
|
|
521
583
|
aggregate.staleExcluded = stale;
|
|
584
|
+
if (systemic.groups.length > 0)
|
|
585
|
+
aggregate.systemic = systemic;
|
|
586
|
+
Object.assign(aggregate, (0, nextStep_1.nextStepFor)(aggregate.verdict, {
|
|
587
|
+
systemicUnresolved: systemic.groups.length > 0 && systemic.policy === 'in-verdict',
|
|
588
|
+
}));
|
|
522
589
|
const usage = (0, ingest_1.loadUsage)();
|
|
523
590
|
if (opts.json) {
|
|
524
|
-
console.log(JSON.stringify(aggregate, null, 2));
|
|
591
|
+
console.log(JSON.stringify({ schema: nextStep_1.SCHEMA_VERSION, ...aggregate }, null, 2));
|
|
525
592
|
}
|
|
526
593
|
else {
|
|
527
594
|
printAggregate(aggregate, reports, baselineDir, currentDir, usage);
|
|
595
|
+
printSystemic(systemic);
|
|
528
596
|
printNameMasks(aggregate.nameMask);
|
|
529
597
|
printNameDrift(aggregate.nameDrift);
|
|
530
598
|
}
|
|
@@ -605,7 +673,18 @@ function escalateBindingVerdict(verdict, drift) {
|
|
|
605
673
|
* diff each pair. Reporting and exit codes stay with the callers.
|
|
606
674
|
*/
|
|
607
675
|
function collectDirDiff(baselineDir, currentDir) {
|
|
608
|
-
|
|
676
|
+
let reports = [];
|
|
677
|
+
// The state each side was captured under. Stamped per page by the same `map` run, so
|
|
678
|
+
// the first pair read settles it for the walk; a baseline written before this field
|
|
679
|
+
// existed leaves it undefined, which `compareCaptureState` reports as *unknown* and
|
|
680
|
+
// never as a match.
|
|
681
|
+
let baselineState;
|
|
682
|
+
let currentState;
|
|
683
|
+
// Exploration depth, settled the same way and for the same reason. Absent means the loaded
|
|
684
|
+
// state only, which is depth 0 — a real value here, not unknown: every contract written
|
|
685
|
+
// before `--deep` existed describes exactly one state, and that is knowable.
|
|
686
|
+
let baselineDepth = 0;
|
|
687
|
+
let currentDepth = 0;
|
|
609
688
|
// Kept out of the verdict rather than counted as `ok` — N ok that were never
|
|
610
689
|
// compared is exactly the false green this guard exists to stop.
|
|
611
690
|
const stale = [];
|
|
@@ -614,6 +693,10 @@ function collectDirDiff(baselineDir, currentDir) {
|
|
|
614
693
|
const usage = (0, ingest_1.loadUsage)();
|
|
615
694
|
const masks = (0, nameMask_1.loadNameMasks)();
|
|
616
695
|
const assess = (baseline, current, baselineFile, currentFile, page) => {
|
|
696
|
+
baselineState ??= baseline.captureState;
|
|
697
|
+
currentState ??= current.captureState;
|
|
698
|
+
baselineDepth = Math.max(baselineDepth, baseline.exploredDepth ?? 0);
|
|
699
|
+
currentDepth = Math.max(currentDepth, current.exploredDepth ?? 0);
|
|
617
700
|
const report = attachBaselineHrefs((0, nameMask_1.diffWithMasks)(baseline, current, masks, usage), baseline);
|
|
618
701
|
report.nameDrift = (0, nameDrift_1.assessNameDrift)(report.rows, current.elements, usage);
|
|
619
702
|
report.verdict = (0, nameDrift_1.escalateVerdict)(report.verdict, report.nameDrift);
|
|
@@ -667,7 +750,18 @@ function collectDirDiff(baselineDir, currentDir) {
|
|
|
667
750
|
console.error(`⚠ Skipping ${name}: ${e instanceof Error ? e.message : String(e)}`);
|
|
668
751
|
}
|
|
669
752
|
}
|
|
670
|
-
|
|
753
|
+
// Last, over the finished walk: a cross-page fact cannot be known page by page.
|
|
754
|
+
// `applySystemic` is a no-op unless the state comparison licenses holding a group
|
|
755
|
+
// out, so a project with no systemic delta — or with one the state cannot explain —
|
|
756
|
+
// gets exactly the reports it got before.
|
|
757
|
+
const systemic = (0, systemicDelta_1.assessSystemic)(reports, baselineState, currentState);
|
|
758
|
+
reports = (0, systemicDelta_1.applySystemic)(reports, systemic);
|
|
759
|
+
return {
|
|
760
|
+
reports,
|
|
761
|
+
stale,
|
|
762
|
+
systemic,
|
|
763
|
+
depthMismatch: baselineDepth === currentDepth ? null : { baseline: baselineDepth, current: currentDepth },
|
|
764
|
+
};
|
|
671
765
|
}
|
|
672
766
|
function aggregateReports(reports) {
|
|
673
767
|
const layoutReports = reports.filter((r) => r.isLayout);
|
|
@@ -716,7 +810,16 @@ function aggregateReports(reports) {
|
|
|
716
810
|
verdict: r.report.verdict,
|
|
717
811
|
counts: r.report.counts,
|
|
718
812
|
healableMappings: r.report.counts.healable > 0
|
|
719
|
-
? r.report.rows
|
|
813
|
+
? r.report.rows
|
|
814
|
+
.filter((row) => row.status === 'healable')
|
|
815
|
+
.map((row) => ({
|
|
816
|
+
from: row.selector,
|
|
817
|
+
to: row.healedSelector,
|
|
818
|
+
// Carried per rewrite rather than per report: an agent applies one edit at a
|
|
819
|
+
// time, and "3 of these are exact and 1 is a fuzzy guess" is unrecoverable
|
|
820
|
+
// from a single verdict word.
|
|
821
|
+
confidence: (0, nextStep_1.confidenceFor)(row),
|
|
822
|
+
}))
|
|
720
823
|
: undefined,
|
|
721
824
|
})),
|
|
722
825
|
totals,
|
|
@@ -732,14 +835,14 @@ function printAggregate(agg, reports, baselineDir, currentDir, usage) {
|
|
|
732
835
|
console.log(against);
|
|
733
836
|
console.log(` ${path.basename(baselineDir)} → ${path.basename(currentDir)}`);
|
|
734
837
|
console.log(` ${reports.length} ${reports.length === 1 ? 'file' : 'files'} diffed`);
|
|
735
|
-
console.log(` ${agg.totals.ok} ok · ${agg.totals.renamed} renamed · ${agg.totals.healable}
|
|
838
|
+
console.log(` ${agg.totals.ok} ok · ${agg.totals.renamed} renamed · ${agg.totals.healable} drifted · ` +
|
|
736
839
|
`${agg.totals.ambiguous} ambiguous · ${agg.totals.lost} lost · ${agg.totals.rebound} rebound · ${agg.totals.added} new`);
|
|
737
840
|
// Layout section
|
|
738
841
|
if (agg.layoutVerdict) {
|
|
739
842
|
const li = agg.layoutVerdict === 'PASS' ? '✅' : agg.layoutVerdict === 'FIX' ? '🔧' : '⛔';
|
|
740
843
|
console.log(`\n 📐 Layouts ${li} ${agg.layoutVerdict}`);
|
|
741
844
|
if (agg.layoutCounts) {
|
|
742
|
-
console.log(` ${agg.layoutCounts.ok} ok · ${agg.layoutCounts.healable}
|
|
845
|
+
console.log(` ${agg.layoutCounts.ok} ok · ${agg.layoutCounts.healable} drifted · ` +
|
|
743
846
|
`${agg.layoutCounts.lost} lost · ${agg.layoutCounts.added} new`);
|
|
744
847
|
}
|
|
745
848
|
}
|
|
@@ -759,7 +862,7 @@ function printAggregate(agg, reports, baselineDir, currentDir, usage) {
|
|
|
759
862
|
const counts = [
|
|
760
863
|
`${pr.counts.ok} ok`,
|
|
761
864
|
pr.counts.renamed > 0 ? `${pr.counts.renamed} renamed` : null,
|
|
762
|
-
`${pr.counts.healable}
|
|
865
|
+
`${pr.counts.healable} drifted`,
|
|
763
866
|
pr.counts.ambiguous > 0 ? `${pr.counts.ambiguous} ambiguous` : null,
|
|
764
867
|
`${pr.counts.lost} lost`,
|
|
765
868
|
`${pr.counts.added} new`,
|
|
@@ -986,7 +1089,7 @@ function printHuman(report, files, usage) {
|
|
|
986
1089
|
const icon = verdict === 'PASS' ? '✅' : verdict === 'FIX' ? '🔧' : '⛔';
|
|
987
1090
|
console.log(`\n${icon} ${verdict}`);
|
|
988
1091
|
console.log(` ${path.basename(files[0])} → ${path.basename(files[1])} · ` +
|
|
989
|
-
`${counts.ok} ok · ${counts.renamed} renamed · ${counts.healable}
|
|
1092
|
+
`${counts.ok} ok · ${counts.renamed} renamed · ${counts.healable} drifted · ` +
|
|
990
1093
|
`${counts.ambiguous} ambiguous · ${counts.lost} lost · ${counts.rebound} rebound · ${counts.added} new\n`);
|
|
991
1094
|
const drift = rows.filter((r) => r.status !== 'ok');
|
|
992
1095
|
for (const r of drift) {
|
|
@@ -994,7 +1097,7 @@ function printHuman(report, files, usage) {
|
|
|
994
1097
|
if (r.status === 'healable') {
|
|
995
1098
|
const how = r.matchType === 'exact' ? 'exact name' : `fuzzy ${Math.round((r.score ?? 0) * 100)}%`;
|
|
996
1099
|
const by = r.disambiguatedBy === 'context' ? `, by context "${r.context}"` : '';
|
|
997
|
-
console.log(` 🔧
|
|
1100
|
+
console.log(` 🔧 drifted ${label(r)}\n ${r.selector} → ${r.healedSelector} (${how}${by})${note}`);
|
|
998
1101
|
}
|
|
999
1102
|
else if (r.status === 'renamed') {
|
|
1000
1103
|
console.log(` ✏️ renamed ${label(r)}\n name "${r.name}" → "${r.newName}" · selector still valid: ${r.selector}${note}`);
|