@jevitate/cli 0.9.0 → 0.11.0

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 (190) hide show
  1. package/dist/anchor-baselines.d.ts +26 -0
  2. package/dist/anchor-baselines.js +25 -0
  3. package/dist/anchor-baselines.js.map +1 -0
  4. package/dist/approval-provenance.d.ts +32 -2
  5. package/dist/approval-provenance.js +92 -5
  6. package/dist/approval-provenance.js.map +1 -1
  7. package/dist/approved-demo.d.ts +86 -0
  8. package/dist/approved-demo.js +184 -0
  9. package/dist/approved-demo.js.map +1 -0
  10. package/dist/bin.js +13352 -7050
  11. package/dist/build-info.generated.js +2 -2
  12. package/dist/campaign-api.d.ts +15 -0
  13. package/dist/campaign-api.js +67 -6
  14. package/dist/campaign-api.js.map +1 -1
  15. package/dist/campaign-cli.js +7 -2
  16. package/dist/campaign-cli.js.map +1 -1
  17. package/dist/campaign-preflight.d.ts +76 -0
  18. package/dist/campaign-preflight.js +117 -0
  19. package/dist/campaign-preflight.js.map +1 -0
  20. package/dist/catalog-analysis.js +14 -0
  21. package/dist/catalog-analysis.js.map +1 -1
  22. package/dist/catalog-api.js +12 -4
  23. package/dist/catalog-api.js.map +1 -1
  24. package/dist/catalog-bundle-api.d.ts +83 -0
  25. package/dist/catalog-bundle-api.js +309 -0
  26. package/dist/catalog-bundle-api.js.map +1 -0
  27. package/dist/catalog-bundle-findings.d.ts +64 -0
  28. package/dist/catalog-bundle-findings.js +326 -0
  29. package/dist/catalog-bundle-findings.js.map +1 -0
  30. package/dist/catalog-bundle.d.ts +296 -0
  31. package/dist/catalog-bundle.js +777 -0
  32. package/dist/catalog-bundle.js.map +1 -0
  33. package/dist/catalog-cli.d.ts +6 -0
  34. package/dist/catalog-cli.js +181 -5
  35. package/dist/catalog-cli.js.map +1 -1
  36. package/dist/catalog-refs.d.ts +42 -0
  37. package/dist/catalog-refs.js +188 -0
  38. package/dist/catalog-refs.js.map +1 -0
  39. package/dist/catalog-review.d.ts +3 -1
  40. package/dist/catalog-review.js +61 -0
  41. package/dist/catalog-review.js.map +1 -1
  42. package/dist/catalog.d.ts +27 -3
  43. package/dist/catalog.js +46 -2
  44. package/dist/catalog.js.map +1 -1
  45. package/dist/check-api.js +180 -9
  46. package/dist/check-api.js.map +1 -1
  47. package/dist/check-cli.d.ts +2 -0
  48. package/dist/check-cli.js +96 -2
  49. package/dist/check-cli.js.map +1 -1
  50. package/dist/check-execute.d.ts +18 -0
  51. package/dist/check-execute.js +56 -3
  52. package/dist/check-execute.js.map +1 -1
  53. package/dist/check-types.d.ts +105 -0
  54. package/dist/check-types.js.map +1 -1
  55. package/dist/cli-output.d.ts +6 -0
  56. package/dist/cli-output.js +38 -1
  57. package/dist/cli-output.js.map +1 -1
  58. package/dist/cli-shared.d.ts +10 -0
  59. package/dist/cli-shared.js +0 -4
  60. package/dist/cli-shared.js.map +1 -1
  61. package/dist/cold-start-report.d.ts +32 -0
  62. package/dist/cold-start-report.js +31 -0
  63. package/dist/cold-start-report.js.map +1 -0
  64. package/dist/defect-evidence.js +10 -5
  65. package/dist/defect-evidence.js.map +1 -1
  66. package/dist/demo-aspect-api.d.ts +11 -0
  67. package/dist/demo-aspect-api.js +27 -2
  68. package/dist/demo-aspect-api.js.map +1 -1
  69. package/dist/demo-aspect-cli.js +5 -0
  70. package/dist/demo-aspect-cli.js.map +1 -1
  71. package/dist/demo-capture.d.ts +79 -2
  72. package/dist/demo-capture.js +504 -12
  73. package/dist/demo-capture.js.map +1 -1
  74. package/dist/environments.d.ts +8 -1
  75. package/dist/environments.js +14 -3
  76. package/dist/environments.js.map +1 -1
  77. package/dist/exit-codes.js +16 -0
  78. package/dist/exit-codes.js.map +1 -1
  79. package/dist/explore-adversarial.js +2 -2
  80. package/dist/explore-adversarial.js.map +1 -1
  81. package/dist/explore-shared.d.ts +11 -0
  82. package/dist/explore-shared.js +9 -0
  83. package/dist/explore-shared.js.map +1 -1
  84. package/dist/findings-filing.js +7 -3
  85. package/dist/findings-filing.js.map +1 -1
  86. package/dist/forge-verify.d.ts +224 -0
  87. package/dist/forge-verify.js +649 -0
  88. package/dist/forge-verify.js.map +1 -0
  89. package/dist/github-issue-filer.js +15 -9
  90. package/dist/github-issue-filer.js.map +1 -1
  91. package/dist/init-codeowners.d.ts +19 -0
  92. package/dist/init-codeowners.js +66 -0
  93. package/dist/init-codeowners.js.map +1 -1
  94. package/dist/job-draft-api.d.ts +52 -0
  95. package/dist/job-draft-api.js +110 -0
  96. package/dist/job-draft-api.js.map +1 -0
  97. package/dist/journeeze-api.d.ts +77 -0
  98. package/dist/journeeze-api.js +371 -0
  99. package/dist/journeeze-api.js.map +1 -0
  100. package/dist/journeeze-connect.d.ts +156 -0
  101. package/dist/journeeze-connect.js +357 -0
  102. package/dist/journeeze-connect.js.map +1 -0
  103. package/dist/journey-anchor-api.d.ts +37 -0
  104. package/dist/journey-anchor-api.js +58 -0
  105. package/dist/journey-anchor-api.js.map +1 -0
  106. package/dist/journey-anchor-cli.d.ts +17 -0
  107. package/dist/journey-anchor-cli.js +104 -0
  108. package/dist/journey-anchor-cli.js.map +1 -0
  109. package/dist/journey-anchor-gate.d.ts +52 -0
  110. package/dist/journey-anchor-gate.js +101 -0
  111. package/dist/journey-anchor-gate.js.map +1 -0
  112. package/dist/journey-api.d.ts +30 -1
  113. package/dist/journey-api.js +118 -16
  114. package/dist/journey-api.js.map +1 -1
  115. package/dist/journey-cli.js +93 -10
  116. package/dist/journey-cli.js.map +1 -1
  117. package/dist/journey-demo-api.d.ts +29 -0
  118. package/dist/journey-demo-api.js +29 -7
  119. package/dist/journey-demo-api.js.map +1 -1
  120. package/dist/journey-heal.js +1 -1
  121. package/dist/journey-heal.js.map +1 -1
  122. package/dist/journey-lint-sarif.d.ts +3 -2
  123. package/dist/journey-lint-sarif.js +11 -2
  124. package/dist/journey-lint-sarif.js.map +1 -1
  125. package/dist/journey-migrate-api.d.ts +54 -0
  126. package/dist/journey-migrate-api.js +64 -0
  127. package/dist/journey-migrate-api.js.map +1 -0
  128. package/dist/journey-network-checks.d.ts +11 -0
  129. package/dist/journey-network-checks.js +36 -13
  130. package/dist/journey-network-checks.js.map +1 -1
  131. package/dist/journey-proposal-store.d.ts +9 -0
  132. package/dist/journey-proposal-store.js +23 -0
  133. package/dist/journey-proposal-store.js.map +1 -1
  134. package/dist/journey-result-record.d.ts +2 -0
  135. package/dist/journey-result-record.js +5 -0
  136. package/dist/journey-result-record.js.map +1 -1
  137. package/dist/journey-review-api.js +9 -1
  138. package/dist/journey-review-api.js.map +1 -1
  139. package/dist/journey-review.d.ts +11 -1
  140. package/dist/journey-review.js +77 -5
  141. package/dist/journey-review.js.map +1 -1
  142. package/dist/journey-stale-api.d.ts +40 -0
  143. package/dist/journey-stale-api.js +57 -0
  144. package/dist/journey-stale-api.js.map +1 -0
  145. package/dist/load-api.js +4 -2
  146. package/dist/load-api.js.map +1 -1
  147. package/dist/locator-health-api.d.ts +101 -0
  148. package/dist/locator-health-api.js +203 -0
  149. package/dist/locator-health-api.js.map +1 -0
  150. package/dist/locator-health.d.ts +195 -0
  151. package/dist/locator-health.js +419 -0
  152. package/dist/locator-health.js.map +1 -0
  153. package/dist/mcp-api.d.ts +3 -1
  154. package/dist/mcp-api.js +21 -2
  155. package/dist/mcp-api.js.map +1 -1
  156. package/dist/mcp-cli-tools.d.ts +4 -1
  157. package/dist/mcp-cli-tools.js +82 -10
  158. package/dist/mcp-cli-tools.js.map +1 -1
  159. package/dist/openrouter-call.d.ts +23 -10
  160. package/dist/openrouter-call.js +14 -11
  161. package/dist/openrouter-call.js.map +1 -1
  162. package/dist/png-pixels.d.ts +12 -0
  163. package/dist/png-pixels.js +96 -0
  164. package/dist/png-pixels.js.map +1 -0
  165. package/dist/project-config.d.ts +29 -0
  166. package/dist/project-config.js +76 -0
  167. package/dist/project-config.js.map +1 -0
  168. package/dist/regression-api.js +2 -1
  169. package/dist/regression-api.js.map +1 -1
  170. package/dist/server-log-verify.js +2 -1
  171. package/dist/server-log-verify.js.map +1 -1
  172. package/dist/signal-triage.js +7 -3
  173. package/dist/signal-triage.js.map +1 -1
  174. package/dist/source-run-api.js +2 -2
  175. package/dist/source-run-api.js.map +1 -1
  176. package/dist/verify-fix-api.d.ts +29 -0
  177. package/dist/verify-fix-api.js +43 -2
  178. package/dist/verify-fix-api.js.map +1 -1
  179. package/dist/verify-fix-cli.js +4 -0
  180. package/dist/verify-fix-cli.js.map +1 -1
  181. package/dist/zip-store.d.ts +15 -0
  182. package/dist/zip-store.js +93 -0
  183. package/dist/zip-store.js.map +1 -0
  184. package/package.json +12 -10
  185. package/skills/jevitate-ci-check/SKILL.md +16 -0
  186. package/skills/jevitate-demo/SKILL.md +4 -1
  187. package/skills/jevitate-getting-started/SKILL.md +7 -2
  188. package/skills/jevitate-run-journey/SKILL.md +15 -0
  189. package/skills/jevitate-test-campaign/SKILL.md +26 -0
  190. package/skills/jevitate-verify-fix/SKILL.md +5 -0
