testaro 78.0.6 → 78.4.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 (361) hide show
  1. package/.gitattributes +94 -0
  2. package/.github/workflows/ci.yml +58 -0
  3. package/.github/workflows/publish.yml +49 -0
  4. package/.github/workflows/typescript.yml +35 -0
  5. package/AGENTS.md +2 -2
  6. package/CLAUDE.md +25 -19
  7. package/CONTAINERS.md +10 -6
  8. package/CONTRIBUTING.md +29 -3
  9. package/Dockerfile +1 -1
  10. package/README.md +75 -20
  11. package/UPGRADES.md +4 -0
  12. package/actSpecs-doc.md +13 -1
  13. package/actSpecs.js +29 -5
  14. package/call.js +6 -5
  15. package/docker-compose.yml +8 -2
  16. package/docs/checkpoint-scanning.md +199 -0
  17. package/docs/standard-result-outcome.md +165 -0
  18. package/env.example +44 -10
  19. package/eslint.config.mjs +101 -0
  20. package/netWatch.js +373 -222
  21. package/package.json +46 -31
  22. package/pour/README.md +47 -0
  23. package/pour/pour.min.js +8 -0
  24. package/procs/actDo.js +808 -0
  25. package/procs/catalog.d.ts +17 -0
  26. package/procs/catalog.js +313 -221
  27. package/procs/catalog.ts +398 -0
  28. package/procs/checkpoint.js +110 -0
  29. package/procs/config.d.ts +35 -0
  30. package/procs/config.js +63 -0
  31. package/procs/dateTime.js +2 -1
  32. package/procs/doActs.js +442 -849
  33. package/procs/doTestAct.js +27 -121
  34. package/procs/flow.js +221 -0
  35. package/procs/generateRuleRegistry.js +94 -0
  36. package/procs/getSource.d.ts +26 -0
  37. package/procs/getSource.js +1 -0
  38. package/procs/job.js +116 -1
  39. package/procs/launch.d.ts +40 -0
  40. package/procs/launch.js +239 -108
  41. package/procs/nu.d.ts +48 -0
  42. package/procs/nu.js +1 -0
  43. package/procs/scope.js +196 -0
  44. package/procs/shoot.d.ts +33 -0
  45. package/procs/shoot.js +3 -1
  46. package/procs/standard.d.ts +18 -0
  47. package/procs/standard.js +83 -0
  48. package/procs/standard.ts +126 -0
  49. package/procs/testAct.js +145 -0
  50. package/procs/testaro.d.ts +20 -0
  51. package/procs/testaro.js +237 -189
  52. package/procs/testaro.ts +321 -0
  53. package/procs/userPath.js +126 -0
  54. package/procs/xPath.d.ts +4 -0
  55. package/procs/xPath.js +82 -64
  56. package/procs/xPath.ts +106 -0
  57. package/procs/xPathScript.d.ts +6 -0
  58. package/procs/xPathScript.js +70 -0
  59. package/run.js +3 -2
  60. package/surea11y/README.md +44 -0
  61. package/surea11y/surea11y.browser.js +14 -0
  62. package/testaro/adbID.d.ts +3 -0
  63. package/testaro/adbID.js +39 -41
  64. package/testaro/adbID.ts +66 -0
  65. package/testaro/allCapStyle.d.ts +3 -0
  66. package/testaro/allCapStyle.js +32 -34
  67. package/testaro/allCapStyle.ts +55 -0
  68. package/testaro/allCaps.d.ts +16 -0
  69. package/testaro/allCaps.js +179 -151
  70. package/testaro/allCaps.ts +228 -0
  71. package/testaro/allHidden.d.ts +13 -0
  72. package/testaro/allHidden.js +30 -30
  73. package/testaro/allHidden.ts +50 -0
  74. package/testaro/allSlanted.d.ts +3 -0
  75. package/testaro/allSlanted.js +31 -33
  76. package/testaro/allSlanted.ts +54 -0
  77. package/testaro/altScheme.d.ts +3 -0
  78. package/testaro/altScheme.js +28 -30
  79. package/testaro/altScheme.ts +50 -0
  80. package/testaro/attVal.d.ts +3 -0
  81. package/testaro/attVal.js +21 -21
  82. package/testaro/attVal.ts +52 -0
  83. package/testaro/autocomplete.d.ts +3 -0
  84. package/testaro/autocomplete.js +60 -75
  85. package/testaro/autocomplete.ts +101 -0
  86. package/testaro/bulk.d.ts +13 -0
  87. package/testaro/bulk.js +33 -33
  88. package/testaro/bulk.ts +55 -0
  89. package/testaro/buttonMenu.d.ts +9 -0
  90. package/testaro/buttonMenu.js +318 -319
  91. package/testaro/buttonMenu.ts +391 -0
  92. package/testaro/captionLoc.d.ts +3 -0
  93. package/testaro/captionLoc.js +18 -20
  94. package/testaro/captionLoc.ts +40 -0
  95. package/testaro/datalistRef.d.ts +3 -0
  96. package/testaro/datalistRef.js +34 -36
  97. package/testaro/datalistRef.ts +55 -0
  98. package/testaro/distortion.d.ts +3 -0
  99. package/testaro/distortion.js +58 -26
  100. package/testaro/distortion.ts +81 -0
  101. package/testaro/docType.d.ts +15 -0
  102. package/testaro/docType.js +26 -25
  103. package/testaro/docType.ts +45 -0
  104. package/testaro/dupAtt.d.ts +16 -0
  105. package/testaro/dupAtt.js +114 -104
  106. package/testaro/dupAtt.ts +144 -0
  107. package/testaro/elements.d.ts +6 -0
  108. package/testaro/elements.js +153 -153
  109. package/testaro/elements.ts +215 -0
  110. package/testaro/embAc.d.ts +3 -0
  111. package/testaro/embAc.js +20 -20
  112. package/testaro/embAc.ts +40 -0
  113. package/testaro/focAll.d.ts +13 -0
  114. package/testaro/focAll.js +186 -191
  115. package/testaro/focAll.ts +217 -0
  116. package/testaro/focAndOp.d.ts +3 -0
  117. package/testaro/focAndOp.js +101 -104
  118. package/testaro/focAndOp.ts +128 -0
  119. package/testaro/focInd.d.ts +3 -0
  120. package/testaro/focInd.js +65 -66
  121. package/testaro/focInd.ts +96 -0
  122. package/testaro/focVis.d.ts +3 -0
  123. package/testaro/focVis.js +29 -30
  124. package/testaro/focVis.ts +52 -0
  125. package/testaro/headEl.d.ts +10 -0
  126. package/testaro/headEl.js +62 -62
  127. package/testaro/headEl.ts +82 -0
  128. package/testaro/headingAmb.d.ts +3 -0
  129. package/testaro/headingAmb.js +55 -63
  130. package/testaro/headingAmb.ts +72 -0
  131. package/testaro/hovInd.d.ts +12 -0
  132. package/testaro/hovInd.js +164 -130
  133. package/testaro/hovInd.ts +199 -0
  134. package/testaro/hover.d.ts +3 -0
  135. package/testaro/hover.js +156 -126
  136. package/testaro/hover.ts +154 -0
  137. package/testaro/hr.d.ts +3 -0
  138. package/testaro/hr.js +15 -17
  139. package/testaro/hr.ts +36 -0
  140. package/testaro/imageLink.d.ts +3 -0
  141. package/testaro/imageLink.js +18 -20
  142. package/testaro/imageLink.ts +42 -0
  143. package/testaro/labClash.d.ts +3 -0
  144. package/testaro/labClash.js +32 -33
  145. package/testaro/labClash.ts +54 -0
  146. package/testaro/legendLoc.d.ts +3 -0
  147. package/testaro/legendLoc.js +18 -20
  148. package/testaro/legendLoc.ts +42 -0
  149. package/testaro/lineHeight.d.ts +3 -0
  150. package/testaro/lineHeight.js +44 -48
  151. package/testaro/lineHeight.ts +70 -0
  152. package/testaro/linkAmb.d.ts +7 -0
  153. package/testaro/linkAmb.js +80 -80
  154. package/testaro/linkAmb.ts +105 -0
  155. package/testaro/linkExt.d.ts +3 -0
  156. package/testaro/linkExt.js +14 -16
  157. package/testaro/linkExt.ts +35 -0
  158. package/testaro/linkOldAtt.d.ts +3 -0
  159. package/testaro/linkOldAtt.js +26 -28
  160. package/testaro/linkOldAtt.ts +48 -0
  161. package/testaro/linkTo.d.ts +3 -0
  162. package/testaro/linkTo.js +23 -22
  163. package/testaro/linkTo.ts +43 -0
  164. package/testaro/linkUl.d.ts +3 -0
  165. package/testaro/linkUl.js +31 -34
  166. package/testaro/linkUl.ts +54 -0
  167. package/testaro/miniText.d.ts +3 -0
  168. package/testaro/miniText.js +42 -44
  169. package/testaro/miniText.ts +68 -0
  170. package/testaro/motion.d.ts +10 -0
  171. package/testaro/motion.js +93 -96
  172. package/testaro/motion.ts +125 -0
  173. package/testaro/nonTable.d.ts +3 -0
  174. package/testaro/nonTable.js +40 -45
  175. package/testaro/nonTable.ts +66 -0
  176. package/testaro/optRoleSel.d.ts +3 -0
  177. package/testaro/optRoleSel.js +17 -19
  178. package/testaro/optRoleSel.ts +41 -0
  179. package/testaro/phOnly.d.ts +3 -0
  180. package/testaro/phOnly.js +19 -21
  181. package/testaro/phOnly.ts +43 -0
  182. package/testaro/pseudoP.d.ts +3 -0
  183. package/testaro/pseudoP.js +37 -38
  184. package/testaro/pseudoP.ts +59 -0
  185. package/testaro/radioSet.d.ts +3 -0
  186. package/testaro/radioSet.js +58 -59
  187. package/testaro/radioSet.ts +79 -0
  188. package/testaro/registry.d.ts +63 -0
  189. package/testaro/registry.js +67 -0
  190. package/testaro/registry.ts +141 -0
  191. package/testaro/role.d.ts +3 -0
  192. package/testaro/role.js +28 -29
  193. package/testaro/role.ts +53 -0
  194. package/testaro/secHeading.d.ts +3 -0
  195. package/testaro/secHeading.js +31 -33
  196. package/testaro/secHeading.ts +53 -0
  197. package/testaro/styleDiff.d.ts +25 -0
  198. package/testaro/styleDiff.js +249 -252
  199. package/testaro/styleDiff.ts +303 -0
  200. package/testaro/tabNav.d.ts +33 -0
  201. package/testaro/tabNav.js +273 -342
  202. package/testaro/tabNav.ts +454 -0
  203. package/testaro/targetsNear.d.ts +9 -0
  204. package/testaro/targetsNear.js +131 -132
  205. package/testaro/targetsNear.ts +160 -0
  206. package/testaro/textNodes.d.ts +6 -0
  207. package/testaro/textNodes.js +139 -135
  208. package/testaro/textNodes.ts +185 -0
  209. package/testaro/textSem.d.ts +3 -0
  210. package/testaro/textSem.js +26 -28
  211. package/testaro/textSem.ts +47 -0
  212. package/testaro/title.d.ts +9 -0
  213. package/testaro/title.js +16 -13
  214. package/testaro/title.ts +31 -0
  215. package/testaro/titledEl.d.ts +3 -0
  216. package/testaro/titledEl.js +16 -18
  217. package/testaro/titledEl.ts +38 -0
  218. package/testaro/zIndex.d.ts +3 -0
  219. package/testaro/zIndex.js +20 -22
  220. package/testaro/zIndex.ts +42 -0
  221. package/tests/alfa.d.ts +45 -0
  222. package/tests/alfa.js +139 -141
  223. package/tests/alfa.ts +214 -0
  224. package/tests/aslint.d.ts +33 -0
  225. package/tests/aslint.js +273 -249
  226. package/tests/aslint.ts +301 -0
  227. package/tests/axe.d.ts +27 -0
  228. package/tests/axe.js +200 -200
  229. package/tests/axe.ts +277 -0
  230. package/tests/ed11y.d.ts +28 -0
  231. package/tests/ed11y.js +142 -99
  232. package/tests/ed11y.ts +178 -0
  233. package/tests/htmlcs.d.ts +21 -0
  234. package/tests/htmlcs.js +175 -140
  235. package/tests/htmlcs.ts +181 -0
  236. package/tests/ibm.d.ts +52 -0
  237. package/tests/ibm.js +165 -166
  238. package/tests/ibm.ts +251 -0
  239. package/tests/nuVal.d.ts +13 -0
  240. package/tests/nuVal.js +107 -112
  241. package/tests/nuVal.ts +145 -0
  242. package/tests/nuVnu.d.ts +15 -0
  243. package/tests/nuVnu.js +142 -111
  244. package/tests/nuVnu.ts +144 -0
  245. package/tests/pour.d.ts +31 -0
  246. package/tests/pour.js +242 -0
  247. package/tests/pour.ts +273 -0
  248. package/tests/qualWeb.d.ts +39 -0
  249. package/tests/qualWeb.js +303 -272
  250. package/tests/qualWeb.ts +415 -0
  251. package/tests/surea11y.d.ts +33 -0
  252. package/tests/surea11y.js +288 -0
  253. package/tests/surea11y.ts +334 -0
  254. package/tests/testaro.d.ts +25 -0
  255. package/tests/testaro.js +746 -652
  256. package/tests/testaro.ts +862 -0
  257. package/tests/wave.d.ts +46 -0
  258. package/tests/wave.js +167 -177
  259. package/tests/wave.ts +252 -0
  260. package/tsconfig.json +17 -0
  261. package/types.d.ts +243 -0
  262. package/types.js +10 -0
  263. package/types.ts +376 -0
  264. package/validation/act/README.md +46 -0
  265. package/validation/act/capture.js +424 -0
  266. package/validation/act/chromium-issue-draft.md +66 -0
  267. package/validation/act/fp-triage-2026-08-22.md +86 -0
  268. package/validation/act/isolation-notes.md +159 -0
  269. package/validation/act/playwright-issue-draft.md +73 -0
  270. package/validation/act/propose-mappings.js +0 -0
  271. package/validation/act/repro-cdp-raw.js +105 -0
  272. package/validation/act/repro-metarefresh.js +65 -0
  273. package/validation/act/score.js +184 -0
  274. package/validation/act/stage3a-stress-report.md +69 -0
  275. package/validation/act/stage3b-mapping-proposals.md +94 -0
  276. package/validation/act/stage3b-triage-draft.md +138 -0
  277. package/validation/act/surea11y-track-a-2026-09-01.md +50 -0
  278. package/validation/executors/netWatch.js +180 -90
  279. package/validation/executors/test.js +17 -2
  280. package/validation/executors/tests.js +118 -10
  281. package/validation/jobs/reports/raw/260901T1000-surea11y-validation.json +964 -0
  282. package/validation/jobs/todo/240101T1200-simple-example.json +14 -6
  283. package/validation/jobs/todo/240101T1300-shoot-example.json +2 -1
  284. package/validation/jobs/todo/260821T1900-pour-validation.json +45 -0
  285. package/validation/jobs/todo/260901T1000-surea11y-validation.json +45 -0
  286. package/validation/knownFailures.json +1 -0
  287. package/validation/tests/jobProperties/adbID.json +27 -2
  288. package/validation/tests/jobProperties/{focOp.json → allCapStyle.json} +56 -53
  289. package/validation/tests/jobProperties/allCaps.json +25 -0
  290. package/validation/tests/jobProperties/allHidden.json +133 -13
  291. package/validation/tests/jobProperties/allSlanted.json +3 -3
  292. package/validation/tests/jobProperties/altScheme.json +23 -8
  293. package/validation/tests/jobProperties/attVal.json +57 -57
  294. package/validation/tests/jobProperties/autocomplete.json +11 -1
  295. package/validation/tests/jobProperties/bulk.json +6 -1
  296. package/validation/tests/jobProperties/buttonMenu.json +63 -42
  297. package/validation/tests/jobProperties/captionLoc.json +1 -6
  298. package/validation/tests/jobProperties/checkpoint-browser.json +401 -0
  299. package/validation/tests/jobProperties/checkpoint-page.json +397 -0
  300. package/validation/tests/jobProperties/checkpoint.json +395 -0
  301. package/validation/tests/jobProperties/datalistRef.json +20 -5
  302. package/validation/tests/jobProperties/distortion.json +28 -3
  303. package/validation/tests/jobProperties/docType.json +2 -2
  304. package/validation/tests/jobProperties/dupAtt.json +48 -33
  305. package/validation/tests/jobProperties/elements.json +28 -28
  306. package/validation/tests/jobProperties/embAc.json +36 -31
  307. package/validation/tests/jobProperties/focAndOp.json +284 -0
  308. package/validation/tests/jobProperties/focInd.json +34 -34
  309. package/validation/tests/jobProperties/focVis.json +1 -1
  310. package/validation/tests/jobProperties/hover.json +39 -37
  311. package/validation/tests/jobProperties/hr.json +13 -3
  312. package/validation/tests/jobProperties/imageLink.json +0 -5
  313. package/validation/tests/jobProperties/labClash.json +68 -33
  314. package/validation/tests/jobProperties/legendLoc.json +1 -6
  315. package/validation/tests/jobProperties/lineHeight.json +2 -2
  316. package/validation/tests/jobProperties/linkAmb.json +13 -18
  317. package/validation/tests/jobProperties/linkExt.json +1 -1
  318. package/validation/tests/jobProperties/linkOldAtt.json +12 -2
  319. package/validation/tests/jobProperties/linkTo.json +1 -1
  320. package/validation/tests/jobProperties/linkUl.json +65 -65
  321. package/validation/tests/jobProperties/miniText.json +7 -2
  322. package/validation/tests/jobProperties/motion.json +4 -50
  323. package/validation/tests/jobProperties/nonTable.json +48 -3
  324. package/validation/tests/jobProperties/optRoleSel.json +12 -2
  325. package/validation/tests/jobProperties/phOnly.json +6 -16
  326. package/validation/tests/jobProperties/pseudoP.json +17 -2
  327. package/validation/tests/jobProperties/radioSet.json +30 -30
  328. package/validation/tests/jobProperties/role.json +25 -5
  329. package/validation/tests/jobProperties/secHeading.json +26 -21
  330. package/validation/tests/jobProperties/styleDiff.json +35 -5
  331. package/validation/tests/jobProperties/tabNav.json +3 -1
  332. package/validation/tests/jobProperties/targetsNear.json +237 -0
  333. package/validation/tests/jobProperties/textNodes.json +29 -29
  334. package/validation/tests/jobProperties/textSem.json +26 -1
  335. package/validation/tests/jobProperties/title.json +12 -2
  336. package/validation/tests/jobProperties/titledEl.json +39 -9
  337. package/validation/tests/jobProperties/userPath.json +317 -0
  338. package/validation/tests/jobProperties/zIndex.json +40 -35
  339. package/validation/tests/targets/allCapStyle/index.html +26 -0
  340. package/validation/tests/targets/checkpoint/index.html +38 -0
  341. package/validation/tests/targets/datalistRef/index.html +1 -1
  342. package/validation/tests/targets/focAndOp/bad.html +29 -0
  343. package/validation/tests/targets/{focOp → focAndOp}/good.html +3 -1
  344. package/validation/tests/targets/focInd/bad.html +2 -1
  345. package/validation/tests/targets/headEl/index.html +10 -1
  346. package/validation/tests/targets/{targetSmall → targetsNear}/index.html +15 -1
  347. package/validation/tests/targets/userPath/index.html +42 -0
  348. package/validation/validateTest.js +62 -10
  349. package/.claude/settings.local.json +0 -11
  350. package/.eslintrc.json +0 -41
  351. package/htmlcs/.eslintrc.json +0 -67
  352. package/memory/MEMORY.md +0 -3
  353. package/memory/project_validation_pause.md +0 -10
  354. package/validation/tests/jobProperties/linkTitle.json +0 -127
  355. package/validation/tests/jobProperties/opFoc.json +0 -164
  356. package/validation/tests/jobProperties/targetSmall.json +0 -152
  357. package/validation/tests/jobProperties/targetTiny.json +0 -142
  358. package/validation/tests/targets/focOp/bad.html +0 -25
  359. package/validation/tests/targets/linkTitle/index.html +0 -24
  360. package/validation/tests/targets/opFoc/bad.html +0 -26
  361. package/validation/tests/targets/opFoc/good.html +0 -23
