@ia-qa/self-healing 1.6.1 → 1.6.3

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,101 @@
1
+ ---
2
+ name: ia-qa-heal
3
+ description: >-
4
+ Repair, audit and reason about E2E test locators (Playwright, Cypress, Selenium) by
5
+ reading a local page contract instead of opening a browser. Use when a UI test fails on
6
+ a locator (selector not found, strict-mode violation, a renamed button or label), when
7
+ tests must be updated after a UI change, when asked which pages a suite covers, or
8
+ whether a suite still names elements that exist. Requires @ia-qa/self-healing
9
+ (`ia-qa-heal`). Runs entirely on the user's machine.
10
+ ---
11
+
12
+ # ia-qa-heal — heal locators from the contract, not from a browser
13
+
14
+ ## 1. Read the contract before you open anything
15
+
16
+ `.ia-qa/mapping/<page>.json` is a captured snapshot of every interactive element on a page:
17
+
18
+ ```json
19
+ {
20
+ "page": "checkout", "url": "/checkout", "capturedAt": "2026-08-02T17:40:20.067Z",
21
+ "elements": [
22
+ { "role": "button", "name": "Pay now", "selector": "#root form > button", "context": "main" },
23
+ { "role": "link", "name": "Back to cart", "selector": "a[href=\"/cart\"]", "context": "nav", "href": "/cart" }
24
+ ]
25
+ }
26
+ ```
27
+
28
+ A failing locator is usually answered by **reading that file**: the element's current role, its
29
+ accessible name, a selector that works. A test asserting a `button` where the page now renders a
30
+ `link` is one `grep` away — no browser, no screenshot, no round-trip. Do this first.
31
+
32
+ Where things live:
33
+
34
+ | Path | What it is |
35
+ |---|---|
36
+ | `.ia-qa/mapping/` | the current capture |
37
+ | `.ia-qa/baseline/` | the committed reference `diff` compares against |
38
+ | `.ia-qa/mapping/_layouts/` | elements shared by every page (navbar, footer) — one fix, not N |
39
+ | `.ia-qa/usage.json` | the locators the suite actually uses (produced by `ingest`) |
40
+ | `.ia-qa/mapping/_overview.md`, `_navigation.md`/`.svg` | app index + navigation graph |
41
+
42
+ Open a browser only when the contract *cannot* answer: the page is not mapped, or the element
43
+ exists only in a state nothing captured (behind a tab, a modal, a mode toggle).
44
+
45
+ ## 2. Which verb answers which question
46
+
47
+ | The user's question | Verb |
48
+ |---|---|
49
+ | "my tests broke — fix them" | `run` (capture during their suite → diff → confirm → fix → re-run to verify) |
50
+ | "did anything drift?" | `diff` → PASS/FIX/BLOCK (the CI gate) |
51
+ | "day one, no baseline — is my suite still valid?" | `audit` |
52
+ | "is the app I mapped even sound?" | `check` (dead links · unnamed elements · name collisions · orphan pages) |
53
+ | "which pages am I *not* covering?" | `discover` (`--sitemap` / `--crawl`; suggests only) |
54
+ | "give me the app's structure" | `graph --format mermaid\|svg\|json\|markdown` |
55
+ | "apply the rewrites" | `fix` — `--dry-run` first, always |
56
+ | "what do my tests actually use?" | `ingest` |
57
+ | first-time setup | write `.ia-qa/config.json` directly — `init` is a TTY wizard and **will refuse in your shell** |
58
+
59
+ Exact flags: `ia-qa-heal <verb> --help`. Do not guess them from this file.
60
+
61
+ ## 3. Reading a verdict
62
+
63
+ - **PASS** — nothing moved.
64
+ - **FIX** — every drifted element was re-identified; `fix` rewrites them deterministically.
65
+ - **BLOCK** — a human decides.
66
+
67
+ Row statuses: `ok`, `renamed`, `healable`, `ambiguous`, `lost`, `added`. Any row may also carry
68
+ `rebound: true` + `reboundTo`.
69
+
70
+ BLOCK is raised by `lost` (element gone), `ambiguous` (several candidates — a coin flip), `rebound`
71
+ (the old selector still resolves, but onto a **different** element: the test stays green while
72
+ clicking the wrong thing), or a name-drift finding that is not attributable.
73
+
74
+ ## 4. Never do this
75
+
76
+ 1. **Never hand-rewrite a locator reported `unattributable`.** `getByText`, `cy.contains`,
77
+ `getByTitle`, `getByAltText` name a **string, not an element** — nothing proves the test meant
78
+ the renamed button rather than a heading that never moved. They are reported with `file:line`
79
+ and hold the verdict at BLOCK *on purpose*. Editing one breaks a test that was **passing** —
80
+ the only outcome worse than the drift. Surface it to the human instead.
81
+ 2. **Never treat `audit` as a gate.** It is advisory (exit 0) unless `--strict`: a "missing"
82
+ locator may live on an unmapped page, or an unmapped *state*.
83
+ 3. **Never call a green `run` a green app.** `run` only re-captures the pages the suite actually
84
+ exercised; the rest keep the baseline's `capturedAt`, are `stale`, and sit outside the verdict.
85
+ Read the coverage line it prints before concluding anything.
86
+ 4. **Never diff a `map` baseline against a `run` capture.** The two read a page differently and
87
+ produce phantom drift. The tool warns — take the warning, don't explain it away.
88
+ 5. **Never run `fix` blind.** `--dry-run` first, show the plan, then apply. It never commits;
89
+ neither should you without the human seeing the diff.
90
+ 6. **There is no `--ai` flag on `ia-qa-heal`,** and adding one is not the fix — the gate stays
91
+ deterministic by design. The BYOK layer is the separate `ia-qa-heal-ai` binary: it *suggests*
92
+ matches for `lost`/`ambiguous` rows for a human to confirm, and never gates CI.
93
+ 7. **Renames need an inventory.** `diff` can only gate on a changed *label* if `ingest` has
94
+ inventoried what the suite uses. No `usage.json` ⇒ no name escalation — so if their tests
95
+ locate by `getByRole(..., { name })`, run `ingest` before trusting a PASS.
96
+
97
+ ## 5. What leaves the machine
98
+
99
+ Nothing, except: requests to the user's **own** `baseUrl` (`check`'s link half, `discover --crawl`
100
+ — GET only, same origin, never a `/logout` or `/delete` URL), and `ia-qa-heal-ai` calling the
101
+ user's own LLM if they opted in. Say that plainly rather than implying more or less.