@@ -0,0 +1,419 @@
1
+ import { looksGenerated } from "@jevitate/recorder";
2
+ // === Classification ===
3
+ const LEVEL_RANK = { low: 0, medium: 1, high: 2 };
4
+ /** One notch down: a rung that only resolves with an ordinal (the recorder's `CAPPED_STABILITY`). */
5
+ const CAPPED = { high: "medium", medium: "low", low: "low" };
6
+ const REASON_TEXT = {
7
+ "no-test-id": "no test id",
8
+ "test-id-not-in-convention": "test id attribute is not in the convention",
9
+ "tflow-id-not-a-test-id": "data-tflow-id is tracking metadata, not a test id",
10
+ "generated-value": "generated-looking id or name",
11
+ "no-accessible-name": "no accessible name",
12
+ "duplicate-name": "duplicate name needs disambiguation (nth)",
13
+ "positional-css": "deep or positional css path",
14
+ "text-copy": "text locator on copy likely to change",
15
+ };
16
+ /** The target a step acts on, or null (navigate, press, assert, a forEach's items are its own). */
17
+ export function stepTarget(step) {
18
+ switch (step.kind) {
19
+ case "click":
20
+ case "fill":
21
+ case "waitFor":
22
+ case "extract":
23
+ case "select":
24
+ case "upload":
25
+ case "editText":
26
+ return step.target;
27
+ default:
28
+ return null;
29
+ }
30
+ }
31
+ /** The recorded rung replay tries (an anchor first, as `resolveTarget` does). */
32
+ function recordedRung(d) {
33
+ if (d.anchor !== undefined && d.testId === undefined && (d.anchor.id !== undefined || d.anchor.name !== undefined))
34
+ return "anchor";
35
+ return ownRung(d);
36
+ }
37
+ function ownRung(d) {
38
+ if (d.testId)
39
+ return "testId";
40
+ if (d.role && d.name)
41
+ return "role+name";
42
+ if (d.label)
43
+ return "label";
44
+ if (d.text)
45
+ return "text";
46
+ if (d.css)
47
+ return "css";
48
+ return d.role ? "role" : "css";
49
+ }
50
+ function asRung(r) {
51
+ return ["testId", "anchor", "role+name", "role", "label", "text", "css"].find((x) => x === r) ?? "css";
52
+ }
53
+ /** How deep a css path is, and whether it is positional. */
54
+ function cssShape(css) {
55
+ return { depth: css.split(">").length, positional: /:nth-(of-type|child)\(|:first-|:last-/.test(css) };
56
+ }
57
+ function describeLocator(d, rung) {
58
+ const nth = d.ordinal === undefined ? "" : ` nth=${d.ordinal}`;
59
+ switch (rung) {
60
+ case "testId":
61
+ return `testId[${d.testIdAttr ?? "data-testid"}]=${d.testId ?? "?"}`;
62
+ case "anchor":
63
+ return d.anchor?.id !== undefined ? `[id=${JSON.stringify(d.anchor.id)}]` : `[name=${JSON.stringify(d.anchor?.name ?? "")}]`;
64
+ case "role+name":
65
+ return `role=${d.role ?? "?"} name=${JSON.stringify(d.name ?? "")}${nth}`;
66
+ case "label":
67
+ return `label=${JSON.stringify(d.label ?? "")}${nth}`;
68
+ case "text":
69
+ return `text=${JSON.stringify(d.text ?? "")}${nth}`;
70
+ default:
71
+ return `css=${d.css ?? "?"}`;
72
+ }
73
+ }
74
+ /**
75
+ * One target's verdict against the convention. `via` (a run's resolution) replaces the recorded rung
76
+ * when present; everything else is read from the descriptor.
77
+ */
78
+ export function classifyTarget(d, testIdAttributes, via) {
79
+ const rung = via === undefined ? recordedRung(d) : asRung(via.rung);
80
+ const ordinal = via === undefined ? d.ordinal : via.ordinal;
81
+ const codes = [];
82
+ let level;
83
+ switch (rung) {
84
+ case "testId": {
85
+ const attr = (via?.testIdAttr ?? d.testIdAttr ?? "data-testid").toLowerCase();
86
+ const generated = looksGenerated(d.testId ?? "");
87
+ level = generated ? "low" : "high";
88
+ if (attr === "data-tflow-id")
89
+ codes.push("tflow-id-not-a-test-id");
90
+ else if (!testIdAttributes.includes(attr))
91
+ codes.push("test-id-not-in-convention");
92
+ if (generated)
93
+ codes.push("generated-value");
94
+ break;
95
+ }
96
+ case "anchor":
97
+ // A non-generated id/name attribute (the recorder refuses generated ones): sturdy, but not the convention.
98
+ level = "medium";
99
+ codes.push("no-test-id");
100
+ break;
101
+ case "role+name":
102
+ level = looksGenerated(d.name ?? "") ? "low" : "high";
103
+ codes.push("no-test-id");
104
+ if (level === "low")
105
+ codes.push("generated-value");
106
+ break;
107
+ case "label":
108
+ level = "medium";
109
+ codes.push("no-test-id");
110
+ break;
111
+ case "text":
112
+ level = "medium";
113
+ codes.push("no-test-id", "text-copy");
114
+ break;
115
+ default: {
116
+ level = "low";
117
+ codes.push("no-test-id");
118
+ const shape = cssShape(d.css ?? "");
119
+ if (shape.positional || shape.depth > 3)
120
+ codes.push("positional-css");
121
+ if (/#[A-Za-z0-9_-]*\d{4}|[0-9a-fA-F]{8}/.test(d.css ?? ""))
122
+ codes.push("generated-value");
123
+ }
124
+ }
125
+ if (ordinal !== undefined && rung !== "testId" && rung !== "anchor" && rung !== "css") {
126
+ level = CAPPED[level];
127
+ codes.push("duplicate-name");
128
+ }
129
+ if (rung !== "testId" && !(d.name || d.label))
130
+ codes.push("no-accessible-name");
131
+ if (d.tflowId !== undefined && rung !== "testId" && !codes.includes("tflow-id-not-a-test-id"))
132
+ codes.push("tflow-id-not-a-test-id");
133
+ return { rung, level, codes };
134
+ }
135
+ // === Suggestions ===
136
+ /** `"Save contact"` → `save-contact` (ASCII letters and digits, at most 40 chars). */
137
+ export function kebabCase(s) {
138
+ return s
139
+ .normalize("NFKD")
140
+ .replace(/[̀-ͯ]/g, "")
141
+ .replace(/([a-z0-9])([A-Z])/g, "$1-$2")
142
+ .toLowerCase()
143
+ .replace(/[^a-z0-9]+/g, "-")
144
+ .replace(/^-+|-+$/g, "")
145
+ .slice(0, 40)
146
+ .replace(/-+$/g, "");
147
+ }
148
+ /** The path of a page URL (`https://x/contacts/new?a=1` → `/contacts/new`); a path is kept as is. */
149
+ export function routeOf(url) {
150
+ if (url === undefined || url === "")
151
+ return undefined;
152
+ try {
153
+ return new URL(url).pathname;
154
+ }
155
+ catch {
156
+ const path = url.split(/[?#]/)[0] ?? url;
157
+ return path.startsWith("/") ? path : `/${path}`;
158
+ }
159
+ }
160
+ function singular(word) {
161
+ if (/ies$/.test(word))
162
+ return word.replace(/ies$/, "y");
163
+ if (/(ss|us)$/.test(word))
164
+ return word;
165
+ return word.replace(/s$/, "");
166
+ }
167
+ /** The route's first meaningful segment (not an id or a verb like `new`/`edit`), singular. */
168
+ function routeWords(route) {
169
+ return (route ?? "").split("/").filter((s) => s !== "" && !/^\d+$/.test(s) && !looksGenerated(s) && !/^(new|edit|create|index|view)$/i.test(s));
170
+ }
171
+ function routeNoun(route) {
172
+ const first = routeWords(route)[0];
173
+ return first === undefined ? undefined : singular(kebabCase(first));
174
+ }
175
+ /**
176
+ * The suggested test id: the element's name in kebab-case, with the route's noun when the name is a
177
+ * single word (`Save` on /contacts/new → `save-contact`); an unnamed element is named by its route
178
+ * and tag (`contacts-div`).
179
+ */
180
+ export function suggestTestId(name, route, fallbackTag) {
181
+ const base = name === undefined ? "" : kebabCase(name);
182
+ const noun = routeNoun(route);
183
+ if (base !== "")
184
+ return !base.includes("-") && noun !== undefined && !base.includes(noun) ? `${base}-${noun}` : base;
185
+ const where = kebabCase(routeWords(route).join("-"));
186
+ return `${where === "" ? "page" : where}-${fallbackTag}`;
187
+ }
188
+ function lastCssTag(css) {
189
+ const last = (css ?? "").split(">").pop()?.trim() ?? "";
190
+ return /^([a-z][a-z0-9-]*)/i.exec(last)?.[1]?.toLowerCase() ?? "element";
191
+ }
192
+ function describeElement(d) {
193
+ const name = d.name ?? d.label;
194
+ if (d.role && name)
195
+ return `the ${JSON.stringify(name)} ${d.role}`;
196
+ if (d.label)
197
+ return `the ${JSON.stringify(d.label)} field`;
198
+ if (d.role)
199
+ return `the ${d.role}`;
200
+ if (d.text)
201
+ return `the element with text ${JSON.stringify(d.text.length > 40 ? `${d.text.slice(0, 40)}…` : d.text)}`;
202
+ if (d.testId)
203
+ return `the element with ${d.testIdAttr ?? "data-testid"}=${JSON.stringify(d.testId)}`;
204
+ return `the element at css ${JSON.stringify(d.css ?? "?")}`;
205
+ }
206
+ /** The element's identity across steps and runs: route + role + name (else its css). */
207
+ export function suggestionKey(d, route) {
208
+ const name = d.name ?? d.label ?? d.text;
209
+ if (name !== undefined)
210
+ return `${route ?? ""}|${d.role ?? ""}|${name}`;
211
+ if (d.testId !== undefined)
212
+ return `${route ?? ""}|testid|${d.testId}`;
213
+ return `${route ?? ""}|css|${d.css ?? ""}`;
214
+ }
215
+ function fixFor(d, codes, attribute, testId, element, route) {
216
+ const on = route === undefined ? "" : ` on ${route}`;
217
+ const named = codes.includes("no-accessible-name") ? " and give it an accessible name (visible text or aria-label)" : "";
218
+ if (codes.includes("test-id-not-in-convention") && d.testId !== undefined && !looksGenerated(d.testId)) {
219
+ return `use ${attribute}=${JSON.stringify(d.testId)} instead of ${d.testIdAttr ?? "?"} on ${element}${on} (or add ${d.testIdAttr ?? "?"} to testIdAttributes in .jevitate/project.json)`;
220
+ }
221
+ if (d.testId !== undefined && looksGenerated(d.testId)) {
222
+ return `replace the generated ${d.testIdAttr ?? attribute}=${JSON.stringify(d.testId)} on ${element}${on} with a stable ${attribute}=${JSON.stringify(testId)}`;
223
+ }
224
+ return `add ${attribute}=${JSON.stringify(testId)} to ${element}${on}${named}`;
225
+ }
226
+ function buildSuggestion(d, codes, route, attribute, html, occurrence) {
227
+ const name = d.name ?? d.label ?? d.text;
228
+ const testId = suggestTestId(d.testId !== undefined && !looksGenerated(d.testId) ? d.testId : name, route, lastCssTag(d.css));
229
+ const element = describeElement(d);
230
+ return {
231
+ key: suggestionKey(d, route),
232
+ ...(route === undefined ? {} : { route }),
233
+ ...(d.role === undefined ? {} : { role: d.role }),
234
+ ...(name === undefined ? {} : { name }),
235
+ element,
236
+ ...(html === undefined ? {} : { html: html.length > 160 ? `${html.slice(0, 160)}…` : html }),
237
+ attribute,
238
+ testId,
239
+ fix: fixFor(d, codes, attribute, testId, element, route),
240
+ reasons: codes.map((c) => REASON_TEXT[c]),
241
+ occurrences: [occurrence],
242
+ };
243
+ }
244
+ /** Merges suggestions for the same element (route + role + name): occurrences and reasons combined. */
245
+ export function dedupeSuggestions(lists) {
246
+ const byKey = new Map();
247
+ for (const s of lists.flat()) {
248
+ const prev = byKey.get(s.key);
249
+ if (prev === undefined) {
250
+ byKey.set(s.key, s);
251
+ continue;
252
+ }
253
+ const occ = [...prev.occurrences];
254
+ for (const o of s.occurrences) {
255
+ if (!occ.some((p) => p.journeyId === o.journeyId && p.index === o.index && p.stepId === o.stepId))
256
+ occ.push(o);
257
+ }
258
+ byKey.set(s.key, {
259
+ ...prev,
260
+ ...(prev.html === undefined && s.html !== undefined ? { html: s.html } : {}),
261
+ reasons: [...new Set([...prev.reasons, ...s.reasons])],
262
+ occurrences: occ,
263
+ });
264
+ }
265
+ // The elements most steps depend on first: the highest-value fixes lead the work list.
266
+ return [...byKey.values()].sort((a, b) => b.occurrences.length - a.occurrences.length || a.key.localeCompare(b.key));
267
+ }
268
+ // === Analysis ===
269
+ function flatWithRoute(rec) {
270
+ return rec.pages.flatMap((p) => p.steps.map((recorded) => ({ recorded, route: routeOf(p.url) })));
271
+ }
272
+ export function emptyLevels() {
273
+ return { high: 0, medium: 0, low: 0 };
274
+ }
275
+ /** `7/9 steps on stable locators; 2 brittle (high 7 · medium 1 · low 1)`. */
276
+ export function healthLine(h) {
277
+ const total = h.stable + h.brittle;
278
+ if (total === 0)
279
+ return "no steps act on a target";
280
+ return `${h.stable}/${total} steps on stable locators; ${h.brittle} brittle (high ${h.levels.high} · medium ${h.levels.medium} · low ${h.levels.low})`;
281
+ }
282
+ /**
283
+ * A recording's locator health: every step with a target (flat index, as the interpreter numbers
284
+ * them), against the convention; with `resolved`, the rung each step actually matched at replay.
285
+ */
286
+ export function analyzeRecording(rec, opts) {
287
+ const attribute = opts.testIdAttributes[0] ?? "data-testid";
288
+ const resolvedBy = new Map((opts.resolved ?? []).map((r) => [r.index, r]));
289
+ const steps = [];
290
+ const suggestions = [];
291
+ const levels = { high: 0, medium: 0, low: 0 };
292
+ flatWithRoute(rec).forEach(({ recorded, route }, index) => {
293
+ const d = stepTarget(recorded.step);
294
+ if (d === null)
295
+ return;
296
+ const via = resolvedBy.get(index);
297
+ const v = classifyTarget(d, opts.testIdAttributes, via);
298
+ const brittle = v.codes.length > 0;
299
+ levels[v.level] += 1;
300
+ const stepId = recorded.stepId ?? via?.stepId;
301
+ let key;
302
+ if (brittle) {
303
+ const occurrence = { ...(opts.journeyId === undefined ? {} : { journeyId: opts.journeyId }), index, ...(stepId === undefined ? {} : { stepId }) };
304
+ const s = buildSuggestion(d, v.codes, route, attribute, opts.html?.get(index), occurrence);
305
+ suggestions.push(s);
306
+ key = s.key;
307
+ }
308
+ steps.push({
309
+ ...(stepId === undefined ? {} : { stepId }),
310
+ index,
311
+ kind: recorded.step.kind,
312
+ rung: v.rung,
313
+ level: v.level,
314
+ stability: brittle ? "brittle" : "stable",
315
+ source: via === undefined ? "recorded" : "resolved",
316
+ ...(route === undefined ? {} : { route }),
317
+ locator: describeLocator(d, v.rung),
318
+ reasons: v.codes.map((c) => REASON_TEXT[c]),
319
+ reasonCodes: v.codes,
320
+ ...(key === undefined ? {} : { suggestion: key }),
321
+ });
322
+ });
323
+ const stable = steps.filter((s) => s.stability === "stable").length;
324
+ const brittle = steps.length - stable;
325
+ return { stable, brittle, steps, levels, line: healthLine({ stable, brittle, levels }), suggestions: dedupeSuggestions([suggestions]) };
326
+ }
327
+ /** Totals over several healths (a report, a check): counts, levels and the de-duplicated work list. */
328
+ export function combineHealth(healths) {
329
+ const levels = { high: 0, medium: 0, low: 0 };
330
+ let stable = 0;
331
+ let brittle = 0;
332
+ for (const h of healths) {
333
+ stable += h.stable;
334
+ brittle += h.brittle;
335
+ levels.high += h.levels.high;
336
+ levels.medium += h.levels.medium;
337
+ levels.low += h.levels.low;
338
+ }
339
+ return { stable, brittle, levels, line: healthLine({ stable, brittle, levels }), suggestions: dedupeSuggestions(healths.map((h) => h.suggestions)) };
340
+ }
341
+ // === Gate ===
342
+ /** `check --max-brittle-steps <n>`: true when a Journey item's brittle steps exceed the opt-in threshold. */
343
+ export function exceedsBrittleSteps(health, maxBrittleSteps) {
344
+ return maxBrittleSteps !== undefined && health.brittle > maxBrittleSteps;
345
+ }
346
+ function stepKey(s) {
347
+ return s.stepId ?? `#${s.index}`;
348
+ }
349
+ function score(s) {
350
+ return (s.stability === "stable" ? 10 : 0) + LEVEL_RANK[s.level ?? (s.stability === "stable" ? "high" : "low")];
351
+ }
352
+ /**
353
+ * The trend of `current` against `baseline` (a previous report or run of the same Journey), keyed by
354
+ * step id (else index): a Journey re-recorded or healed after the app added test ids shows its
355
+ * improvement; a step that fell back to a brittle rung shows as a regression.
356
+ */
357
+ export function compareLocatorHealth(current, baseline) {
358
+ const before = new Map(baseline.steps.map((s) => [stepKey(s), s]));
359
+ const seen = new Set();
360
+ const changes = [];
361
+ let unchanged = 0;
362
+ let added = 0;
363
+ const shape = (s) => ({ rung: s.rung, level: s.level ?? (s.stability === "stable" ? "high" : "low"), stability: s.stability });
364
+ for (const s of current.steps) {
365
+ const key = stepKey(s);
366
+ seen.add(key);
367
+ const b = before.get(key);
368
+ if (b === undefined) {
369
+ added++;
370
+ continue;
371
+ }
372
+ const delta = score(s) - score(b);
373
+ if (delta === 0)
374
+ unchanged++;
375
+ else
376
+ changes.push({ key, from: shape(b), to: shape(s), direction: delta > 0 ? "improved" : "regressed" });
377
+ }
378
+ const removed = [...before.keys()].filter((k) => !seen.has(k)).length;
379
+ return {
380
+ improved: changes.filter((c) => c.direction === "improved").length,
381
+ regressed: changes.filter((c) => c.direction === "regressed").length,
382
+ unchanged,
383
+ added,
384
+ removed,
385
+ brittleDelta: current.brittle - baseline.brittle,
386
+ changes,
387
+ };
388
+ }
389
+ /** `trend vs baseline: 2 improved, 1 regressed (brittle -1)`. */
390
+ export function trendLine(t) {
391
+ const sign = t.brittleDelta > 0 ? `+${t.brittleDelta}` : String(t.brittleDelta);
392
+ return `trend vs baseline: ${t.improved} improved, ${t.regressed} regressed (brittle ${sign})`;
393
+ }
394
+ /** The compact form of a detail: per-step rung/stability/reasons, and the top `top` fixes. */
395
+ export function compactHealth(h, top = 5) {
396
+ return {
397
+ stable: h.stable,
398
+ brittle: h.brittle,
399
+ levels: h.levels,
400
+ line: h.line,
401
+ steps: h.steps.map((s) => ({
402
+ ...(s.stepId === undefined ? {} : { stepId: s.stepId }),
403
+ index: s.index,
404
+ rung: s.rung,
405
+ stability: s.stability,
406
+ reasons: s.reasons,
407
+ ...("level" in s ? { level: s.level } : {}),
408
+ })),
409
+ suggestions: h.suggestions.slice(0, top).map((s) => ({
410
+ key: s.key,
411
+ element: s.element,
412
+ fix: s.fix,
413
+ reasons: s.reasons,
414
+ ...(s.route === undefined ? {} : { route: s.route }),
415
+ steps: s.occurrences.length,
416
+ })),
417
+ };
418
+ }
419
+ //# sourceMappingURL=locator-health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locator-health.js","sourceRoot":"","sources":["../src/locator-health.ts"],"names":[],"mappings":"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"}
package/dist/mcp-api.d.ts CHANGED
@@ -57,13 +57,15 @@ export interface McpJourneyRunOptions {
57
57
  readonly fixtures?: (site: string) => MissionFixtures | undefined;
58
58
  /** #303: record and compare each replayed step's action delta (`--action-deltas`). */
59
59
  readonly actionDeltas?: boolean;
60
+ /** #486: the in-flight response bound of a step's responseStatus check (`--step-response-timeout`). */
61
+ readonly stepResponseTimeoutMs?: number;
60
62
  }
61
63
  /**
62
64
  * #255: what `verify_fix` hands the runner — the finding (resolved result path + fingerprint) and
63
65
  * the CLI-parity replay options. Operator-only settings (shell hooks, `cmd:` log sources, re-sending
64
66
  * paid/destructive hang writes, redaction literals) are never MCP arguments: targets.json decides.
65
67
  */
66
- export type McpVerifyFixArgs = Pick<RunVerifyFixOptions, "resultPath" | "fingerprint"> & Partial<Pick<RunVerifyFixOptions, "storageState" | "browser" | "replays" | "invariantFiles" | "fixtureFlags" | "emulation" | "allowEmulationOverride" | "screenshots" | "actionDeltas">>;
68
+ export type McpVerifyFixArgs = Pick<RunVerifyFixOptions, "resultPath" | "fingerprint"> & Partial<Pick<RunVerifyFixOptions, "storageState" | "browser" | "replays" | "invariantFiles" | "fixtureFlags" | "emulation" | "allowEmulationOverride" | "screenshots" | "actionDeltas" | "allow" | "env" | "environmentsFile">>;
67
69
  export interface McpApiDeps {
68
70
  /** Journeys store directory (`~/.jevitate/journeys` in production). */
69
71
  journeysDir: string;
package/dist/mcp-api.js CHANGED
@@ -25,6 +25,7 @@ import { parseScreenshotsArg } from "./run-screenshots.js";
25
25
  import { makeEvidenceSelfHealer } from "./self-heal-adapter.js";
26
26
  import { JourneyHealArgsError, MCP_HEAL_NAMES, journeyHealBudget, readJourneyChangeScope, validateJourneyHeal } from "./journey-heal.js";
27
27
  import { ChangesArgsError, ChangesInputError } from "./change-context.js";
28
+ import { MAX_STEP_RESPONSE_TIMEOUT_MS } from "./journey-network-checks.js";
28
29
  import { McpArgError, argErrorBody, optBool, optEnum, optInt, optNamedSessions, optPath, optExtensions, optRecordVideo, optScreenshots, optString, optStringArray, optStringMap, optViewport } from "./mcp-args.js";
29
30
  import { defaultMcpPathRoots } from "./mcp-paths.js";
30
31
  import { CLI_TOOL_SPECS, cliToolInputSchema, runCliTool } from "./mcp-cli-tools.js";
@@ -128,6 +129,7 @@ export function buildMcpTools(deps) {
128
129
  ...(o?.screenshots === undefined ? {} : { screenshots: o.screenshots }),
129
130
  ...(o?.fixtures === undefined ? {} : { fixtures: o.fixtures }),
130
131
  ...(o?.actionDeltas === true ? { actionDeltas: true } : {}),
132
+ ...(o?.stepResponseTimeoutMs === undefined ? {} : { stepResponseTimeoutMs: o.stepResponseTimeoutMs }),
131
133
  })));
132
134
  const pathRoots = deps.pathRoots ?? defaultMcpPathRoots();
133
135
  // queue_exploration: enqueue over the SAME promoted fs store `jevitate
@@ -332,6 +334,9 @@ export function buildMcpTools(deps) {
332
334
  ...(a.allowEmulationOverride === undefined ? {} : { allowEmulationOverride: a.allowEmulationOverride }),
333
335
  ...(a.screenshots === undefined ? {} : { screenshots: a.screenshots }),
334
336
  ...(a.actionDeltas === true ? { actionDeltas: true } : {}),
337
+ ...(a.allow === undefined ? {} : { allow: a.allow }),
338
+ ...(a.env === undefined ? {} : { env: a.env }),
339
+ ...(a.environmentsFile === undefined ? {} : { environmentsFile: a.environmentsFile }),
335
340
  }));