@@ -0,0 +1,199 @@
1
+ # Checkpoints: scanning across a serial action flow
2
+
3
+ **Status:** Phases 0, 1, and 1b shipped in Testaro 78.2 (2026-09-02); Phases 2 (`report.flow`),
4
+ 3 (`scope: 'changed'`), and 4 (incremental pruning, selector acts, the user-path converter)
5
+ shipped in 78.3 (2026-09-02). The interaction modalities and the yra-monitor ingestion changes
6
+ remain follow-ups.
7
+
8
+ ## Why
9
+
10
+ A job is a serial list of acts: launch a browser, interact with the page, test it. Until 78.2,
11
+ the interaction acts had no effect on any test: every `test` act ran in a child process that
12
+ launched its own browser and navigated to the job target, and the element catalog was built
13
+ once, before any act, for that same page. A job of the form launch → click → test therefore
14
+ tested the page as first loaded, and any test after a `url` act resolved its elements against
15
+ the wrong page's catalog.
16
+
17
+ The maintainer's goals: keep the serial action flow; keep or extend a running list of found
18
+ issues as the flow progresses; use explicit triggers for re-scanning rather than re-running
19
+ every tool after every action; and re-scan either the whole page or only the parts that
20
+ changed, as the case requires.
21
+
22
+ ## The model
23
+
24
+ A **checkpoint** is a named page state reached by the flow, snapshotted and tested:
25
+
26
+ - **Checkpoint 0** is the job target as launched. `getCatalog` creates it before any act, as
27
+ it always built the catalog, and records it as `report.checkpoints[0]` (named `start`).
28
+ - A **`checkpoint` act** (`{type: 'checkpoint', which: '<name>'}`) creates the next one from
29
+ the live page in the parent process: catalog entries for the current state, page image(s)
30
+ if the job asks for them, the URL and title, an ARIA snapshot, and a DOM digest.
31
+ - A **`test` act belongs to the most recent checkpoint** (`act.checkpoint`), and every
32
+ standard instance it reports carries `checkpoint`.
33
+
34
+ Checkpoint mode is opt-in: it is on only when the job contains a `checkpoint` act. Legacy
35
+ jobs (no such act) run as before and produce today's report plus the `checkpoint: 0` fields;
36
+ if interaction acts precede a test act in a legacy job, `jobData.warnings` says that the act
37
+ tests checkpoint 0. In checkpoint mode, interaction acts followed by a test act without an
38
+ intervening checkpoint produce an implicit checkpoint (named `act<N>`) and a warning; the
39
+ warning never fails the job.
40
+
41
+ ### Navigation and interaction checkpoints
42
+
43
+ A checkpoint is a **navigation checkpoint** when the page was reached by navigation alone
44
+ (a `launch` or `url` act) and no interaction act ran since; it is an **interaction
45
+ checkpoint** when acts on a page (button, link, text, press, reveal, …) reached it. The
46
+ distinction decides how a test act's browser reaches the state:
47
+
48
+ - Navigation checkpoint: the child navigates to the checkpoint's URL. Every tool runs.
49
+ - Interaction checkpoint: the child navigates to the URL the interaction started from
50
+ (`launchURL`) and **replays** the recorded interaction acts (`replay`, act indexes) before
51
+ running the tool. The replay happens inside `launchOnce` after navigation and before any
52
+ `data-xpath` stamping, so elements the acts reveal or create are stamped like the rest,
53
+ and every per-rule relaunch of a contaminating testaro rule replays automatically.
54
+
55
+ Replay is a re-enactment, not a restore. Server state, sessions, timers and randomness can
56
+ make the replayed DOM differ from the snapshot. Each test act records
57
+ `data.replay = {checkpoint, acts, elapsedMs, fidelity}` where `fidelity` is `exact` when
58
+ the replayed DOM's digest equals the checkpoint's, else `divergent`. A replay failure
59
+ prevents the test act with `checkpoint replay failed at act N (…)` and is not retried.
60
+
61
+ ### What each tool can observe
62
+
63
+ `procs/job.js` `toolInputs` declares what a tool tests:
64
+
65
+ | input | tools | interaction checkpoints |
66
+ | --- | --- | --- |
67
+ | `page` | alfa, aslint, axe, ed11y, htmlcs, ibm, pour, surea11y, testaro | yes (replayed page) |
68
+ | `html` | qualWeb, nuVal, nuVnu (they read the live page's HTML) | yes (replayed page) |
69
+ | `url` | wave; nuVal and nuVnu with `withSource: true` | no: prevented with a stated reason |
70
+
71
+ ### Report shape
72
+
73
+ - `report.checkpoints[k]`: `{index, name, implicit, actIndex, launchActIndex, launchURL,
74
+ replay, interaction: {modality}, kind: 'navigation' | 'interaction', url, title,
75
+ imageIndexes, catalogRange, elementCount, ariaSnapshot, domDigest, elapsedMs, testActs}`.
76
+ - `report.catalog` stays one flat map. Entries carry `checkpoint`; indexes come from a
77
+ monotonic job-time counter (`report.catalogNextIndex`), so entries of different checkpoints
78
+ never collide, and the job-time `report.pathIDs` is scoped per checkpoint so the same XPath
79
+ in two states maps to two entries. Pruning keeps only cited entries, as before.
80
+ - `report.images`: `images[0]` (and `[1]` at `imageScale > 1`) keep meaning checkpoint 0;
81
+ later checkpoints' images are indexed by `checkpoints[k].imageIndexes`. The testaro `motion`
82
+ rule compares against its checkpoint's image.
83
+ - `jobData.catalogData.checkpoints[k]`: element and entry counts per checkpoint.
84
+ - `report.flow` (two or more checkpoints): `{checkpoints: [{index, name, kind, url, actIndex,
85
+ testActs, tools, issueCount}], deltas: [{from, to, tools, notObserved, added, persisted,
86
+ removed, notRetested, structure, aria}]}`; each issue is `{tool, ruleID, pathID, startTag,
87
+ what, ordinalSeverity, outcome, count, actIndexes}`.
88
+ - Test acts: `scope` (`page` default or `changed`) and `data.scope = {requested, applied,
89
+ reason, roots, pathIDs, commonRoot, localRules?, pageRules?}`. Job-time only, deleted by
90
+ `pruneCatalog`: `report.scope`, `report.ruleScopeRoots`.
91
+ - Progress events: `checkpointStart` and `checkpointEnd` (with `kind` and `elapsedMs`);
92
+ `actEnd` carries `checkpoint`.
93
+
94
+ ### Interaction modality (planned)
95
+
96
+ The act executors live in `procs/actDo.js` and take an `interaction` option
97
+ (`{modality: 'efficient'}` today), recorded on each checkpoint so replay uses the same
98
+ modality. Low-level events (`mousedown`/`mouseup`), emulated human mouse travel and key
99
+ pauses, and pass-to-pass comparisons between modalities are designed to slot in there.
100
+
101
+ ## Phases
102
+
103
+ - **Phase 0 (shipped):** act-loop repairs (`next` acts, failed launches, the `url` and
104
+ `state` acts), `procs/actDo.js`, `procs/xPathScript.js`, `catalogPage`, checkpoint 0.
105
+ - **Phase 1 (shipped):** the `checkpoint` act, `procs/checkpoint.js`, replay in the
106
+ launcher, implicit checkpoints, tool gating, the `validation/tests/jobProperties/checkpoint`
107
+ validator with report-level expectations.
108
+ - **Phase 1b (shipped):** the `isolation` job property (or the `ISOLATION` environment
109
+ default): `process` (the default: a child process and browser per test act), `browser`
110
+ (one browser shared by the job's launches, a fresh context per test act, replay), `page`
111
+ (tools run in sequence in the job's process on the live checkpoint page, no replay;
112
+ contaminating testaro rules still get a fresh context with replay). `procs/testAct.js`
113
+ performs a test act for both the child process and the in-process levels; the launcher
114
+ keeps the shared browser and prepares a live page (XPath script or attributes, accessible
115
+ names) without changing its DOM. Recorded in `jobData.isolation`. Only `process` can kill a
116
+ tool that overruns its time limit; the others report the act as timed out and continue.
117
+ - **Phase 2 (shipped):** `report.flow` (`procs/flow.js`, `getFlow(report)`), added at job end
118
+ before pruning when the job has two or more checkpoints, and usable standalone on a stored
119
+ report (the structure diff then covers cited elements only). `flow.checkpoints[k]`
120
+ summarizes each checkpoint; `flow.deltas[k - 1]` compares checkpoints `k - 1` and `k`:
121
+ `added`, `persisted`, and `removed` issues with identity `tool | ruleID | pathID | startTag`
122
+ (box and text excluded, since layout and copy shift without the defect changing; a summary
123
+ instance has an empty element key), compared only for the `tools` that observed both
124
+ checkpoints with a non-prevented act (`notObserved` lists the rest); `structure`, the
125
+ catalog diff (`procs/scope.js`, `getStructureDiff`): `added`, `removed`, and `changed`
126
+ (start tag) XPaths, `textChanged` XPaths, and `roots`, the outermost changed elements (the
127
+ nearest surviving ancestor stands for a removed element; a text change counts only where no
128
+ descendant changed, because the catalog text of an element includes its descendants'); and
129
+ `aria`, a line diff of the ARIA snapshots (`diff`, the jsdiff library yra-monitor also uses;
130
+ at most 500 changed lines recorded, `truncated` otherwise).
131
+ - **Phase 3 (shipped):** `scope: 'page' | 'changed'` on test acts (`actSpecs.js`; a job with a
132
+ changed-scope act must have a checkpoint act). Before such an act the acts loop calls
133
+ `getChangedRoots(report, k)`, which converts the structure roots of the diff between
134
+ checkpoints `k - 1` and `k` to CSS selectors (`/html/body/main[1]/div[2]` becomes
135
+ `html > body > main:nth-of-type(1) > div:nth-of-type(2)`, exact because `getXPath`
136
+ subscripts among same-tag siblings, which is what `nth-of-type` counts) and gives them to
137
+ the tool as the job-time `report.scope` when the tool is in `toolScopes` (`procs/job.js`:
138
+ axe, surea11y, testaro). The act records `data.scope`; when the act cannot be scoped (no
139
+ previous checkpoint, no change, more than `maxRoots` = 50 roots, an XPath with no selector,
140
+ a tool without a root option) it tests the whole page with `applied: false` and a reason,
141
+ and `jobData.warnings` says so. axe takes the roots as its `include` context; surea11y takes
142
+ the nearest common ancestor of the roots (`commonRoot`), since its context is one selector;
143
+ the testaro tool sets `report.ruleScopeRoots` per rule to the roots for a rule with
144
+ `local: true` in `allRules` and to null otherwise, and `doTest` keeps only candidates inside
145
+ a root (`getBasicResult` rules, hover and role, are not local). The conservative first-pass
146
+ classification marks as not local: allHidden, bulk, docType, dupAtt, headEl, headingAmb,
147
+ secHeading, linkAmb, labClash, radioSet, styleDiff, targetsNear, title, motion, role, hover,
148
+ hovInd, focAll, focAndOp, focInd, focVis, tabNav, buttonMenu, elements, textNodes. Whether a
149
+ partial scan is valid is not automated; the author chooses per act, and the rule metadata
150
+ protects the choice. Limits: the diff is by XPath, so inserting one sibling shifts later
151
+ same-tag indexes and the changed set can grow to the container (correct but coarse); a
152
+ changed-scope act finds only defects inside the roots, so `flow` unions the findings of every
153
+ act at a checkpoint rather than trusting one act.
154
+ - **Phase 4 (shipped):** three parts.
155
+ - *Incremental pruning.* When checkpoint `k + 1` is created, `makeCheckpoint` first records
156
+ the structure diff between `k` and `k + 1` on the new checkpoint (job-time
157
+ `checkpoints[k + 1].structure`, which `getChangedRoots` and `getFlow` read instead of
158
+ recomputing, and which the acts loop deletes once `flow` holds it), then calls
159
+ `pruneCheckpoint(report, k)` (`procs/catalog.ts`), which deletes checkpoint `k`'s entries
160
+ that no test act of checkpoint `k` cites and drops its job-time XPath map. No later test
161
+ act can cite checkpoint `k`, since a test act belongs to the latest checkpoint, so the
162
+ temporary report that every test act reads stays bounded by one checkpoint's page plus
163
+ the cited entries of earlier ones. `catalogData.elementCount` is therefore the count of
164
+ entries ever made (`catalogNextIndex`), not the count at pruning time. A single-checkpoint
165
+ job is unaffected.
166
+ - *Selector acts.* The move acts (`button`, `checkbox`, `link`, `radio`, `search`, `select`,
167
+ `text`) accept a `selector` property, any Playwright selector (CSS, `text=`, `role=`,
168
+ `label=`), which replaces the act type's element selector and text matching; `index`
169
+ still picks among matches. A `text` act with `clear: true` replaces the input's value
170
+ instead of appending to it, and a `select` act whose `what` matches no option text falls
171
+ back to Playwright's value-or-label matching. Replay uses the same executors, so
172
+ selector acts replay like the rest.
173
+ - *User-path converter.* `procs/userPath.js` `getUserPathActs({startUrl, actions,
174
+ testActs, scanAtCheckpoints, what})` maps a recorded user path of the shape yra-monitor
175
+ stores (actions `click`, `fill`, `select`, `navigate`, `wait`, `checkpoint`, with
176
+ Playwright selectors) to a job's acts: launch, then one act per action (`button`, `text`
177
+ with `clear`, `select`, `url`, `state: idle` for a pause), and for each `checkpoint`
178
+ action a `checkpoint` act (named from its label, made unique) followed by a copy of each
179
+ requested test act, with `scope: 'changed'` downgraded to `page` at the first checkpoint.
180
+ The `userPath` validator runs the converter's output for a fill, select, checkpoint,
181
+ click, wait, checkpoint, navigate, checkpoint path. yra-monitor's user-path executor still
182
+ replays actions itself and runs a stub axe scan at checkpoints; replacing that with a
183
+ Testaro job built by this converter waits on the ingestion changes below.
184
+ - `flow` and scoped acts: a tool whose acts at a checkpoint were all scoped to changed
185
+ subtrees observed nothing outside them, so an earlier issue of that tool whose element
186
+ lies outside the roots is listed in the delta as `notRetested`, not `removed`.
187
+
188
+ ## Relationship to yra-monitor
189
+
190
+ yra-monitor's DOM-difference feature captures outerHTML, a screenshot, element positions
191
+ and the ARIA snapshot per URL per scan and diffs them across scans. Checkpoints capture the
192
+ same artifacts per page state inside one job. The cross-scan history and comparison UI stay
193
+ downstream; a later consolidation can store Testaro's checkpoint-0 artifacts instead of
194
+ launching a separate capture browser.
195
+
196
+ Until yra-monitor carries `checkpoint` and the checkpoint URL on stored rows, adds a
197
+ checkpoint dimension to its unique-issue key and auto-resolve scoping, and stores one
198
+ screenshot per checkpoint, multi-checkpoint reports must not be fed through its scan
199
+ ingestion path.
@@ -0,0 +1,165 @@
1
+ # Certainty in the standard result: the `outcome` field
2
+
3
+ **Status:** Phase A shipped in Testaro 78.1.0 (2026-09-01). Phases B–D are recommendations for
4
+ the Testilo fork and yra-monitor, and for a later Testaro major version.
5
+ **Companion:** yra-monitor `docs/plans/engine-candidacy-pipeline.md` (Stage-1 instance policy,
6
+ Stage-3b taxonomy mapping).
7
+
8
+ ## Why
9
+
10
+ Rule engines report two different things about a finding: how bad it is (impact) and whether
11
+ they are sure it is a defect (certainty). Every Testaro adapter used to fold certainty into
12
+ `ordinalSeverity`, and each did it differently:
13
+
14
+ | tool | uncertain native value | ordinal | certain native value | ordinal |
15
+ | --- | --- | --- | --- | --- |
16
+ | axe, pour, surea11y | incomplete / cantTell | 0–1 | violation / fail | 2–3 |
17
+ | htmlcs | Warning | 0 | Error | 2 |
18
+ | ibm | recommendation | 0 | violation and potential violation | 2 |
19
+ | alfa | cantTell (rule ID rewritten) | 0 | failed | 2 |
20
+ | aslint | warning | 1 | error | 2 |
21
+ | qualWeb act-rules | warning | 1 | failed | 3 |
22
+ | qualWeb best-practices | warning | 0 | failed | 1 |
23
+ | wave | alert | 0 | error, contrast | 3 |
24
+ | ed11y | dismissable warning | 0 | error | 2 |
25
+ | nuVal, nuVnu | info | 0 | error | 3 |
26
+
27
+ So severity 0 meant "definitely minor" for some tools and "possibly critical" for others, and
28
+ nothing downstream could recover certainty except by the heuristic "ordinal 2 or above means
29
+ asserted". The ACT validation harness had to read native results to score sensitivity and
30
+ specificity by certainty band. The Testilo issue classification (tic) grew 57 `*Risk` issues to
31
+ hold uncertain findings at weight 1, and its scorer counted an axe `incomplete` contrast finding
32
+ exactly like an asserted violation because it maps rule IDs only.
33
+
34
+ The two new candidate engines make the loss visible: pour separates `violations`, `incomplete`,
35
+ and `manualReview`; surea11y grades per occurrence (`fail` / `cantTell`) and publishes six
36
+ machine-readable `uncertainty` codes with a `needed` sentence saying what a reviewer must
37
+ determine.
38
+
39
+ ## The model
40
+
41
+ Four dimensions, kept separate:
42
+
43
+ | dimension | meaning | where it lives |
44
+ | --- | --- | --- |
45
+ | outcome (per instance) | did the engine assert a failure, or could it not tell | standard instance, set by the adapter |
46
+ | rule certainty (per rule) | is the check deterministic, or a heuristic that can only say "suspicious" | tic tool-rule entry, as an override |
47
+ | impact (per instance) | how bad for a user | `ordinalSeverity`, once it means impact only |
48
+ | issue weight (per issue) | severity of the defect class | tic `weight` |
49
+
50
+ Certainty exists at two levels because engines both under-assert (axe `incomplete`) and
51
+ over-assert (a heuristic reported as an error). The instance level is Testaro's job; the rule
52
+ level is the taxonomy's.
53
+
54
+ ## Phase A: Testaro 78.1.0 (shipped)
55
+
56
+ Every standard instance now has:
57
+
58
+ ```js
59
+ {
60
+ ruleID, what, ordinalSeverity, count, catalogIndex, // unchanged
61
+ outcome: 'failed' | 'cantTell', // required; ACT Rules Format vocabulary
62
+ uncertainty: 'not-computable' | 'judgement-required' | 'runtime-dependent'
63
+ | 'spec-only' | 'equivalence-unknown' | 'out-of-scope', // optional, cantTell only
64
+ needed: '<what a reviewer must determine>' // optional, cantTell only, ≤ 300 chars
65
+ }
66
+ ```
67
+
68
+ and `standardResult.outcomeTotals = {failed, cantTell}` (count-weighted).
69
+
70
+ - `procs/standard.ts` is the one place the shape is built (`getStandardResult`, `getInstance`,
71
+ `addInstance`, `pushInstance`). Adapters that compute `totals` from native counts (ibm, htmlcs,
72
+ ed11y, wave, testaro) use `pushInstance`; the rest use `addInstance`.
73
+ - `ordinalSeverity` and `totals` are unchanged for every tool. The golden diff on the pour and
74
+ surea11y validation jobs is identical after stripping the new fields.
75
+ - Per-tool mapping: axe/pour/surea11y violations → `failed`, incomplete → `cantTell`; htmlcs
76
+ Warning → `cantTell`; ibm potential violation, potential recommendation, and manual →
77
+ `cantTell`; alfa `cantTell` → `cantTell` (the adapter no longer rewrites the rule ID to
78
+ `cantTell` / `cantTellTextContrast` or prefixes `what`; r66 and r69 carry
79
+ `judgement-required`); aslint warning, qualWeb warning, wave alert, nuVal/nuVnu info →
80
+ `cantTell`; ed11y dismissable warning → `cantTell` with `judgement-required`.
81
+ - surea11y forwards `uncertainty.code` and `uncertainty.needed` (occurrence first, then check).
82
+ Its `confidence` value is dropped as non-portable.
83
+ - Testaro's own rules default to `failed`. An `allRules` entry may set `outcome` and
84
+ `uncertainty` (`allCaps` is `cantTell` / `judgement-required`, consistent with the earlier
85
+ ruling that AI confidence must not be encoded in `ordinalSeverity`). A violation description
86
+ may carry a prefix: `2:` severity only, `2?:` severity plus `cantTell`, `?:` `cantTell` at the
87
+ rule's default severity. `getBasicResult` accepts `outcome`, `uncertainty`, and `needed` on
88
+ each violation.
89
+ - `validation/act/capture.js` records `outcomeTotals` and per-instance `outcome` and
90
+ `uncertainty`, and marks a row as an error if any instance lacks a valid outcome.
91
+
92
+ Why `outcome` and not `certainty`: the ACT Rules Format and EARL already use `failed` /
93
+ `cantTell`, the ACT testcase feed the harness scores against uses `failed`, and a closed
94
+ two-value enum keeps consumer logic total. A graded `certainty` would invite scales no two
95
+ engines share and collide with surea11y's separate `confidence`.
96
+
97
+ Why `needed` is its own field: `what` is an identity key downstream (unique-issue rows, tsp
98
+ description sampling). Appending guidance to it would fracture identity across pages.
99
+
100
+ ## Phase B: Testilo fork (recommended)
101
+
102
+ Tool-rule entries in the tic gain optional properties; issues gain two:
103
+
104
+ ```js
105
+ tools: { htmlcs: { 'W-AAA.1_4_3.G18.BgImage': {
106
+ variable: false, quality: 1, what: '…',
107
+ outcome: 'cantTell', // override; 'cantTell' is the common case
108
+ uncertainty: 'not-computable', // rule-level default; the instance value wins
109
+ review: '…', // fallback "what to confirm" when the engine gives no `needed`
110
+ ignore: 'invalid' // replaces prose [invalid]/[irrelevant]/[duplicative]/[unreliable]
111
+ }}}
112
+ issue: { how: '<one imperative fix sentence>', group: 'landmarkNotTop' }
113
+ ```
114
+
115
+ - Precedence: `rule.outcome` > `instance.outcome` > legacy inference > `failed`. Legacy
116
+ inference (reports without `outcome`) is per tool: axe, pour, surea11y, htmlcs, ibm, wave,
117
+ qualWeb → `ordinalSeverity >= 2 ? failed : cantTell`; alfa → rule ID starts with `cantTell`;
118
+ testaro, ed11y, aslint, nuVal, nuVnu → `failed`.
119
+ - `[speculative]` becomes `outcome: 'cantTell'`; the other bracket tags become `ignore`. The
120
+ `ignorable` pseudo-issue dissolves into per-rule `ignore`. A loader assertion rejects any
121
+ `what` still carrying a bracket tag.
122
+ - Consolidate the 57 `*Risk` issues: merge into the base issue when one exists with the same fix
123
+ (7 name twins plus about 11 semantic twins such as contrastRisk → contrastAA/AAA by level);
124
+ rename (drop `Risk`) when the issue is the sole home of a defect class with a concrete fix
125
+ (pseudoHeadingRisk → pseudoHeading, which already holds axe `p-as-heading` and takes pour's
126
+ at `failed`; eventKeyboardRisk → eventKeyboard; labelConfusionRisk → labelPlacement with every
127
+ rule `cantTell` / `judgement-required`); keep three page-environment advisories
128
+ (applicationRisk, browserSupportRisk, noScriptRisk) with every rule `cantTell`. Export a
129
+ `migrations` map (`{contrastRisk: 'contrastAA', …}`) for downstream issue-ID rewrites.
130
+ - Scoring: `cantTellWeight = 0.25` per instance inside the quality-weighted count (a weight-1
131
+ Risk issue merged into a weight-4 base is score-neutral). Element-aware dedup: an element's
132
+ outcome is `failed` if any tool says so, corroborated when more than one does;
133
+ `effectiveCount = max(old per-tool max with the factor applied, element-union count)`.
134
+ Output stays additive (`instanceCounts` numeric; add `outcomeCounts`, `elementCounts`,
135
+ `details.elements`; `details.element` byte-identical).
136
+ - Granularity principle: one tic issue = one remediation class = one WCAG success criterion ×
137
+ one fix. `ruleID` and `what` carry the diagnosis; certainty is never a reason to split an
138
+ issue. Families get `group`; element-conditioned splits (pour `link-name` → image link vs text
139
+ link) are done in the adapter with a `.`-suffixed faceted rule ID, two tic entries each.
140
+
141
+ ## Phase C: yra-monitor (recommended)
142
+
143
+ Types, `tic-manager` `resolveOutcome`, scoring-service passthrough, standardized rows and DB
144
+ columns (`outcome`, `uncertainty`, `needed`) with a promote-only conflict rule (`failed` wins),
145
+ issue-ID rewrite from `migrations`, a "Needs review" badge and filter, and for `cantTell` rows
146
+ the text `instance.needed ?? rule.review ?? 'Manual review required'` under a "To confirm"
147
+ heading.
148
+
149
+ ## Phase D: Testaro 79 (recommended)
150
+
151
+ Redefine `ordinalSeverity` as impact only: 0 minor, 1 moderate, 2 serious, 3 critical, which is
152
+ what yra-monitor already labels it as. `totals` keeps its shape but its composition changes
153
+ (an axe incomplete + critical finding moves from `totals[1]` to `totals[3]`). Safe only after
154
+ Phases B and C, when every consumer reads `outcome`.
155
+
156
+ ## Open decisions
157
+
158
+ 1. `cantTellWeight = 0.25`: confirm against Stage-4 precision on surea11y's `cantTell` band.
159
+ 2. The semantic Risk merges need a "same fix?" pass; videoCaptionRisk → videoNoText and
160
+ fieldSetRisk → fieldSetMissing are the least certain.
161
+ 3. Weights for renamed heuristic issues once they can carry a `failed` band.
162
+ 4. The alfa rule-ID change is visible to the current Testilo now: r66/r69 `cantTell` instances
163
+ map to contrastAAA/contrastAA at full weight until the scorer reads `outcome`. Pin Testaro
164
+ 78.1 in the worker only together with the Phase-B Testilo.
165
+ 5. Whether `unique_issues.severity` should roll up (`GREATEST`) on conflict.
package/env.example CHANGED
@@ -2,8 +2,6 @@
2
2
  WAVE_KEY=__placeholder__
3
3
  # You can get an Anthropic API key at https://console.anthropic.com/.
4
4
  ANTHROPIC_API_KEY=__placeholder__
5
- # Name to identify this Testaro instance to a server.
6
- AGENT=__placeholder__
7
5
  # Whether to make the browser visible (normally false).
8
6
  HEADED_BROWSER=false
9
7
  # Whether to output even forked-job logging to the primary log (normally false).
@@ -12,23 +10,44 @@ DEBUG=false
12
10
  WAITS=0
13
11
  # See https://nodejs.org/api/cli.html#options for all permitted options.
14
12
  NODE_OPTIONS='--trace-uncaught --trace-warnings'
15
- # Suppresses a routine warning from QualWeb.
13
+ # Whether to suppress a routine warning from QualWeb.
16
14
  PUPPETEER_DISABLE_HEADLESS_WARNING=true
17
- # Replace the next 2 placeholders with http://localhost:3000 if Kilotest and Testaro are on the same host or the URL of the server (e.g., https://kilotest.com) if they are on different hosts.
18
- # URL to poll for available jobs when watching the network.
19
- NETWATCH_URL_JOB=__placeholder__/api/testaro-agent/job
20
- # URL to report results to when watching the network.
21
- NETWATCH_URL_REPORT=__placeholder__/api/testaro-agent/report
22
- # Password of this Testaro agent
23
- NETWATCH_URL_AUTH=__placeholder__
24
15
  # Directory (relative to project root) to watch for jobs.
25
16
  JOBDIR=__placeholder__
26
17
  # Directory (relative to project root) to write reports to when watching a directory for jobs.
27
18
  REPORTDIR=__placeholder__
28
19
  # Multiplier for time limits (normally 1).
29
20
  TIMEOUT_MULTIPLIER=1
21
+ # Default isolation of test acts when a job does not specify one: process (a child process and
22
+ # browser per test act), browser (one browser per job, a fresh context per test act), or page
23
+ # (test acts share the live page of the current checkpoint). See docs/checkpoint-scanning.md.
24
+ ISOLATION=process
30
25
  # Whether to abort the job when launch retries are exhausted or a test act crashes (default false).
31
26
  ABORT_ASSERTIVELY=false
27
+
28
+ # Deployment options. A job property of the same meaning overrides each of these.
29
+
30
+ # Load state a navigation waits for before tools run: networkidle (default), load, or
31
+ # domcontentloaded. Retries relax it one step. networkidle never arrives on pages whose
32
+ # network never quiets (beacons, polling), so a deployment scanning such pages may prefer load.
33
+ NAV_WAIT_UNTIL=networkidle
34
+ # Navigation time limit in milliseconds (default 10000).
35
+ NAV_TIMEOUT=10000
36
+ # Whether a 4xx response (other than 408 and 429) ends launch retries at once (default false).
37
+ NAV_FAIL_FAST_4XX=false
38
+ # Scanner identity sent as the X-YRA-Scanner request header, so firewalls can recognize the
39
+ # scanner by a stable value (default none; jobs may set scannerId).
40
+ SCANNER_ID=__placeholder__
41
+ # Whether to scroll the full page after navigation so lazily loaded content is present before
42
+ # cataloguing, imaging, and each tool (default false; jobs may set scroll).
43
+ PRESCAN_SCROLL=false
44
+ # Launch retries per rule of the testaro tool, which relaunches per contaminating rule
45
+ # (default 2; raise for non-Chromium browsers, which navigate flakily on relaunch).
46
+ TESTARO_RULE_RETRIES=2
47
+ # Whether the qualWeb tool's browser runs stealth evasions and blocks ads and trackers
48
+ # (default true; a qualWeb test act may set stealth and adBlock).
49
+ QUALWEB_STEALTH=true
50
+ QUALWEB_ADBLOCK=true
32
51
  # URL of any non-default (e.g., self-hosted) Nu Html Checker API (see ghcr.io/validator/validator).
33
52
  TESTARO_NU_URL=__placeholder__
34
53
  # Whether to launch the Playwright chromium browser without its sandbox (normally false).
@@ -37,6 +56,21 @@ TESTARO_NU_URL=__placeholder__
37
56
  # or run containers with a seccomp profile that permits user-namespace cloning.
38
57
  TESTARO_CHROMIUM_NO_SANDBOX=false
39
58
 
59
+ # netWatch variables
60
+
61
+ # Replace the next 2 placeholders with the URL of the server (e.g., http://localhost:3000 or https://kilotest.com), including the path required by the server.
62
+ # URL to poll for available jobs.
63
+ NETWATCH_URL_JOB=__placeholder__
64
+ # URL to send a job report.
65
+ NETWATCH_URL_REPORT=__placeholder__
66
+ # Type of authentication (none, pathBody, or header) required by the server.
67
+ NETWATCH_AUTH_TYPE=__placeholder__
68
+ # ID of this instance. Recorded in reports as sources.agent and used by header
69
+ # authentication (where it must not contain a colon).
70
+ NETWATCH_WORKER_ID=__placeholder__
71
+ # Password of this instance. Required when NETWATCH_AUTH_TYPE is pathBody or header.
72
+ NETWATCH_WORKER_SECRET=__placeholder__
73
+
40
74
  ## License
41
75
 
42
76
  # © 2021–2025 CVS Health and/or one of its affiliates. All rights reserved.
@@ -0,0 +1,101 @@
1
+ /*
2
+ © 2026 Jeff Witt.
3
+
4
+ Licensed under the MIT License. See LICENSE file at the project root or
5
+ https://opensource.org/license/mit/ for details.
6
+
7
+ SPDX-License-Identifier: MIT
8
+ */
9
+
10
+ /*
11
+ eslint.config.mjs
12
+ Flat ESLint configuration, replacing .eslintrc.json (unsupported since ESLint 9).
13
+ */
14
+
15
+ // IMPORTS
16
+
17
+ import js from '@eslint/js';
18
+ import globals from 'globals';
19
+ import {defineConfig, globalIgnores} from 'eslint/config';
20
+
21
+ // CONFIGURATION
22
+
23
+ export default defineConfig([
24
+ globalIgnores([
25
+ // Build output.
26
+ 'dist/',
27
+ // Vendored HTML CodeSniffer bundle; not to be reformatted.
28
+ 'htmlcs/HTMLCS.js',
29
+ // Vendored Editoria11y bundles.
30
+ 'ed11y/',
31
+ // Vendored SureA11y standalone browser bundle (MPL-2.0; not to be modified).
32
+ 'surea11y/surea11y.browser.js',
33
+ // Vendored Pour Engine bundle, built with esbuild (see pour/README.md).
34
+ 'pour/pour.min.js',
35
+ // Files emitted by tsc from TypeScript sources (issue #73); lint the .ts sources instead.
36
+ 'types.js',
37
+ 'procs/catalog.js',
38
+ 'procs/standard.js',
39
+ 'procs/testaro.js',
40
+ 'procs/xPath.js',
41
+ 'testaro/*.js',
42
+ 'tests/*.js',
43
+ '**/*.d.ts',
44
+ // Validation fixtures, some of which are intentionally defective.
45
+ 'validation/tests/targets/'
46
+ ]),
47
+ {
48
+ files: ['**/*.js'],
49
+ extends: [js.configs.recommended],
50
+ languageOptions: {
51
+ ecmaVersion: 2021,
52
+ sourceType: 'commonjs',
53
+ globals: {
54
+ ...globals.browser,
55
+ ...globals.node
56
+ }
57
+ },
58
+ rules: {
59
+ indent: [
60
+ 'error',
61
+ 2,
62
+ {
63
+ MemberExpression: 0,
64
+ ObjectExpression: 'first'
65
+ }
66
+ ],
67
+ 'linebreak-style': [
68
+ 'error',
69
+ 'unix'
70
+ ],
71
+ quotes: [
72
+ 'error',
73
+ 'single'
74
+ ],
75
+ semi: [
76
+ 'error',
77
+ 'always'
78
+ ],
79
+ 'no-use-before-define': [
80
+ 'error'
81
+ ],
82
+ 'brace-style': [
83
+ 'error',
84
+ 'stroustrup'
85
+ ],
86
+ /*
87
+ Transitional demotions to warnings, so linting can gate changes now.
88
+ These rules have pre-existing violations, some of them latent defects.
89
+ Each is to be restored to an error once its violations are repaired.
90
+ */
91
+ 'no-async-promise-executor': 'warn',
92
+ 'no-const-assign': 'warn',
93
+ 'no-constant-binary-expression': 'warn',
94
+ 'no-empty': 'warn',
95
+ 'no-undef': 'warn',
96
+ 'no-unused-vars': 'warn',
97
+ 'no-use-before-define': 'warn',
98
+ 'no-useless-assignment': 'warn'
99
+ }
100
+ }
101
+ ]);