@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.
- package/README.md +118 -2
- package/TUTORIAL.md +138 -3
- package/assets/logoKawaiiGreenTiny.png +0 -0
- package/dist/cli/args.js +4 -2
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/diff.d.ts +6 -1
- package/dist/cli/diff.js +114 -4
- package/dist/cli/diff.js.map +1 -1
- package/dist/cli/emit.d.ts +71 -0
- package/dist/cli/emit.js +150 -0
- package/dist/cli/emit.js.map +1 -0
- package/dist/cli/fix.js +36 -0
- package/dist/cli/fix.js.map +1 -1
- package/dist/cli/history.d.ts +12 -0
- package/dist/cli/history.js +95 -0
- package/dist/cli/history.js.map +1 -0
- package/dist/cli/index.js +53 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.js +32 -0
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/map.js +60 -0
- package/dist/cli/map.js.map +1 -1
- package/dist/cli/run.js +77 -7
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/ui.d.ts +16 -0
- package/dist/cli/ui.js +275 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/evidence.d.ts +130 -0
- package/dist/evidence.js +232 -0
- package/dist/evidence.js.map +1 -0
- package/dist/history.d.ts +145 -0
- package/dist/history.js +230 -0
- package/dist/history.js.map +1 -0
- package/dist/htmlReport.d.ts +47 -0
- package/dist/htmlReport.js +76 -1
- package/dist/htmlReport.js.map +1 -1
- package/dist/junit.d.ts +53 -0
- package/dist/junit.js +150 -0
- package/dist/junit.js.map +1 -0
- package/dist/mcp/server.js +41 -0
- package/dist/mcp/server.js.map +1 -1
- package/dist/ui/icon.d.ts +41 -0
- package/dist/ui/icon.js +115 -0
- package/dist/ui/icon.js.map +1 -0
- package/dist/ui/lock.d.ts +40 -0
- package/dist/ui/lock.js +138 -0
- package/dist/ui/lock.js.map +1 -0
- package/dist/ui/page.d.ts +1 -0
- package/dist/ui/page.js +584 -0
- package/dist/ui/page.js.map +1 -0
- package/dist/ui/registry.d.ts +62 -0
- package/dist/ui/registry.js +139 -0
- package/dist/ui/registry.js.map +1 -0
- package/dist/ui/server.d.ts +67 -0
- package/dist/ui/server.js +604 -0
- package/dist/ui/server.js.map +1 -0
- package/dist/ui/shortcut.d.ts +90 -0
- package/dist/ui/shortcut.js +176 -0
- package/dist/ui/shortcut.js.map +1 -0
- package/package.json +2 -1
- package/skills/ia-qa-heal/SKILL.md +27 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A desktop shortcut that opens this project's console — `ia-qa-heal ui --shortcut`.
|
|
3
|
+
*
|
|
4
|
+
* The console already opens a browser by itself, so the only thing standing
|
|
5
|
+
* between a manual QA and their drift report is a terminal they have no reason
|
|
6
|
+
* to own. This removes it: double-click an icon, the console starts in the
|
|
7
|
+
* right project directory and the browser opens on it. No URL is ever typed,
|
|
8
|
+
* copied, or seen — which is also what keeps the session token out of the way
|
|
9
|
+
* instead of making it something a person has to handle.
|
|
10
|
+
*
|
|
11
|
+
* Nothing about the icon is cosmetic-only: an unlabelled `.cmd` on a desktop is
|
|
12
|
+
* a file people are right to be wary of double-clicking.
|
|
13
|
+
*
|
|
14
|
+
* **Per platform, and the honest limits.**
|
|
15
|
+
*
|
|
16
|
+
* - **Windows** — a real `.lnk`, which is the only shape that can carry a custom
|
|
17
|
+
* icon. It is created by shelling out to PowerShell's `WScript.Shell` (present
|
|
18
|
+
* on every supported Windows, and not a dependency), and it targets a `.cmd`
|
|
19
|
+
* kept *inside* the project rather than a second file on the desktop. If
|
|
20
|
+
* PowerShell refuses, the `.cmd` is placed on the desktop instead and the
|
|
21
|
+
* caller says so — a working shortcut with the default icon beats none.
|
|
22
|
+
* - **Linux** — a `.desktop` entry, whose `Icon=` takes the PNG directly.
|
|
23
|
+
* - **macOS** — a `.command`, with the **system's default icon**. Setting a
|
|
24
|
+
* custom one needs a resource fork via `Rez`/`fileicon` or Finder scripting;
|
|
25
|
+
* both are fragile and neither is worth a silent failure. Stated, not hidden.
|
|
26
|
+
*/
|
|
27
|
+
export type Platform = 'win32' | 'darwin' | 'linux';
|
|
28
|
+
export interface ShortcutFile {
|
|
29
|
+
file: string;
|
|
30
|
+
contents: string;
|
|
31
|
+
mode?: number;
|
|
32
|
+
}
|
|
33
|
+
/** A Windows `.lnk`, which cannot be written as text — see `installShortcut`. */
|
|
34
|
+
export interface LnkPlan {
|
|
35
|
+
file: string;
|
|
36
|
+
target: string;
|
|
37
|
+
workingDir: string;
|
|
38
|
+
iconPath: string;
|
|
39
|
+
description: string;
|
|
40
|
+
}
|
|
41
|
+
export interface ShortcutPlan {
|
|
42
|
+
/** Plain-text files to write, in order. */
|
|
43
|
+
files: ShortcutFile[];
|
|
44
|
+
/** Present on Windows only. */
|
|
45
|
+
lnk?: LnkPlan;
|
|
46
|
+
/** Where the .ico has to be written for the lnk to find it. */
|
|
47
|
+
icoPath?: string;
|
|
48
|
+
/** True when this platform gets the system's default icon. */
|
|
49
|
+
defaultIconOnly?: boolean;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The desktop name. It carries the site because this icon sits on the desktop
|
|
53
|
+
* of every QA who uses the tool, which makes it the cheapest honest place to
|
|
54
|
+
* say where it comes from — and because "console" alone, months later, names
|
|
55
|
+
* nothing.
|
|
56
|
+
*/
|
|
57
|
+
export declare const SHORTCUT_NAME = "ia-qa-heal console (ia-qa.com)";
|
|
58
|
+
/**
|
|
59
|
+
* What would be written, without writing it. Split out so the shape of every
|
|
60
|
+
* platform's launcher is testable on any platform — the one thing that would
|
|
61
|
+
* otherwise only ever be exercised on the maintainer's own OS.
|
|
62
|
+
*/
|
|
63
|
+
export declare function planShortcut(opts: {
|
|
64
|
+
platform: Platform;
|
|
65
|
+
desktop: string;
|
|
66
|
+
projectDir: string;
|
|
67
|
+
configDir: string;
|
|
68
|
+
nodePath: string;
|
|
69
|
+
cliEntry: string;
|
|
70
|
+
logoPath: string;
|
|
71
|
+
name?: string;
|
|
72
|
+
}): ShortcutPlan;
|
|
73
|
+
/**
|
|
74
|
+
* Create the Windows `.lnk`. PowerShell's COM shell object is the only way to
|
|
75
|
+
* write one without either a native module or hand-assembling the binary Shell
|
|
76
|
+
* Link format. Returns false rather than throwing: the caller has a working
|
|
77
|
+
* fallback and a shortcut is not worth failing a command over.
|
|
78
|
+
*
|
|
79
|
+
* Every value is passed as an environment variable, never interpolated into the
|
|
80
|
+
* script text — a project path containing a quote would otherwise end up being
|
|
81
|
+
* executed as PowerShell.
|
|
82
|
+
*/
|
|
83
|
+
export declare function createWindowsLnk(lnk: LnkPlan): boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Where the desktop is. `XDG_DESKTOP_DIR` and localized folder names exist and
|
|
86
|
+
* are not resolvable without reading user config, so a missing `Desktop` falls
|
|
87
|
+
* back to the home directory rather than guessing wrong and writing an icon
|
|
88
|
+
* nobody finds.
|
|
89
|
+
*/
|
|
90
|
+
export declare function desktopDir(home?: string): string;
|
|
@@ -0,0 +1,176 @@
|
|
|
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.SHORTCUT_NAME = void 0;
|
|
37
|
+
exports.planShortcut = planShortcut;
|
|
38
|
+
exports.createWindowsLnk = createWindowsLnk;
|
|
39
|
+
exports.desktopDir = desktopDir;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const os = __importStar(require("os"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
/**
|
|
44
|
+
* The desktop name. It carries the site because this icon sits on the desktop
|
|
45
|
+
* of every QA who uses the tool, which makes it the cheapest honest place to
|
|
46
|
+
* say where it comes from — and because "console" alone, months later, names
|
|
47
|
+
* nothing.
|
|
48
|
+
*/
|
|
49
|
+
exports.SHORTCUT_NAME = 'ia-qa-heal console (ia-qa.com)';
|
|
50
|
+
function quoteWin(s) {
|
|
51
|
+
return `"${s.replace(/"/g, '""')}"`;
|
|
52
|
+
}
|
|
53
|
+
function quotePosix(s) {
|
|
54
|
+
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* What would be written, without writing it. Split out so the shape of every
|
|
58
|
+
* platform's launcher is testable on any platform — the one thing that would
|
|
59
|
+
* otherwise only ever be exercised on the maintainer's own OS.
|
|
60
|
+
*/
|
|
61
|
+
function planShortcut(opts) {
|
|
62
|
+
const name = opts.name ?? exports.SHORTCUT_NAME;
|
|
63
|
+
const description = 'Open the local selector-drift console for this project — ia-qa.com';
|
|
64
|
+
if (opts.platform === 'win32') {
|
|
65
|
+
// The .cmd lives in the project, not on the desktop: the desktop gets one
|
|
66
|
+
// icon, not a launcher plus its helper.
|
|
67
|
+
const cmd = path.join(opts.configDir, 'console.cmd');
|
|
68
|
+
return {
|
|
69
|
+
files: [
|
|
70
|
+
{
|
|
71
|
+
file: cmd,
|
|
72
|
+
contents: `@echo off\r\n` +
|
|
73
|
+
`title ${name}\r\n` +
|
|
74
|
+
`cd /d ${quoteWin(opts.projectDir)}\r\n` +
|
|
75
|
+
`echo ia-qa-heal console - ia-qa.com\r\n` +
|
|
76
|
+
`echo Starting... your browser will open on its own.\r\n` +
|
|
77
|
+
`echo Close this window to stop it.\r\n` +
|
|
78
|
+
`echo.\r\n` +
|
|
79
|
+
`${quoteWin(opts.nodePath)} ${quoteWin(opts.cliEntry)} ui\r\n` +
|
|
80
|
+
`if errorlevel 1 pause\r\n`,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
lnk: {
|
|
84
|
+
file: path.join(opts.desktop, `${name}.lnk`),
|
|
85
|
+
target: cmd,
|
|
86
|
+
workingDir: opts.projectDir,
|
|
87
|
+
iconPath: path.join(opts.configDir, 'console.ico'),
|
|
88
|
+
description,
|
|
89
|
+
},
|
|
90
|
+
icoPath: path.join(opts.configDir, 'console.ico'),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
if (opts.platform === 'darwin') {
|
|
94
|
+
return {
|
|
95
|
+
files: [
|
|
96
|
+
{
|
|
97
|
+
file: path.join(opts.desktop, `${name}.command`),
|
|
98
|
+
contents: `#!/bin/sh\n` +
|
|
99
|
+
`# ${description}\n` +
|
|
100
|
+
`cd ${quotePosix(opts.projectDir)} || exit 1\n` +
|
|
101
|
+
`echo " ia-qa-heal console - ia-qa.com"\n` +
|
|
102
|
+
`echo " Starting... your browser will open on its own."\n` +
|
|
103
|
+
`echo " Close this window to stop it."\n` +
|
|
104
|
+
`exec ${quotePosix(opts.nodePath)} ${quotePosix(opts.cliEntry)} ui\n`,
|
|
105
|
+
mode: 0o755,
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
defaultIconOnly: true,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
files: [
|
|
113
|
+
{
|
|
114
|
+
file: path.join(opts.desktop, `${name}.desktop`),
|
|
115
|
+
contents: `[Desktop Entry]\n` +
|
|
116
|
+
`Type=Application\n` +
|
|
117
|
+
`Name=${name}\n` +
|
|
118
|
+
`Comment=${description}\n` +
|
|
119
|
+
`Icon=${opts.logoPath}\n` +
|
|
120
|
+
`Exec=sh -c "cd ${quotePosix(opts.projectDir)} && ${quotePosix(opts.nodePath)} ${quotePosix(opts.cliEntry)} ui"\n` +
|
|
121
|
+
`Terminal=true\n` +
|
|
122
|
+
`Categories=Development;\n`,
|
|
123
|
+
mode: 0o755,
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Create the Windows `.lnk`. PowerShell's COM shell object is the only way to
|
|
130
|
+
* write one without either a native module or hand-assembling the binary Shell
|
|
131
|
+
* Link format. Returns false rather than throwing: the caller has a working
|
|
132
|
+
* fallback and a shortcut is not worth failing a command over.
|
|
133
|
+
*
|
|
134
|
+
* Every value is passed as an environment variable, never interpolated into the
|
|
135
|
+
* script text — a project path containing a quote would otherwise end up being
|
|
136
|
+
* executed as PowerShell.
|
|
137
|
+
*/
|
|
138
|
+
function createWindowsLnk(lnk) {
|
|
139
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
140
|
+
const { execFileSync } = require('child_process');
|
|
141
|
+
const script = '$s = (New-Object -ComObject WScript.Shell).CreateShortcut($env:IAQA_LNK); ' +
|
|
142
|
+
'$s.TargetPath = $env:IAQA_TARGET; ' +
|
|
143
|
+
'$s.WorkingDirectory = $env:IAQA_CWD; ' +
|
|
144
|
+
'$s.Description = $env:IAQA_DESC; ' +
|
|
145
|
+
'if (Test-Path $env:IAQA_ICON) { $s.IconLocation = $env:IAQA_ICON }; ' +
|
|
146
|
+
'$s.Save()';
|
|
147
|
+
try {
|
|
148
|
+
execFileSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', script], {
|
|
149
|
+
stdio: 'ignore',
|
|
150
|
+
timeout: 15000,
|
|
151
|
+
env: {
|
|
152
|
+
...process.env,
|
|
153
|
+
IAQA_LNK: lnk.file,
|
|
154
|
+
IAQA_TARGET: lnk.target,
|
|
155
|
+
IAQA_CWD: lnk.workingDir,
|
|
156
|
+
IAQA_DESC: lnk.description,
|
|
157
|
+
IAQA_ICON: lnk.iconPath,
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
return fs.existsSync(lnk.file);
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Where the desktop is. `XDG_DESKTOP_DIR` and localized folder names exist and
|
|
168
|
+
* are not resolvable without reading user config, so a missing `Desktop` falls
|
|
169
|
+
* back to the home directory rather than guessing wrong and writing an icon
|
|
170
|
+
* nobody finds.
|
|
171
|
+
*/
|
|
172
|
+
function desktopDir(home = os.homedir()) {
|
|
173
|
+
const candidate = path.join(home, 'Desktop');
|
|
174
|
+
return fs.existsSync(candidate) ? candidate : home;
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=shortcut.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shortcut.js","sourceRoot":"","sources":["../../src/ui/shortcut.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ia-qa/self-healing",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.8",
|
|
4
4
|
"description": "Local-first self-healing for UI tests: a local MCP server + CLI that map your app's pages to a role/name/selector contract, diff selector drift (PASS/FIX/BLOCK), and apply deterministic fixes to Cypress/Playwright/Selenium tests. Runs on your machine — nothing leaves it.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"self-healing",
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"files": [
|
|
54
54
|
"dist",
|
|
55
55
|
"skills",
|
|
56
|
+
"assets",
|
|
56
57
|
"README.md",
|
|
57
58
|
"TUTORIAL.md",
|
|
58
59
|
"ROADMAP.md"
|
|
@@ -38,6 +38,8 @@ Where things live:
|
|
|
38
38
|
| `.ia-qa/mapping/_layouts/` | elements shared by every page (navbar, footer) — one fix, not N |
|
|
39
39
|
| `.ia-qa/usage.json` | the locators the suite actually uses (produced by `ingest`) |
|
|
40
40
|
| `.ia-qa/mapping/_overview.md`, `_navigation.md`/`.svg` | app index + navigation graph |
|
|
41
|
+
| `.ia-qa/history.jsonl` | one line per `diff`/`run`: verdict, counts, capture source, page coverage |
|
|
42
|
+
| `.ia-qa/mapping/_shots/` | page screenshots + element positions from `map --screenshots` — artefacts, never diffed |
|
|
41
43
|
|
|
42
44
|
Open a browser only when the contract *cannot* answer: the page is not mapped, or the element
|
|
43
45
|
exists only in a state nothing captured (behind a tab, a modal, a mode toggle).
|
|
@@ -54,10 +56,19 @@ exists only in a state nothing captured (behind a tab, a modal, a mode toggle).
|
|
|
54
56
|
| "give me the app's structure" | `graph --format mermaid\|svg\|json\|markdown` |
|
|
55
57
|
| "apply the rewrites" | `fix` — `--dry-run` first, always |
|
|
56
58
|
| "what do my tests actually use?" | `ingest` |
|
|
59
|
+
| "is this getting better or worse?" | `history` (`--json`) — the trend no single run can reconstruct |
|
|
60
|
+
| "gate my pipeline on it" | `diff --junit <file>` — every major CI charts the trend natively |
|
|
61
|
+
| "let me look at it / decide myself" | tell the human to run `ia-qa-heal ui` — **do not run it yourself** (§4.8) |
|
|
57
62
|
| first-time setup | write `.ia-qa/config.json` directly — `init` is a TTY wizard and **will refuse in your shell** |
|
|
58
63
|
|
|
59
64
|
Exact flags: `ia-qa-heal <verb> --help`. Do not guess them from this file.
|
|
60
65
|
|
|
66
|
+
### Machine-readable outputs
|
|
67
|
+
|
|
68
|
+
`diff`, `run`, `audit`, `check` and `history` all take `--json`. Prefer it over parsing prose.
|
|
69
|
+
Under `run --json`, every human line moves to stderr so stdout stays one parseable document.
|
|
70
|
+
`diff --junit` / `run --junit` additionally write JUnit XML — the format a CI already knows.
|
|
71
|
+
|
|
61
72
|
## 3. Reading a verdict
|
|
62
73
|
|
|
63
74
|
- **PASS** — nothing moved.
|
|
@@ -93,9 +104,25 @@ clicking the wrong thing), or a name-drift finding that is not attributable.
|
|
|
93
104
|
7. **Renames need an inventory.** `diff` can only gate on a changed *label* if `ingest` has
|
|
94
105
|
inventoried what the suite uses. No `usage.json` ⇒ no name escalation — so if their tests
|
|
95
106
|
locate by `getByRole(..., { name })`, run `ingest` before trusting a PASS.
|
|
107
|
+
8. **Never run `ia-qa-heal ui`.** It starts a local web console and **blocks until a human closes
|
|
108
|
+
the browser tab** — in your shell that is a hung command, not a result. It also refuses to
|
|
109
|
+
start under CI for the same reason. When the verdict is BLOCK and the human has to choose
|
|
110
|
+
between candidates, *tell them the command*; they run it. Everything the console shows is
|
|
111
|
+
available to you as JSON (`diff --json`) — the console adds pictures for a person, not data
|
|
112
|
+
for you.
|
|
113
|
+
9. **Never read an empty history as "nothing drifted".** `.ia-qa/history.jsonl` is written by
|
|
114
|
+
`diff` and `run`; absent means *not recorded*, exactly like a missing `usage.json` means *not
|
|
115
|
+
measured*. And when you report a trend, repeat what it excluded: runs captured a different way
|
|
116
|
+
(a `map` baseline vs a live suite capture) are deliberately held out, because charting both
|
|
117
|
+
invents a trend out of a capture artifact. `history --json` and the `heal_history` MCP tool
|
|
118
|
+
both hand you that exclusion — pass it on rather than presenting the series as complete.
|
|
96
119
|
|
|
97
120
|
## 5. What leaves the machine
|
|
98
121
|
|
|
99
122
|
Nothing, except: requests to the user's **own** `baseUrl` (`check`'s link half, `discover --crawl`
|
|
100
123
|
— GET only, same origin, never a `/logout` or `/delete` URL), and `ia-qa-heal-ai` calling the
|
|
101
124
|
user's own LLM if they opted in. Say that plainly rather than implying more or less.
|
|
125
|
+
|
|
126
|
+
`ui` does not change that: it binds `127.0.0.1` only, is gated by a per-process token, and serves a
|
|
127
|
+
page with no external asset. If asked whether it "hosts" anything, the answer is no — it is a local
|
|
128
|
+
process that dies with the browser tab.
|