336
341
  /** #255: verify_fix's CLI-parity replay options, validated like `verify-fix`'s flags (typed, before any browser). */
337
342
  const verifyFixOptions = (args) => {
@@ -353,6 +358,12 @@ export function buildMcpTools(deps) {
353
358
  const extension = optExtensions(args, pathRoots);
354
359
  const governance = governanceArgs(args);
355
360
  const actionDeltas = optBool(args, "actionDeltas");
361
+ // #484: replaying under another allow list widens what the replay may reach — explicit only (never
362
+ // defaulted), and the report carries allowlistOverride. An MCP caller never names origins: only
363
+ // `env` (an operator-configured environment) can override the stored list; `--allow` is CLI-only.
364
+ if (args["allow"] !== undefined)
365
+ throw new McpArgError("'allow' is not an MCP argument: a replay reaches only operator-configured origins — pass 'env' (an environment in environments.json), or run `jevitate verify-fix --allow` yourself");
366
+ const env = optString(args, "env");
356
367
  try {
357
368
  const browser = browserRunFromFlags({ browserArg: [], ...governance, ...(extension === undefined ? {} : { extension }), ...(headed === undefined ? {} : { headed }), ...(slowMo === undefined ? {} : { slowMo }), ...(recordVideo === undefined ? {} : { recordVideo }) });
358
369
  const screenshots = parseScreenshotsArg(shots);
@@ -369,6 +380,7 @@ export function buildMcpTools(deps) {
369
380
  ...(screenshots === undefined ? {} : { screenshots }),
370
381
  ...(emulation === undefined ? {} : { emulation }),
371
382
  ...(actionDeltas === true ? { actionDeltas: true } : {}),
383
+ ...(env === undefined ? {} : { env, ...(deps.environmentsFile === undefined ? {} : { environmentsFile: deps.environmentsFile }) }),
372
384
  };
373
385
  }
374
386
  catch (err) {
@@ -517,6 +529,10 @@ export function buildMcpTools(deps) {
517
529
  const healMaxMs = optInt(args, "healMaxMs", 1);
518
530
  const healMaxRunAttempts = optInt(args, "healMaxRunAttempts", 1);
519
531
  const healMaxRunMs = optInt(args, "healMaxRunMs", 1);
532
+ const stepResponseTimeoutMs = optInt(args, "stepResponseTimeoutMs", 1);
533
+ if (stepResponseTimeoutMs !== undefined && stepResponseTimeoutMs > MAX_STEP_RESPONSE_TIMEOUT_MS) {
534
+ throw new McpArgError(`'stepResponseTimeoutMs' must be an integer from 1 to ${MAX_STEP_RESPONSE_TIMEOUT_MS}`);
535
+ }
520
536
  const healRequest = {
521
537
  selfHeal,
522
538
  ...(changes === undefined ? {} : { changes }),
@@ -559,6 +575,7 @@ export function buildMcpTools(deps) {
559
575
  ...(emulation === undefined ? {} : { emulation }),
560
576
  ...(screenshots === undefined ? {} : { screenshots }),
561
577
  ...(optBool(args, "actionDeltas") === true ? { actionDeltas: true } : {}),
578
+ ...(stepResponseTimeoutMs === undefined ? {} : { stepResponseTimeoutMs }),
562
579
  },
563
580
  ...(usage === undefined ? {} : { usage }),
564
581
  };
@@ -568,7 +585,7 @@ export function buildMcpTools(deps) {
568
585
  description: "Replay a finding's reproduction (by mission result id — or a finished queue_exploration missionId — + fingerprint) N times in fresh browsers (default 3) — `jevitate verify-fix`. status: fixed (signal absent on every replay) | still-reproduces | intermittent (fired on some but not all replays — never a pass) | inconclusive (replay could not reach the step — never a pass). " +
569
586
  "#255 (same as the CLI flags): 'replays' (>= 1); 'recordVideo' (true or a directory: the before/after evidence pair — `evidence.before` is the run's own clip, `evidence.after` a captioned replay — plus videoPaths); 'screenshots'; 'headed'/'slowMo'; " +
570
587
  "'storageState' (a path inside the project or ~/.jevitate, never a repo's .jevitate/; overrides the mission's session); 'viewport' {width,height} or 'device' (refused when it differs from the finding's recorded emulation unless 'allowEmulationOverride'); 'geolocation' '<lat>,<lng>[,<accuracy m>]' (#329); " +
571
- "'invariants' (invariant file paths re-checking a declared-invariant defect); 'fixtures' (a fixtures file overriding the mission's saved one); 'fixtureIdentity' (#243: 'name=<storageState path>' entries re-binding a fixture step's auth.identity; default: the identities the mission recorded); 'extension' (#256: unpacked extension directories — refused unless the same build the finding was recorded with); 'actionDeltas' (#303, opt-in: the defect step's replayed delta vs the recorded one, as evidence on each attempt); 'maxBrowsers'/'maxBrowserMemory' (#205: machine-wide browser cap, browser memory ceiling in MiB); 'tags' (#426: {key: value} run metadata, returned on the result — never a secret). Operator-only settings (shell hooks, cmd: log sources, re-sending paid/destructive hang writes) come from targets.json, never an argument.",
588
+ "'invariants' (invariant file paths re-checking a declared-invariant defect); 'fixtures' (a fixtures file overriding the mission's saved one); 'fixtureIdentity' (#243: 'name=<storageState path>' entries re-binding a fixture step's auth.identity; default: the identities the mission recorded); 'extension' (#256: unpacked extension directories — refused unless the same build the finding was recorded with); 'actionDeltas' (#303, opt-in: the defect step's replayed delta vs the recorded one, as evidence on each attempt); 'maxBrowsers'/'maxBrowserMemory' (#205: machine-wide browser cap, browser memory ceiling in MiB); 'tags' (#426: {key: value} run metadata, returned on the result — never a secret); #484 'env' (a named environment in environments.json: replay under its CURRENT allow list instead of the one stored with the result, e.g. to prove a localhost → 127.0.0.1 config fix; explicit only, never defaulted) — the result then carries allowlistOverride (source, origins, recorded, added, removed); raw origins are CLI-only (`jevitate verify-fix --allow`), never an MCP argument. Operator-only settings (shell hooks, cmd: log sources, re-sending paid/destructive hang writes) come from targets.json, never an argument.",
572
589
  inputSchema: {
573
590
  type: "object",
574
591
  properties: {
@@ -592,6 +609,7 @@ export function buildMcpTools(deps) {
592
609
  maxBrowsers: { type: "integer", minimum: 1 },
593
610
  maxBrowserMemory: { type: "integer", minimum: 1 },
594
611
  tags: { type: "object", additionalProperties: { type: "string" } },
612
+ env: { type: "string" },
595
613
  },
596
614
  required: ["id", "fingerprint"],
597
615
  },
@@ -615,7 +633,7 @@ export function buildMcpTools(deps) {
615
633
  "with a clear error when no storageState is given. #255 (same as the CLI flags): 'env' (a named environment in .jevitate/environments.json; its session from ~/.jevitate/targets.json) and/or 'baseUrl'; " +
616
634
  "'headed' (needs a display) and 'slowMo'; 'recordVideo' (true, or a directory) → videoPaths; 'screenshots' (true | screens | steps | mode:<dir> | <dir>) → screenshotPaths; " +
617
635
  "'viewport' {width,height} or 'device' (mutually exclusive); 'geolocation' '<lat>,<lng>[,<accuracy m>]' (#329); 'fixtures' (a fixtures JSON path: setup before, restore after; 'fixtureIdentity' (#243) 'name=<storageState path>' entries name who a step with auth.identity authenticates as; an environment's shell hooks are never run over MCP — they refuse as the CLI does without --allow-shell-hooks); " +
618
- "'extension' (#256: unpacked extension directories inside the project or ~/.jevitate; a Journey recorded with extensions needs the same build); 'selfHeal' fail-closed (default) | hybrid | full with 'real' or 'fakeAi' (a write step never self-heals; 'jevProvider' typesafe | openrouter picks the Jev key with real, #429) — #453: a heal mode needs a change context, 'changes' (a git range such as HEAD~1..HEAD, read-only) and/or 'changeNote' (strings), else it is refused; only a break that change explains is retargeted, one-for-one, and the run ends healed-pending-review (exitCode 5: a proposed revision a person accepts, never a pass); budgets 'healMaxAttempts'/'healMaxModelCalls'/'healMaxMs' (per broken step) and 'healMaxRunAttempts'/'healMaxRunMs' (per run), each >= 1; the result's 'exitCode' is the CLI's (0 ok, 5 pending review, 1 failed); 'actionDeltas' (#303, opt-in: each replayed step's action delta, compared with the recorded one → actionDeltas); 'maxBrowsers'/'maxBrowserMemory' (#205: machine-wide browser cap, browser memory ceiling in MiB); 'tags' (#426: {key: value} run metadata, returned on the result — never a secret). A site-policy refusal is {error: throttled, retryAfter}.",
636
+ "'extension' (#256: unpacked extension directories inside the project or ~/.jevitate; a Journey recorded with extensions needs the same build); 'selfHeal' fail-closed (default) | hybrid | full with 'real' or 'fakeAi' (a write step never self-heals; 'jevProvider' typesafe | openrouter picks the Jev key with real, #429) — #453: a heal mode needs a change context, 'changes' (a git range such as HEAD~1..HEAD, read-only) and/or 'changeNote' (strings), else it is refused; only a break that change explains is retargeted, one-for-one, and the run ends healed-pending-review (exitCode 5: a proposed revision a person accepts, never a pass); budgets 'healMaxAttempts'/'healMaxModelCalls'/'healMaxMs' (per broken step) and 'healMaxRunAttempts'/'healMaxRunMs' (per run), each >= 1; the result's 'exitCode' is the CLI's (0 ok, 5 pending review, 1 failed); 'actionDeltas' (#303, opt-in: each replayed step's action delta, compared with the recorded one → actionDeltas); 'stepResponseTimeoutMs' (#486, 1..300000, default 15000: how long a step's responseStatus check waits for a request sent but not yet answered, e.g. a cold start — a late 2xx is an advisory slowSteps finding; a step's own responseTimeoutMs overrides it); 'maxBrowsers'/'maxBrowserMemory' (#205: machine-wide browser cap, browser memory ceiling in MiB); 'tags' (#426: {key: value} run metadata, returned on the result — never a secret). A site-policy refusal is {error: throttled, retryAfter}.",
619
637
  inputSchema: {
620
638
  type: "object",
621
639
  properties: {
@@ -648,6 +666,7 @@ export function buildMcpTools(deps) {
648
666
  maxBrowsers: { type: "integer", minimum: 1 },
649
667
  maxBrowserMemory: { type: "integer", minimum: 1 },
650
668
  actionDeltas: { type: "boolean" },
669
+ stepResponseTimeoutMs: { type: "integer", minimum: 1, maximum: MAX_STEP_RESPONSE_TIMEOUT_MS },
651
670
  tags: { type: "object", additionalProperties: { type: "string" } },
652
671
  },
653
672
  required: ["id"],