agentic-qe 3.7.8 → 3.7.10

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 (569) hide show
  1. package/.claude/skills/.validation/README.md +111 -111
  2. package/.claude/skills/.validation/examples/chaos-engineering-output.example.json +530 -530
  3. package/.claude/skills/.validation/examples/performance-testing-output.example.json +252 -252
  4. package/.claude/skills/.validation/examples/security-testing-output.example.json +413 -413
  5. package/.claude/skills/.validation/examples/testability-scoring-output.example.json +350 -350
  6. package/.claude/skills/.validation/schemas/skill-eval.schema.json +462 -462
  7. package/.claude/skills/.validation/schemas/skill-frontmatter.schema.json +341 -341
  8. package/.claude/skills/.validation/schemas/skill-output-meta.schema.json +199 -199
  9. package/.claude/skills/.validation/schemas/skill-output.template.json +610 -610
  10. package/.claude/skills/.validation/skill-validation-mcp-integration.md +250 -250
  11. package/.claude/skills/.validation/templates/eval.template.yaml +366 -366
  12. package/.claude/skills/.validation/templates/schemas/output.json +145 -145
  13. package/.claude/skills/.validation/templates/security-testing-eval.template.yaml +725 -725
  14. package/.claude/skills/.validation/templates/skill-frontmatter.example.yaml +225 -225
  15. package/.claude/skills/.validation/test-data/invalid-output.json +5 -5
  16. package/.claude/skills/.validation/test-data/minimal-output.json +9 -9
  17. package/.claude/skills/.validation/test-data/sample-output.json +73 -73
  18. package/.claude/skills/a11y-ally/evals/a11y-ally.yaml +376 -376
  19. package/.claude/skills/a11y-ally/schemas/output.json +549 -549
  20. package/.claude/skills/accessibility-testing/evals/accessibility-testing.yaml +719 -719
  21. package/.claude/skills/accessibility-testing/schemas/output.json +776 -776
  22. package/.claude/skills/accessibility-testing/test-data/sample-output.json +191 -191
  23. package/.claude/skills/agentic-quality-engineering/schemas/output.json +577 -577
  24. package/.claude/skills/api-testing-patterns/evals/api-testing-patterns.yaml +696 -696
  25. package/.claude/skills/api-testing-patterns/schemas/output.json +845 -845
  26. package/.claude/skills/aqe-v2-v3-migration/schemas/output.json +513 -513
  27. package/.claude/skills/brutal-honesty-review/schemas/output.json +291 -291
  28. package/.claude/skills/bug-reporting-excellence/schemas/output.json +288 -288
  29. package/.claude/skills/chaos-engineering-resilience/evals/chaos-engineering-resilience.yaml +761 -761
  30. package/.claude/skills/chaos-engineering-resilience/schemas/output.json +1205 -1205
  31. package/.claude/skills/cicd-pipeline-qe-orchestrator/evals/cicd-pipeline-qe-orchestrator.yaml +157 -157
  32. package/.claude/skills/cicd-pipeline-qe-orchestrator/schemas/output.json +542 -542
  33. package/.claude/skills/code-review-quality/schemas/output.json +264 -264
  34. package/.claude/skills/compatibility-testing/evals/compatibility-testing.yaml +410 -410
  35. package/.claude/skills/compatibility-testing/schemas/output.json +551 -551
  36. package/.claude/skills/compliance-testing/evals/compliance-testing.yaml +1107 -1107
  37. package/.claude/skills/compliance-testing/schemas/output.json +845 -845
  38. package/.claude/skills/consultancy-practices/schemas/output.json +282 -282
  39. package/.claude/skills/contract-testing/evals/contract-testing.yaml +748 -748
  40. package/.claude/skills/contract-testing/schemas/output.json +638 -638
  41. package/.claude/skills/database-testing/evals/database-testing.yaml +968 -968
  42. package/.claude/skills/database-testing/schemas/output.json +1446 -1446
  43. package/.claude/skills/debug-loop/SKILL.md +61 -61
  44. package/.claude/skills/enterprise-integration-testing/SKILL.md +735 -735
  45. package/.claude/skills/enterprise-integration-testing/evals/enterprise-integration-testing.yaml +158 -0
  46. package/.claude/skills/enterprise-integration-testing/schemas/output.json +74 -0
  47. package/.claude/skills/enterprise-integration-testing/scripts/validate-config.json +25 -0
  48. package/.claude/skills/iterative-loop/SKILL.md +371 -371
  49. package/.claude/skills/localization-testing/evals/localization-testing.yaml +544 -544
  50. package/.claude/skills/localization-testing/schemas/output.json +325 -325
  51. package/.claude/skills/middleware-testing-patterns/SKILL.md +798 -798
  52. package/.claude/skills/middleware-testing-patterns/evals/middleware-testing-patterns.yaml +153 -0
  53. package/.claude/skills/middleware-testing-patterns/schemas/output.json +58 -0
  54. package/.claude/skills/middleware-testing-patterns/scripts/validate-config.json +25 -0
  55. package/.claude/skills/mobile-testing/evals/mobile-testing.yaml +537 -537
  56. package/.claude/skills/mobile-testing/schemas/output.json +318 -318
  57. package/.claude/skills/mutation-testing/evals/mutation-testing.yaml +652 -652
  58. package/.claude/skills/mutation-testing/schemas/output.json +707 -707
  59. package/.claude/skills/mutation-testing/test-data/sample-output.json +295 -295
  60. package/.claude/skills/n8n-expression-testing/evals/n8n-expression-testing.yaml +450 -450
  61. package/.claude/skills/n8n-expression-testing/schemas/output.json +369 -369
  62. package/.claude/skills/n8n-integration-testing-patterns/evals/n8n-integration-testing-patterns.yaml +522 -522
  63. package/.claude/skills/n8n-integration-testing-patterns/schemas/output.json +291 -291
  64. package/.claude/skills/n8n-security-testing/evals/n8n-security-testing.yaml +493 -493
  65. package/.claude/skills/n8n-security-testing/schemas/output.json +293 -293
  66. package/.claude/skills/n8n-trigger-testing-strategies/evals/n8n-trigger-testing-strategies.yaml +500 -500
  67. package/.claude/skills/n8n-trigger-testing-strategies/schemas/output.json +295 -295
  68. package/.claude/skills/n8n-workflow-testing-fundamentals/evals/n8n-workflow-testing-fundamentals.yaml +497 -497
  69. package/.claude/skills/n8n-workflow-testing-fundamentals/schemas/output.json +254 -254
  70. package/.claude/skills/observability-testing-patterns/SKILL.md +930 -930
  71. package/.claude/skills/observability-testing-patterns/evals/observability-testing-patterns.yaml +157 -0
  72. package/.claude/skills/observability-testing-patterns/schemas/output.json +58 -0
  73. package/.claude/skills/observability-testing-patterns/scripts/validate-config.json +25 -0
  74. package/.claude/skills/pentest-validation/SKILL.md +268 -268
  75. package/.claude/skills/pentest-validation/evals/pentest-validation.yaml +708 -708
  76. package/.claude/skills/pentest-validation/schemas/output.json +281 -281
  77. package/.claude/skills/performance-analysis/evals/performance-analysis.yaml +144 -144
  78. package/.claude/skills/performance-analysis/schemas/output.json +588 -588
  79. package/.claude/skills/performance-testing/evals/performance-testing.yaml +772 -772
  80. package/.claude/skills/performance-testing/schemas/output.json +1184 -1184
  81. package/.claude/skills/pr-review/SKILL.md +61 -61
  82. package/.claude/skills/qcsd-cicd-swarm/SKILL.md +2206 -2206
  83. package/.claude/skills/qcsd-cicd-swarm/evals/qcsd-cicd-swarm.yaml +211 -0
  84. package/.claude/skills/qcsd-cicd-swarm/schemas/output.json +86 -0
  85. package/.claude/skills/qcsd-cicd-swarm/scripts/validate-config.json +30 -0
  86. package/.claude/skills/qcsd-development-swarm/SKILL.md +2154 -2154
  87. package/.claude/skills/qcsd-development-swarm/evals/qcsd-development-swarm.yaml +162 -0
  88. package/.claude/skills/qcsd-development-swarm/schemas/output.json +72 -0
  89. package/.claude/skills/qcsd-development-swarm/scripts/validate-config.json +25 -0
  90. package/.claude/skills/qcsd-ideation-swarm/evals/qcsd-ideation-swarm.yaml +138 -138
  91. package/.claude/skills/qcsd-ideation-swarm/schemas/output.json +568 -568
  92. package/.claude/skills/qcsd-production-swarm/SKILL.md +2781 -2781
  93. package/.claude/skills/qcsd-production-swarm/evals/qcsd-production-swarm.yaml +246 -246
  94. package/.claude/skills/qcsd-production-swarm/schemas/output.json +505 -505
  95. package/.claude/skills/qcsd-production-swarm/scripts/validate-config.json +25 -25
  96. package/.claude/skills/qe-chaos-resilience/evals/qe-chaos-resilience.yaml +443 -443
  97. package/.claude/skills/qe-chaos-resilience/schemas/output.json +314 -314
  98. package/.claude/skills/qe-code-intelligence/evals/qe-code-intelligence.yaml +459 -459
  99. package/.claude/skills/qe-code-intelligence/schemas/output.json +315 -315
  100. package/.claude/skills/qe-contract-testing/evals/qe-contract-testing.yaml +513 -513
  101. package/.claude/skills/qe-contract-testing/schemas/output.json +295 -295
  102. package/.claude/skills/qe-coverage-analysis/evals/qe-coverage-analysis.yaml +494 -494
  103. package/.claude/skills/qe-coverage-analysis/schemas/output.json +286 -286
  104. package/.claude/skills/qe-defect-intelligence/evals/qe-defect-intelligence.yaml +511 -511
  105. package/.claude/skills/qe-defect-intelligence/schemas/output.json +283 -283
  106. package/.claude/skills/qe-iterative-loop/schemas/output.json +264 -264
  107. package/.claude/skills/qe-learning-optimization/evals/qe-learning-optimization.yaml +144 -144
  108. package/.claude/skills/qe-learning-optimization/schemas/output.json +288 -288
  109. package/.claude/skills/qe-quality-assessment/evals/qe-quality-assessment.yaml +506 -506
  110. package/.claude/skills/qe-quality-assessment/schemas/output.json +550 -550
  111. package/.claude/skills/qe-requirements-validation/evals/qe-requirements-validation.yaml +598 -598
  112. package/.claude/skills/qe-requirements-validation/schemas/output.json +587 -587
  113. package/.claude/skills/qe-security-compliance/evals/qe-security-compliance.yaml +595 -595
  114. package/.claude/skills/qe-security-compliance/schemas/output.json +498 -498
  115. package/.claude/skills/qe-test-execution/evals/qe-test-execution.yaml +607 -607
  116. package/.claude/skills/qe-test-execution/schemas/output.json +529 -529
  117. package/.claude/skills/qe-test-generation/evals/qe-test-generation.yaml +148 -148
  118. package/.claude/skills/qe-test-generation/schemas/output.json +439 -439
  119. package/.claude/skills/qe-visual-accessibility/evals/qe-visual-accessibility.yaml +142 -142
  120. package/.claude/skills/qe-visual-accessibility/schemas/output.json +491 -491
  121. package/.claude/skills/quality-metrics/evals/quality-metrics.yaml +494 -494
  122. package/.claude/skills/quality-metrics/schemas/output.json +403 -403
  123. package/.claude/skills/refactoring-patterns/schemas/output.json +475 -475
  124. package/.claude/skills/regression-testing/evals/regression-testing.yaml +504 -504
  125. package/.claude/skills/regression-testing/schemas/output.json +311 -311
  126. package/.claude/skills/release/SKILL.md +347 -347
  127. package/.claude/skills/risk-based-testing/evals/risk-based-testing.yaml +141 -141
  128. package/.claude/skills/risk-based-testing/schemas/output.json +480 -480
  129. package/.claude/skills/security-testing/evals/security-testing.yaml +789 -789
  130. package/.claude/skills/security-testing/schemas/output.json +879 -879
  131. package/.claude/skills/security-visual-testing/evals/security-visual-testing.yaml +163 -163
  132. package/.claude/skills/security-visual-testing/schemas/output.json +486 -486
  133. package/.claude/skills/sfdipot-product-factors/SKILL.md +239 -239
  134. package/.claude/skills/sherlock-review/schemas/output.json +297 -297
  135. package/.claude/skills/shift-left-testing/evals/shift-left-testing.yaml +145 -145
  136. package/.claude/skills/shift-left-testing/schemas/output.json +459 -459
  137. package/.claude/skills/shift-right-testing/evals/shift-right-testing.yaml +147 -147
  138. package/.claude/skills/shift-right-testing/schemas/output.json +418 -418
  139. package/.claude/skills/skills-manifest.json +1 -1
  140. package/.claude/skills/tdd-london-chicago/schemas/output.json +444 -444
  141. package/.claude/skills/technical-writing/schemas/output.json +268 -268
  142. package/.claude/skills/test-automation-strategy/evals/test-automation-strategy.yaml +148 -148
  143. package/.claude/skills/test-automation-strategy/schemas/output.json +444 -444
  144. package/.claude/skills/test-data-management/evals/test-data-management.yaml +504 -504
  145. package/.claude/skills/test-data-management/schemas/output.json +284 -284
  146. package/.claude/skills/test-design-techniques/evals/test-design-techniques.yaml +142 -142
  147. package/.claude/skills/test-design-techniques/schemas/output.json +295 -295
  148. package/.claude/skills/test-environment-management/schemas/output.json +310 -310
  149. package/.claude/skills/test-idea-rewriting/SKILL.md +229 -229
  150. package/.claude/skills/test-reporting-analytics/evals/test-reporting-analytics.yaml +155 -155
  151. package/.claude/skills/test-reporting-analytics/schemas/output.json +329 -329
  152. package/.claude/skills/testability-scoring/evals/testability-scoring.yaml +814 -814
  153. package/.claude/skills/testability-scoring/resources/templates/config.template.js +84 -84
  154. package/.claude/skills/testability-scoring/schemas/output.json +606 -606
  155. package/.claude/skills/testability-scoring/scripts/generate-html-report.js +1007 -1007
  156. package/.claude/skills/trust-tier-manifest.json +78 -7
  157. package/.claude/skills/verification-quality/evals/verification-quality.yaml +150 -150
  158. package/.claude/skills/verification-quality/schemas/output.json +432 -432
  159. package/.claude/skills/visual-testing-advanced/evals/visual-testing-advanced.yaml +154 -154
  160. package/.claude/skills/visual-testing-advanced/schemas/output.json +294 -294
  161. package/.claude/skills/wms-testing-patterns/evals/wms-testing-patterns.yaml +165 -165
  162. package/.claude/skills/wms-testing-patterns/schemas/output.json +150 -150
  163. package/.claude/skills/wms-testing-patterns/scripts/validate-config.json +51 -51
  164. package/CHANGELOG.md +34 -0
  165. package/README.md +169 -900
  166. package/assets/agents/v3/helpers/quality-criteria/evidence-classification.md +116 -116
  167. package/assets/agents/v3/helpers/quality-criteria/htsm-categories.md +139 -139
  168. package/assets/governance/constitution.md +202 -202
  169. package/assets/governance/shards/chaos-resilience.shard.md +221 -221
  170. package/assets/governance/shards/code-intelligence.shard.md +178 -178
  171. package/assets/governance/shards/contract-testing.shard.md +206 -206
  172. package/assets/governance/shards/coverage-analysis.shard.md +146 -146
  173. package/assets/governance/shards/defect-intelligence.shard.md +182 -182
  174. package/assets/governance/shards/learning-optimization.shard.md +248 -248
  175. package/assets/governance/shards/quality-assessment.shard.md +165 -165
  176. package/assets/governance/shards/requirements-validation.shard.md +177 -177
  177. package/assets/governance/shards/security-compliance.shard.md +196 -196
  178. package/assets/governance/shards/test-execution.shard.md +156 -156
  179. package/assets/governance/shards/test-generation.shard.md +128 -128
  180. package/assets/governance/shards/visual-accessibility.shard.md +209 -209
  181. package/assets/hooks/cross-phase-memory.yaml +253 -253
  182. package/assets/patterns/adr-051-booster-patterns.json +78 -78
  183. package/assets/patterns/adr-051-embedding-patterns.json +147 -147
  184. package/assets/patterns/adr-051-integration-summary.json +62 -62
  185. package/assets/patterns/adr-051-reasoning-patterns.json +166 -166
  186. package/assets/patterns/adr-051-router-patterns.json +113 -113
  187. package/assets/patterns/index.json +136 -136
  188. package/assets/skills/.validation/README.md +111 -111
  189. package/assets/skills/.validation/examples/chaos-engineering-output.example.json +530 -530
  190. package/assets/skills/.validation/examples/performance-testing-output.example.json +252 -252
  191. package/assets/skills/.validation/examples/security-testing-output.example.json +413 -413
  192. package/assets/skills/.validation/examples/testability-scoring-output.example.json +350 -350
  193. package/assets/skills/.validation/schemas/skill-eval.schema.json +462 -462
  194. package/assets/skills/.validation/schemas/skill-frontmatter.schema.json +341 -341
  195. package/assets/skills/.validation/schemas/skill-output-meta.schema.json +199 -199
  196. package/assets/skills/.validation/schemas/skill-output.template.json +610 -610
  197. package/assets/skills/.validation/skill-validation-mcp-integration.md +250 -250
  198. package/assets/skills/.validation/templates/eval.template.yaml +366 -366
  199. package/assets/skills/.validation/templates/schemas/output.json +145 -145
  200. package/assets/skills/.validation/templates/security-testing-eval.template.yaml +725 -725
  201. package/assets/skills/.validation/templates/skill-frontmatter.example.yaml +225 -225
  202. package/assets/skills/.validation/test-data/invalid-output.json +5 -5
  203. package/assets/skills/.validation/test-data/minimal-output.json +9 -9
  204. package/assets/skills/.validation/test-data/sample-output.json +73 -73
  205. package/assets/skills/a11y-ally/SKILL.md +1664 -1658
  206. package/assets/skills/a11y-ally/evals/a11y-ally.yaml +376 -0
  207. package/assets/skills/a11y-ally/schemas/output.json +549 -0
  208. package/assets/skills/a11y-ally/scripts/validate-config.json +42 -0
  209. package/assets/skills/accessibility-testing/evals/accessibility-testing.yaml +719 -719
  210. package/assets/skills/accessibility-testing/schemas/output.json +776 -776
  211. package/assets/skills/accessibility-testing/test-data/sample-output.json +191 -191
  212. package/assets/skills/agentic-quality-engineering/schemas/output.json +577 -577
  213. package/assets/skills/api-testing-patterns/evals/api-testing-patterns.yaml +696 -696
  214. package/assets/skills/api-testing-patterns/schemas/output.json +845 -845
  215. package/assets/skills/aqe-v2-v3-migration/schemas/output.json +513 -513
  216. package/assets/skills/brutal-honesty-review/SKILL.md +5 -0
  217. package/assets/skills/brutal-honesty-review/schemas/output.json +291 -0
  218. package/assets/skills/brutal-honesty-review/scripts/validate-config.json +34 -0
  219. package/assets/skills/bug-reporting-excellence/schemas/output.json +288 -288
  220. package/assets/skills/chaos-engineering-resilience/evals/chaos-engineering-resilience.yaml +761 -761
  221. package/assets/skills/chaos-engineering-resilience/schemas/output.json +1205 -1205
  222. package/assets/skills/cicd-pipeline-qe-orchestrator/README.md +1 -1
  223. package/assets/skills/cicd-pipeline-qe-orchestrator/SKILL.md +6 -0
  224. package/assets/skills/cicd-pipeline-qe-orchestrator/evals/cicd-pipeline-qe-orchestrator.yaml +157 -0
  225. package/assets/skills/cicd-pipeline-qe-orchestrator/schemas/output.json +542 -0
  226. package/assets/skills/cicd-pipeline-qe-orchestrator/scripts/validate-config.json +42 -0
  227. package/assets/skills/code-review-quality/schemas/output.json +264 -264
  228. package/assets/skills/compatibility-testing/evals/compatibility-testing.yaml +410 -410
  229. package/assets/skills/compatibility-testing/schemas/output.json +551 -551
  230. package/assets/skills/compliance-testing/evals/compliance-testing.yaml +1107 -1107
  231. package/assets/skills/compliance-testing/schemas/output.json +845 -845
  232. package/assets/skills/consultancy-practices/schemas/output.json +282 -282
  233. package/assets/skills/contract-testing/evals/contract-testing.yaml +748 -748
  234. package/assets/skills/contract-testing/schemas/output.json +638 -638
  235. package/assets/skills/database-testing/evals/database-testing.yaml +968 -968
  236. package/assets/skills/database-testing/schemas/output.json +1446 -1446
  237. package/assets/skills/debug-loop/SKILL.md +61 -61
  238. package/assets/skills/enterprise-integration-testing/SKILL.md +735 -735
  239. package/assets/skills/enterprise-integration-testing/evals/enterprise-integration-testing.yaml +158 -0
  240. package/assets/skills/enterprise-integration-testing/schemas/output.json +74 -0
  241. package/assets/skills/enterprise-integration-testing/scripts/validate-config.json +25 -0
  242. package/assets/skills/localization-testing/evals/localization-testing.yaml +544 -544
  243. package/assets/skills/localization-testing/schemas/output.json +325 -325
  244. package/assets/skills/middleware-testing-patterns/SKILL.md +798 -798
  245. package/assets/skills/middleware-testing-patterns/evals/middleware-testing-patterns.yaml +153 -0
  246. package/assets/skills/middleware-testing-patterns/schemas/output.json +58 -0
  247. package/assets/skills/middleware-testing-patterns/scripts/validate-config.json +25 -0
  248. package/assets/skills/mobile-testing/evals/mobile-testing.yaml +537 -537
  249. package/assets/skills/mobile-testing/schemas/output.json +318 -318
  250. package/assets/skills/mutation-testing/evals/mutation-testing.yaml +652 -652
  251. package/assets/skills/mutation-testing/schemas/output.json +707 -707
  252. package/assets/skills/mutation-testing/test-data/sample-output.json +295 -295
  253. package/assets/skills/n8n-expression-testing/SKILL.md +6 -0
  254. package/assets/skills/n8n-expression-testing/evals/n8n-expression-testing.yaml +450 -0
  255. package/assets/skills/n8n-expression-testing/schemas/output.json +369 -0
  256. package/assets/skills/n8n-expression-testing/scripts/validate-config.json +39 -0
  257. package/assets/skills/n8n-integration-testing-patterns/SKILL.md +6 -0
  258. package/assets/skills/n8n-integration-testing-patterns/evals/n8n-integration-testing-patterns.yaml +522 -0
  259. package/assets/skills/n8n-integration-testing-patterns/schemas/output.json +291 -0
  260. package/assets/skills/n8n-integration-testing-patterns/scripts/validate-config.json +34 -0
  261. package/assets/skills/n8n-security-testing/SKILL.md +6 -0
  262. package/assets/skills/n8n-security-testing/evals/n8n-security-testing.yaml +493 -0
  263. package/assets/skills/n8n-security-testing/schemas/output.json +293 -0
  264. package/assets/skills/n8n-security-testing/scripts/validate-config.json +34 -0
  265. package/assets/skills/n8n-trigger-testing-strategies/SKILL.md +6 -0
  266. package/assets/skills/n8n-trigger-testing-strategies/evals/n8n-trigger-testing-strategies.yaml +500 -0
  267. package/assets/skills/n8n-trigger-testing-strategies/schemas/output.json +295 -0
  268. package/assets/skills/n8n-trigger-testing-strategies/scripts/validate-config.json +34 -0
  269. package/assets/skills/n8n-workflow-testing-fundamentals/SKILL.md +6 -0
  270. package/assets/skills/n8n-workflow-testing-fundamentals/evals/n8n-workflow-testing-fundamentals.yaml +497 -0
  271. package/assets/skills/n8n-workflow-testing-fundamentals/schemas/output.json +254 -0
  272. package/assets/skills/n8n-workflow-testing-fundamentals/scripts/validate-config.json +35 -0
  273. package/assets/skills/observability-testing-patterns/SKILL.md +930 -930
  274. package/assets/skills/observability-testing-patterns/evals/observability-testing-patterns.yaml +157 -0
  275. package/assets/skills/observability-testing-patterns/schemas/output.json +58 -0
  276. package/assets/skills/observability-testing-patterns/scripts/validate-config.json +25 -0
  277. package/assets/skills/pentest-validation/SKILL.md +268 -268
  278. package/assets/skills/pentest-validation/evals/pentest-validation.yaml +708 -708
  279. package/assets/skills/pentest-validation/schemas/output.json +281 -281
  280. package/assets/skills/pentest-validation/scripts/validate-config.json +12 -0
  281. package/assets/skills/performance-testing/evals/performance-testing.yaml +772 -772
  282. package/assets/skills/performance-testing/schemas/output.json +1184 -1184
  283. package/assets/skills/pr-review/SKILL.md +61 -61
  284. package/assets/skills/qcsd-cicd-swarm/SKILL.md +2206 -2206
  285. package/assets/skills/qcsd-cicd-swarm/evals/qcsd-cicd-swarm.yaml +211 -0
  286. package/assets/skills/qcsd-cicd-swarm/schemas/output.json +86 -0
  287. package/assets/skills/qcsd-cicd-swarm/scripts/validate-config.json +30 -0
  288. package/assets/skills/qcsd-development-swarm/SKILL.md +2154 -2154
  289. package/assets/skills/qcsd-development-swarm/evals/qcsd-development-swarm.yaml +162 -0
  290. package/assets/skills/qcsd-development-swarm/schemas/output.json +72 -0
  291. package/assets/skills/qcsd-development-swarm/scripts/validate-config.json +25 -0
  292. package/assets/skills/qcsd-ideation-swarm/evals/qcsd-ideation-swarm.yaml +138 -0
  293. package/assets/skills/qcsd-ideation-swarm/schemas/output.json +568 -0
  294. package/assets/skills/qcsd-ideation-swarm/scripts/validate-config.json +25 -0
  295. package/assets/skills/qcsd-production-swarm/SKILL.md +2781 -0
  296. package/assets/skills/qcsd-production-swarm/evals/qcsd-production-swarm.yaml +246 -0
  297. package/assets/skills/qcsd-production-swarm/schemas/output.json +505 -0
  298. package/assets/skills/qcsd-production-swarm/scripts/validate-config.json +25 -0
  299. package/assets/skills/qcsd-refinement-swarm/evals/qcsd-refinement-swarm.yaml +139 -0
  300. package/assets/skills/qcsd-refinement-swarm/schemas/output.json +811 -0
  301. package/assets/skills/qcsd-refinement-swarm/scripts/validate-config.json +25 -0
  302. package/assets/skills/qe-chaos-resilience/evals/qe-chaos-resilience.yaml +443 -443
  303. package/assets/skills/qe-chaos-resilience/schemas/output.json +314 -314
  304. package/assets/skills/qe-code-intelligence/evals/qe-code-intelligence.yaml +459 -459
  305. package/assets/skills/qe-code-intelligence/schemas/output.json +315 -315
  306. package/assets/skills/qe-contract-testing/evals/qe-contract-testing.yaml +513 -513
  307. package/assets/skills/qe-contract-testing/schemas/output.json +295 -295
  308. package/assets/skills/qe-coverage-analysis/evals/qe-coverage-analysis.yaml +494 -494
  309. package/assets/skills/qe-coverage-analysis/schemas/output.json +286 -286
  310. package/assets/skills/qe-defect-intelligence/evals/qe-defect-intelligence.yaml +511 -511
  311. package/assets/skills/qe-defect-intelligence/schemas/output.json +283 -283
  312. package/assets/skills/qe-iterative-loop/schemas/output.json +264 -264
  313. package/assets/skills/qe-learning-optimization/evals/qe-learning-optimization.yaml +144 -144
  314. package/assets/skills/qe-learning-optimization/schemas/output.json +288 -288
  315. package/assets/skills/qe-quality-assessment/evals/qe-quality-assessment.yaml +506 -506
  316. package/assets/skills/qe-quality-assessment/schemas/output.json +550 -550
  317. package/assets/skills/qe-requirements-validation/evals/qe-requirements-validation.yaml +598 -598
  318. package/assets/skills/qe-requirements-validation/schemas/output.json +587 -587
  319. package/assets/skills/qe-security-compliance/evals/qe-security-compliance.yaml +595 -595
  320. package/assets/skills/qe-security-compliance/schemas/output.json +498 -498
  321. package/assets/skills/qe-test-execution/evals/qe-test-execution.yaml +607 -607
  322. package/assets/skills/qe-test-execution/schemas/output.json +529 -529
  323. package/assets/skills/qe-test-generation/evals/qe-test-generation.yaml +148 -148
  324. package/assets/skills/qe-test-generation/schemas/output.json +439 -439
  325. package/assets/skills/qe-visual-accessibility/evals/qe-visual-accessibility.yaml +142 -142
  326. package/assets/skills/qe-visual-accessibility/schemas/output.json +491 -491
  327. package/assets/skills/quality-metrics/evals/quality-metrics.yaml +494 -494
  328. package/assets/skills/quality-metrics/schemas/output.json +403 -403
  329. package/assets/skills/refactoring-patterns/schemas/output.json +475 -475
  330. package/assets/skills/regression-testing/evals/regression-testing.yaml +504 -504
  331. package/assets/skills/regression-testing/schemas/output.json +311 -311
  332. package/assets/skills/risk-based-testing/evals/risk-based-testing.yaml +141 -141
  333. package/assets/skills/risk-based-testing/schemas/output.json +480 -480
  334. package/assets/skills/security-testing/evals/security-testing.yaml +789 -789
  335. package/assets/skills/security-testing/schemas/output.json +879 -879
  336. package/assets/skills/security-visual-testing/evals/security-visual-testing.yaml +163 -163
  337. package/assets/skills/security-visual-testing/schemas/output.json +486 -486
  338. package/assets/skills/security-visual-testing/scripts/validate-config.json +45 -0
  339. package/assets/skills/sfdipot-product-factors/SKILL.md +239 -239
  340. package/assets/skills/sherlock-review/SKILL.md +5 -0
  341. package/assets/skills/sherlock-review/schemas/output.json +297 -0
  342. package/assets/skills/sherlock-review/scripts/validate-config.json +35 -0
  343. package/assets/skills/shift-left-testing/evals/shift-left-testing.yaml +145 -145
  344. package/assets/skills/shift-left-testing/schemas/output.json +459 -459
  345. package/assets/skills/shift-right-testing/evals/shift-right-testing.yaml +147 -147
  346. package/assets/skills/shift-right-testing/schemas/output.json +418 -418
  347. package/assets/skills/tdd-london-chicago/schemas/output.json +444 -444
  348. package/assets/skills/technical-writing/schemas/output.json +268 -268
  349. package/assets/skills/test-automation-strategy/evals/test-automation-strategy.yaml +148 -148
  350. package/assets/skills/test-automation-strategy/schemas/output.json +444 -444
  351. package/assets/skills/test-data-management/evals/test-data-management.yaml +504 -504
  352. package/assets/skills/test-data-management/schemas/output.json +284 -284
  353. package/assets/skills/test-design-techniques/evals/test-design-techniques.yaml +142 -142
  354. package/assets/skills/test-design-techniques/schemas/output.json +295 -295
  355. package/assets/skills/test-environment-management/schemas/output.json +310 -310
  356. package/assets/skills/test-idea-rewriting/SKILL.md +229 -229
  357. package/assets/skills/test-reporting-analytics/evals/test-reporting-analytics.yaml +155 -155
  358. package/assets/skills/test-reporting-analytics/schemas/output.json +329 -329
  359. package/assets/skills/testability-scoring/SKILL.md +5 -0
  360. package/assets/skills/testability-scoring/evals/testability-scoring.yaml +814 -0
  361. package/assets/skills/testability-scoring/resources/templates/config.template.js +84 -84
  362. package/assets/skills/testability-scoring/schemas/output.json +606 -0
  363. package/assets/skills/testability-scoring/scripts/generate-html-report.js +1007 -1007
  364. package/assets/skills/testability-scoring/scripts/validate-config.json +42 -0
  365. package/assets/skills/trust-tier-manifest.json +2404 -0
  366. package/assets/skills/verification-quality/evals/verification-quality.yaml +150 -150
  367. package/assets/skills/verification-quality/schemas/output.json +432 -432
  368. package/assets/skills/visual-testing-advanced/evals/visual-testing-advanced.yaml +154 -154
  369. package/assets/skills/visual-testing-advanced/schemas/output.json +294 -294
  370. package/assets/skills/wms-testing-patterns/evals/wms-testing-patterns.yaml +165 -0
  371. package/assets/skills/wms-testing-patterns/schemas/output.json +150 -0
  372. package/assets/skills/wms-testing-patterns/scripts/validate-config.json +51 -0
  373. package/assets/templates/validation-summary.json +56 -56
  374. package/dist/benchmarks/performance-benchmarks.js +1 -1
  375. package/dist/cli/bundle.js +9158 -2288
  376. package/dist/cli/commands/hooks.d.ts.map +1 -1
  377. package/dist/cli/commands/hooks.js +92 -0
  378. package/dist/cli/commands/hooks.js.map +1 -1
  379. package/dist/cli/commands/mcp.d.ts.map +1 -1
  380. package/dist/cli/commands/mcp.js +11 -9
  381. package/dist/cli/commands/mcp.js.map +1 -1
  382. package/dist/cli/commands/migrate.js +2 -2
  383. package/dist/coordination/constants.d.ts +1 -1
  384. package/dist/coordination/constants.js +1 -1
  385. package/dist/coordination/handlers/coverage-handlers.js +1 -1
  386. package/dist/coordination/handlers/coverage-handlers.js.map +1 -1
  387. package/dist/domains/code-intelligence/services/semantic-analyzer.d.ts +1 -1
  388. package/dist/domains/code-intelligence/services/semantic-analyzer.d.ts.map +1 -1
  389. package/dist/domains/code-intelligence/services/semantic-analyzer.js +1 -1
  390. package/dist/domains/code-intelligence/services/semantic-analyzer.js.map +1 -1
  391. package/dist/domains/coverage-analysis/coordinator.js +1 -1
  392. package/dist/domains/coverage-analysis/services/coverage-analyzer.js +1 -1
  393. package/dist/domains/coverage-analysis/services/coverage-embedder.d.ts +1 -1
  394. package/dist/domains/coverage-analysis/services/coverage-embedder.js +1 -1
  395. package/dist/domains/coverage-analysis/services/gap-detector.js +1 -1
  396. package/dist/domains/coverage-analysis/services/ghost-coverage-analyzer.js +1 -1
  397. package/dist/domains/coverage-analysis/services/hnsw-index.d.ts +2 -2
  398. package/dist/domains/coverage-analysis/services/hnsw-index.js +3 -3
  399. package/dist/domains/coverage-analysis/services/sublinear-analyzer.d.ts +1 -1
  400. package/dist/domains/coverage-analysis/services/sublinear-analyzer.js +1 -1
  401. package/dist/domains/test-execution/services/test-prioritizer.js +1 -1
  402. package/dist/domains/test-generation/context/rust-context-builder.d.ts +31 -0
  403. package/dist/domains/test-generation/context/rust-context-builder.d.ts.map +1 -0
  404. package/dist/domains/test-generation/context/rust-context-builder.js +27 -0
  405. package/dist/domains/test-generation/context/rust-context-builder.js.map +1 -0
  406. package/dist/domains/test-generation/coordinator.js +3 -3
  407. package/dist/domains/test-generation/coordinator.js.map +1 -1
  408. package/dist/domains/test-generation/detectors/mobile-detector.d.ts +41 -0
  409. package/dist/domains/test-generation/detectors/mobile-detector.d.ts.map +1 -0
  410. package/dist/domains/test-generation/detectors/mobile-detector.js +111 -0
  411. package/dist/domains/test-generation/detectors/mobile-detector.js.map +1 -0
  412. package/dist/domains/test-generation/detectors/spring-detector.d.ts +22 -0
  413. package/dist/domains/test-generation/detectors/spring-detector.d.ts.map +1 -0
  414. package/dist/domains/test-generation/detectors/spring-detector.js +37 -0
  415. package/dist/domains/test-generation/detectors/spring-detector.js.map +1 -0
  416. package/dist/domains/test-generation/factories/test-generator-factory.d.ts +2 -1
  417. package/dist/domains/test-generation/factories/test-generator-factory.d.ts.map +1 -1
  418. package/dist/domains/test-generation/factories/test-generator-factory.js +33 -13
  419. package/dist/domains/test-generation/factories/test-generator-factory.js.map +1 -1
  420. package/dist/domains/test-generation/generators/flutter-test-generator.d.ts +107 -0
  421. package/dist/domains/test-generation/generators/flutter-test-generator.d.ts.map +1 -0
  422. package/dist/domains/test-generation/generators/flutter-test-generator.js +590 -0
  423. package/dist/domains/test-generation/generators/flutter-test-generator.js.map +1 -0
  424. package/dist/domains/test-generation/generators/go-test-generator.d.ts +139 -0
  425. package/dist/domains/test-generation/generators/go-test-generator.d.ts.map +1 -0
  426. package/dist/domains/test-generation/generators/go-test-generator.js +654 -0
  427. package/dist/domains/test-generation/generators/go-test-generator.js.map +1 -0
  428. package/dist/domains/test-generation/generators/index.d.ts +8 -0
  429. package/dist/domains/test-generation/generators/index.d.ts.map +1 -1
  430. package/dist/domains/test-generation/generators/index.js +8 -0
  431. package/dist/domains/test-generation/generators/index.js.map +1 -1
  432. package/dist/domains/test-generation/generators/jest-rn-generator.d.ts +95 -0
  433. package/dist/domains/test-generation/generators/jest-rn-generator.d.ts.map +1 -0
  434. package/dist/domains/test-generation/generators/jest-rn-generator.js +591 -0
  435. package/dist/domains/test-generation/generators/jest-rn-generator.js.map +1 -0
  436. package/dist/domains/test-generation/generators/junit5-generator.d.ts +107 -0
  437. package/dist/domains/test-generation/generators/junit5-generator.d.ts.map +1 -0
  438. package/dist/domains/test-generation/generators/junit5-generator.js +588 -0
  439. package/dist/domains/test-generation/generators/junit5-generator.js.map +1 -0
  440. package/dist/domains/test-generation/generators/kotlin-junit-generator.d.ts +109 -0
  441. package/dist/domains/test-generation/generators/kotlin-junit-generator.d.ts.map +1 -0
  442. package/dist/domains/test-generation/generators/kotlin-junit-generator.js +588 -0
  443. package/dist/domains/test-generation/generators/kotlin-junit-generator.js.map +1 -0
  444. package/dist/domains/test-generation/generators/pytest-generator.d.ts +8 -1
  445. package/dist/domains/test-generation/generators/pytest-generator.d.ts.map +1 -1
  446. package/dist/domains/test-generation/generators/pytest-generator.js +57 -0
  447. package/dist/domains/test-generation/generators/pytest-generator.js.map +1 -1
  448. package/dist/domains/test-generation/generators/rust-test-generator.d.ts +80 -0
  449. package/dist/domains/test-generation/generators/rust-test-generator.d.ts.map +1 -0
  450. package/dist/domains/test-generation/generators/rust-test-generator.js +442 -0
  451. package/dist/domains/test-generation/generators/rust-test-generator.js.map +1 -0
  452. package/dist/domains/test-generation/generators/swift-testing-generator.d.ts +97 -0
  453. package/dist/domains/test-generation/generators/swift-testing-generator.d.ts.map +1 -0
  454. package/dist/domains/test-generation/generators/swift-testing-generator.js +482 -0
  455. package/dist/domains/test-generation/generators/swift-testing-generator.js.map +1 -0
  456. package/dist/domains/test-generation/generators/xunit-generator.d.ts +110 -0
  457. package/dist/domains/test-generation/generators/xunit-generator.d.ts.map +1 -0
  458. package/dist/domains/test-generation/generators/xunit-generator.js +611 -0
  459. package/dist/domains/test-generation/generators/xunit-generator.js.map +1 -0
  460. package/dist/domains/test-generation/interfaces.d.ts +11 -2
  461. package/dist/domains/test-generation/interfaces.d.ts.map +1 -1
  462. package/dist/domains/test-generation/prompts/language-prompts.d.ts +29 -0
  463. package/dist/domains/test-generation/prompts/language-prompts.d.ts.map +1 -0
  464. package/dist/domains/test-generation/prompts/language-prompts.js +135 -0
  465. package/dist/domains/test-generation/prompts/language-prompts.js.map +1 -0
  466. package/dist/domains/test-generation/services/compilation-validator.d.ts +43 -0
  467. package/dist/domains/test-generation/services/compilation-validator.d.ts.map +1 -0
  468. package/dist/domains/test-generation/services/compilation-validator.js +134 -0
  469. package/dist/domains/test-generation/services/compilation-validator.js.map +1 -0
  470. package/dist/domains/test-generation/services/index.d.ts +2 -1
  471. package/dist/domains/test-generation/services/index.d.ts.map +1 -1
  472. package/dist/domains/test-generation/services/index.js +3 -1
  473. package/dist/domains/test-generation/services/index.js.map +1 -1
  474. package/dist/domains/test-generation/services/test-file-resolver.d.ts +32 -0
  475. package/dist/domains/test-generation/services/test-file-resolver.d.ts.map +1 -0
  476. package/dist/domains/test-generation/services/test-file-resolver.js +159 -0
  477. package/dist/domains/test-generation/services/test-file-resolver.js.map +1 -0
  478. package/dist/domains/test-generation/services/test-generator.d.ts +10 -0
  479. package/dist/domains/test-generation/services/test-generator.d.ts.map +1 -1
  480. package/dist/domains/test-generation/services/test-generator.js +87 -10
  481. package/dist/domains/test-generation/services/test-generator.js.map +1 -1
  482. package/dist/governance/feature-flags.js +2 -2
  483. package/dist/governance/feature-flags.js.map +1 -1
  484. package/dist/governance/shard-embeddings.js +1 -1
  485. package/dist/init/init-wizard-hooks.d.ts.map +1 -1
  486. package/dist/init/init-wizard-hooks.js +0 -1
  487. package/dist/init/init-wizard-hooks.js.map +1 -1
  488. package/dist/init/phases/07-hooks.d.ts.map +1 -1
  489. package/dist/init/phases/07-hooks.js +0 -2
  490. package/dist/init/phases/07-hooks.js.map +1 -1
  491. package/dist/init/phases/08-mcp.d.ts +8 -4
  492. package/dist/init/phases/08-mcp.d.ts.map +1 -1
  493. package/dist/init/phases/08-mcp.js +13 -31
  494. package/dist/init/phases/08-mcp.js.map +1 -1
  495. package/dist/init/phases/10-workers.js +4 -4
  496. package/dist/init/phases/10-workers.js.map +1 -1
  497. package/dist/init/settings-merge.d.ts.map +1 -1
  498. package/dist/init/settings-merge.js +0 -2
  499. package/dist/init/settings-merge.js.map +1 -1
  500. package/dist/init/token-bootstrap.js +1 -1
  501. package/dist/init/token-bootstrap.js.map +1 -1
  502. package/dist/integrations/rl-suite/algorithms/decision-transformer.js +1 -1
  503. package/dist/kernel/constants.d.ts +2 -2
  504. package/dist/kernel/constants.js +2 -2
  505. package/dist/kernel/hnsw-adapter.js +1 -1
  506. package/dist/kernel/progressive-hnsw-backend.d.ts +2 -2
  507. package/dist/kernel/progressive-hnsw-backend.js +2 -2
  508. package/dist/learning/dream/concept-graph.d.ts +1 -1
  509. package/dist/learning/dream/concept-graph.js +1 -1
  510. package/dist/learning/dream/dream-engine.d.ts +1 -1
  511. package/dist/learning/dream/dream-engine.js +1 -1
  512. package/dist/learning/dream/index.d.ts +1 -1
  513. package/dist/learning/dream/index.js +1 -1
  514. package/dist/learning/dream/types.d.ts +1 -1
  515. package/dist/learning/dream/types.d.ts.map +1 -1
  516. package/dist/learning/dream/types.js +1 -1
  517. package/dist/learning/dream/types.js.map +1 -1
  518. package/dist/learning/token-tracker.js +1 -1
  519. package/dist/learning/token-tracker.js.map +1 -1
  520. package/dist/mcp/bundle.js +7538 -893
  521. package/dist/routing/qe-agent-registry.js +4 -4
  522. package/dist/routing/qe-agent-registry.js.map +1 -1
  523. package/dist/routing/types.d.ts +5 -8
  524. package/dist/routing/types.d.ts.map +1 -1
  525. package/dist/routing/types.js.map +1 -1
  526. package/dist/shared/embeddings/embedding-cache.js +2 -2
  527. package/dist/shared/embeddings/index.d.ts +2 -2
  528. package/dist/shared/embeddings/index.js +2 -2
  529. package/dist/shared/embeddings/nomic-embedder.d.ts +4 -4
  530. package/dist/shared/embeddings/nomic-embedder.js +2 -2
  531. package/dist/shared/embeddings/ollama-client.d.ts +1 -1
  532. package/dist/shared/embeddings/ollama-client.js +2 -2
  533. package/dist/shared/embeddings/ollama-client.js.map +1 -1
  534. package/dist/shared/embeddings/types.d.ts +2 -2
  535. package/dist/shared/embeddings/types.js +2 -2
  536. package/dist/shared/language-detector.d.ts +46 -0
  537. package/dist/shared/language-detector.d.ts.map +1 -0
  538. package/dist/shared/language-detector.js +183 -0
  539. package/dist/shared/language-detector.js.map +1 -0
  540. package/dist/shared/llm/providers/ollama.js +1 -1
  541. package/dist/shared/metrics/code-metrics.d.ts.map +1 -1
  542. package/dist/shared/metrics/code-metrics.js +24 -1
  543. package/dist/shared/metrics/code-metrics.js.map +1 -1
  544. package/dist/shared/parsers/index.d.ts +2 -0
  545. package/dist/shared/parsers/index.d.ts.map +1 -1
  546. package/dist/shared/parsers/index.js +2 -0
  547. package/dist/shared/parsers/index.js.map +1 -1
  548. package/dist/shared/parsers/interfaces.d.ts +81 -0
  549. package/dist/shared/parsers/interfaces.d.ts.map +1 -0
  550. package/dist/shared/parsers/interfaces.js +6 -0
  551. package/dist/shared/parsers/interfaces.js.map +1 -0
  552. package/dist/shared/parsers/multi-language-parser.d.ts +144 -0
  553. package/dist/shared/parsers/multi-language-parser.d.ts.map +1 -0
  554. package/dist/shared/parsers/multi-language-parser.js +1271 -0
  555. package/dist/shared/parsers/multi-language-parser.js.map +1 -0
  556. package/dist/shared/parsers/rust-ownership-analyzer.d.ts +45 -0
  557. package/dist/shared/parsers/rust-ownership-analyzer.d.ts.map +1 -0
  558. package/dist/shared/parsers/rust-ownership-analyzer.js +52 -0
  559. package/dist/shared/parsers/rust-ownership-analyzer.js.map +1 -0
  560. package/dist/shared/parsers/typescript-parser.d.ts +16 -0
  561. package/dist/shared/parsers/typescript-parser.d.ts.map +1 -1
  562. package/dist/shared/parsers/typescript-parser.js +85 -0
  563. package/dist/shared/parsers/typescript-parser.js.map +1 -1
  564. package/dist/shared/types/test-frameworks.d.ts +25 -0
  565. package/dist/shared/types/test-frameworks.d.ts.map +1 -0
  566. package/dist/shared/types/test-frameworks.js +111 -0
  567. package/dist/shared/types/test-frameworks.js.map +1 -0
  568. package/package.json +1 -1
  569. package/scripts/prepare-assets.sh +16 -2
@@ -1,1658 +1,1664 @@
1
- ---
2
- name: a11y-ally
3
- description: "Comprehensive WCAG accessibility auditing with multi-tool testing (axe-core + pa11y + Lighthouse), TRUE PARALLEL execution with Promise.allSettled, graceful degradation, retry with backoff, context-aware remediation, learning integration, and video accessibility. Uses 3-tier browser cascade: Vibium → agent-browser → Playwright+Stealth."
4
- category: specialized-testing
5
- priority: critical
6
- tokenEstimate: 10000
7
- agents: []
8
- implementation_status: active
9
- optimization_version: 7.0
10
- last_optimized: 2026-01-26
11
- dependencies: [playwright, playwright-extra, puppeteer-extra-plugin-stealth, "@axe-core/playwright", pa11y, lighthouse]
12
- quick_reference_card: true
13
- tags: [accessibility, wcag, a11y, video, captions, audiodesc, vtt, eu-compliance, context-aware, remediation, axe-core, pa11y, lighthouse, parallel, resilient, graceful-degradation, retry]
14
- ---
15
-
16
- # /a11y-ally - Comprehensive Accessibility Audit
17
-
18
- <default_to_action>
19
- When this skill is invoked with a URL, Claude executes ALL steps automatically without waiting for user prompts between steps.
20
-
21
- ## THIS IS AN LLM-POWERED SKILL
22
-
23
- The value of this skill is **Claude's intelligence**, not just running automated tools:
24
-
25
- | Automated Tools Do | Claude (This Skill) Does |
26
- |--------------------|--------------------------|
27
- | Flag "button has no name" | Analyze context: icon class, parent element, nearby text → generate "Add to wishlist" |
28
- | Flag "image missing alt" | Use Vision to see the image → describe actual content |
29
- | Flag "video has no captions" | Download video, extract frames, analyze each frame with Vision → generate real captions |
30
- | Output generic templates | Generate context-specific, copy-paste ready fixes |
31
-
32
- **IF YOU SKIP THE LLM ANALYSIS, THIS SKILL HAS NO VALUE.**
33
-
34
- ---
35
-
36
- ## EXECUTION MODEL
37
-
38
- **CLAUDE EXECUTES ALL STEPS WITHOUT STOPPING.**
39
-
40
- Do NOT wait for user prompts between steps. Execute the full pipeline:
41
-
42
- 1. **Data Collection**: Run multi-tool scan (axe-core, pa11y, Lighthouse) via Bash
43
- 2. **LLM Analysis**: Read results and analyze context for each violation
44
- 3. **Vision Pipeline**: If videos detected → download → extract frames → Read each frame → describe
45
- 4. **Intelligent Remediation**: Generate context-specific fixes using your reasoning
46
- 5. **Generate Reports**: Write all output files to `docs/accessibility-scans/{page-slug}/`
47
-
48
- **WRONG:**
49
- ```
50
- Claude: "I found 5 violations. Should I analyze them?"
51
- User: "Yes"
52
- Claude: "I see a video. Should I run the video pipeline?"
53
- User: "Yes"
54
- ```
55
-
56
- **RIGHT:**
57
- ```
58
- Claude: [Runs scan] [Analyzes violations] [Downloads video] → [Extracts frames] →
59
- [Reads each frame with Vision] → [Generates captions] → [Writes all files]
60
- "Audit complete. Generated 4 files in docs/accessibility-scans/example/"
61
- ```
62
-
63
- ---
64
-
65
- ## STEP 1: BROWSER AUTOMATION - Content Fetching
66
-
67
- ### 1.1: Try VIBIUM First (Primary)
68
- ```javascript
69
- ToolSearch("select:mcp__vibium__browser_launch")
70
- ToolSearch("select:mcp__vibium__browser_navigate")
71
- mcp__vibium__browser_launch({ headless: true })
72
- mcp__vibium__browser_navigate({ url: "TARGET_URL" })
73
- ```
74
-
75
- **If Vibium fails** → Go to STEP 1b
76
-
77
- ### 1b: Try AGENT-BROWSER Fallback
78
- ```javascript
79
- ToolSearch("select:mcp__claude-flow_alpha__browser_open")
80
- mcp__claude-flow_alpha__browser_open({ url: "TARGET_URL", waitUntil: "networkidle" })
81
- ```
82
-
83
- **If agent-browser fails** Go to STEP 1c
84
-
85
- ### 1c: PLAYWRIGHT + STEALTH (Final Fallback)
86
- ```bash
87
- mkdir -p /tmp/a11y-work && cd /tmp/a11y-work
88
- npm init -y 2>/dev/null
89
- npm install playwright-extra puppeteer-extra-plugin-stealth @axe-core/playwright pa11y lighthouse chrome-launcher 2>/dev/null
90
- ```
91
-
92
- Create and run scan script - see STEP 2 for full multi-tool scan code.
93
-
94
- ### 1d: PARALLEL MULTI-PAGE AUDIT (Optional)
95
-
96
- For auditing multiple URLs simultaneously, use parallel execution:
97
-
98
- ```javascript
99
- // /tmp/a11y-work/parallel-audit.js
100
- const { chromium } = require('playwright-extra');
101
- const stealth = require('puppeteer-extra-plugin-stealth')();
102
- const { AxeBuilder } = require('@axe-core/playwright');
103
-
104
- chromium.use(stealth);
105
-
106
- const MAX_CONCURRENT = 6; // Maximum parallel auditors
107
-
108
- async function auditUrl(browser, url) {
109
- const context = await browser.newContext();
110
- const page = await context.newPage();
111
-
112
- try {
113
- await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 });
114
- await page.waitForTimeout(2000);
115
-
116
- const axeResults = await new AxeBuilder({ page })
117
- .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22aa'])
118
- .analyze();
119
-
120
- return { url, success: true, violations: axeResults.violations };
121
- } catch (error) {
122
- return { url, success: false, error: error.message };
123
- } finally {
124
- await context.close();
125
- }
126
- }
127
-
128
- async function parallelAudit(urls) {
129
- const browser = await chromium.launch({ headless: true });
130
- const results = [];
131
-
132
- // Process in chunks of MAX_CONCURRENT
133
- for (let i = 0; i < urls.length; i += MAX_CONCURRENT) {
134
- const chunk = urls.slice(i, i + MAX_CONCURRENT);
135
- console.log(`Auditing batch ${Math.floor(i/MAX_CONCURRENT) + 1}: ${chunk.length} URLs`);
136
-
137
- const chunkResults = await Promise.all(
138
- chunk.map(url => auditUrl(browser, url))
139
- );
140
- results.push(...chunkResults);
141
- }
142
-
143
- await browser.close();
144
- return results;
145
- }
146
-
147
- // Usage: node parallel-audit.js url1 url2 url3 ...
148
- const urls = process.argv.slice(2);
149
- if (urls.length > 0) {
150
- parallelAudit(urls).then(results => {
151
- console.log(JSON.stringify(results, null, 2));
152
- });
153
- }
154
- ```
155
-
156
- **Usage for multi-page audit:**
157
- ```bash
158
- node parallel-audit.js https://example.com https://example.com/about https://example.com/contact
159
- ```
160
-
161
- ### 1e: SITE CRAWL MODE (Optional)
162
-
163
- For comprehensive site audits, crawl and audit all pages:
164
-
165
- ```javascript
166
- // /tmp/a11y-work/crawl-audit.js
167
- async function crawlAndAudit(startUrl, maxPages = 50) {
168
- const browser = await chromium.launch({ headless: true });
169
- const visited = new Set();
170
- const toVisit = [startUrl];
171
- const results = [];
172
- const baseUrl = new URL(startUrl).origin;
173
-
174
- while (toVisit.length > 0 && results.length < maxPages) {
175
- const url = toVisit.shift();
176
- if (visited.has(url)) continue;
177
- visited.add(url);
178
-
179
- console.log(`[${results.length + 1}/${maxPages}] Auditing: ${url}`);
180
-
181
- const context = await browser.newContext();
182
- const page = await context.newPage();
183
-
184
- try {
185
- await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 });
186
-
187
- // Extract same-domain links for crawling
188
- const links = await page.evaluate((base) => {
189
- return [...document.querySelectorAll('a[href]')]
190
- .map(a => a.href)
191
- .filter(href => href.startsWith(base) && !href.includes('#'))
192
- .filter(href => !href.match(/\.(pdf|jpg|png|gif|css|js)$/i));
193
- }, baseUrl);
194
-
195
- // Add new links to queue
196
- links.forEach(link => {
197
- if (!visited.has(link) && !toVisit.includes(link)) {
198
- toVisit.push(link);
199
- }
200
- });
201
-
202
- // Run accessibility audit
203
- const axeResults = await new AxeBuilder({ page })
204
- .withTags(['wcag2a', 'wcag2aa', 'wcag22aa'])
205
- .analyze();
206
-
207
- results.push({ url, violations: axeResults.violations });
208
- } catch (e) {
209
- results.push({ url, error: e.message });
210
- }
211
-
212
- await context.close();
213
- }
214
-
215
- await browser.close();
216
- return { pagesAudited: results.length, results };
217
- }
218
-
219
- // Usage: node crawl-audit.js https://example.com 50
220
- const [startUrl, maxPages] = process.argv.slice(2);
221
- crawlAndAudit(startUrl, parseInt(maxPages) || 50).then(r => console.log(JSON.stringify(r, null, 2)));
222
- ```
223
-
224
- ---
225
-
226
- ## STEP 2: COMPREHENSIVE WCAG SCAN (Multi-Tool, Parallel, Resilient)
227
-
228
- **IMPORTANT:** This step uses THREE accessibility testing tools for maximum coverage:
229
- - **axe-core**: Industry standard, excellent for ARIA and semantic issues
230
- - **pa11y**: Strong on contrast, links, and HTML validation
231
- - **Lighthouse**: Google's accessibility scoring with performance correlation
232
-
233
- Combined detection rate is ~15% higher than any single tool.
234
-
235
- ### 2.0: RESILIENCE ARCHITECTURE (v7.0 Enhancement)
236
-
237
- **Key improvements over v6.0:**
238
-
239
- | Feature | v6.0 (Old) | v7.0 (New) |
240
- |---------|------------|------------|
241
- | Tool execution | Sequential | **Parallel (Promise.allSettled)** |
242
- | Timeout handling | Global 60s | **Per-tool (60s/60s/90s)** |
243
- | Failure mode | All-or-nothing | **Graceful degradation** |
244
- | Retry logic | None | **Exponential backoff (3 retries)** |
245
- | Output style | Wait for all | **Progressive (stream as ready)** |
246
- | Minimum tools | 3 required | **1 of 3 sufficient** |
247
-
248
- **Coverage by tools succeeded:**
249
- - 3/3 tools: ~95% detection (optimal)
250
- - 2/3 tools: ~85% detection (good)
251
- - 1/3 tools: ~70% detection (acceptable)
252
- - 0/3 tools: FAIL - retry with different strategy
253
-
254
- ### 2.1: Run Multi-Tool Analysis (PARALLEL + RESILIENT)
255
- Create and run `/tmp/a11y-work/multi-tool-scan.js`:
256
- ```javascript
257
- const { chromium } = require('playwright-extra');
258
- const stealth = require('puppeteer-extra-plugin-stealth')();
259
- const { AxeBuilder } = require('@axe-core/playwright');
260
- const pa11y = require('pa11y');
261
- const lighthouse = require('lighthouse').default || require('lighthouse');
262
- const { launch: launchChrome } = require('chrome-launcher');
263
- const fs = require('fs');
264
-
265
- chromium.use(stealth);
266
-
267
- const TARGET_URL = process.argv[2] || 'TARGET_URL';
268
- const OUTPUT_FILE = '/tmp/a11y-work/scan-results.json';
269
- const SYSTEM_CHROMIUM = '/usr/bin/chromium';
270
-
271
- // ========== RESILIENCE UTILITIES ==========
272
-
273
- // Timeout wrapper - wraps any promise with a timeout
274
- function withTimeout(promise, ms, name) {
275
- return Promise.race([
276
- promise,
277
- new Promise((_, reject) =>
278
- setTimeout(() => reject(new Error(`${name} timed out after ${ms}ms`)), ms)
279
- )
280
- ]);
281
- }
282
-
283
- // Retry wrapper - retries with exponential backoff
284
- async function withRetry(fn, name, maxRetries = 3, baseDelay = 2000) {
285
- for (let attempt = 1; attempt <= maxRetries; attempt++) {
286
- try {
287
- return await fn();
288
- } catch (error) {
289
- const isLastAttempt = attempt === maxRetries;
290
- console.log(`[${name}] Attempt ${attempt}/${maxRetries} failed: ${error.message}`);
291
- if (isLastAttempt) throw error;
292
- const delay = baseDelay * Math.pow(2, attempt - 1); // Exponential backoff
293
- console.log(`[${name}] Retrying in ${delay}ms...`);
294
- await new Promise(r => setTimeout(r, delay));
295
- }
296
- }
297
- }
298
-
299
- // Sleep utility
300
- const sleep = (ms) => new Promise(r => setTimeout(r, ms));
301
-
302
- // Progressive output - append results as they arrive
303
- function progressiveOutput(tool, data) {
304
- console.log(`\n=== ${tool.toUpperCase()} COMPLETE ===`);
305
- console.log(JSON.stringify(data, null, 2));
306
-
307
- // Append to results file for progressive access
308
- try {
309
- let results = {};
310
- if (fs.existsSync(OUTPUT_FILE)) {
311
- results = JSON.parse(fs.readFileSync(OUTPUT_FILE, 'utf8'));
312
- }
313
- results[tool] = data;
314
- results.lastUpdated = new Date().toISOString();
315
- fs.writeFileSync(OUTPUT_FILE, JSON.stringify(results, null, 2));
316
- } catch (e) { /* ignore file errors */ }
317
- }
318
-
319
- // ========== TOOL RUNNERS ==========
320
-
321
- // TOOL 1: Axe-core (with page info extraction)
322
- async function runAxeCore(url) {
323
- console.log('[axe-core] Starting...');
324
- const browser = await chromium.launch({
325
- headless: true,
326
- executablePath: SYSTEM_CHROMIUM,
327
- args: [
328
- '--no-sandbox',
329
- '--disable-setuid-sandbox',
330
- '--disable-dev-shm-usage',
331
- '--disable-blink-features=AutomationControlled'
332
- ]
333
- });
334
-
335
- const context = await browser.newContext({
336
- userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
337
- locale: 'en-US',
338
- viewport: { width: 1920, height: 1080 }
339
- });
340
-
341
- const page = await context.newPage();
342
-
343
- try {
344
- // Use domcontentloaded (faster, more reliable than networkidle)
345
- await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 });
346
-
347
- // Random delay to appear human
348
- await sleep(2000 + Math.random() * 2000);
349
-
350
- // Try to dismiss cookie banners
351
- try {
352
- const cookieSelectors = [
353
- 'button:has-text("Accept")', 'button:has-text("Akzeptieren")',
354
- 'button:has-text("Alle akzeptieren")', '[data-testid="cookie-accept"]',
355
- '#onetrust-accept-btn-handler', '.cookie-consent-accept'
356
- ];
357
- for (const selector of cookieSelectors) {
358
- const btn = await page.$(selector);
359
- if (btn) { await btn.click(); await sleep(500); break; }
360
- }
361
- } catch (e) { /* ignore cookie errors */ }
362
-
363
- // Run axe-core analysis
364
- const axeResults = await new AxeBuilder({ page })
365
- .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22aa'])
366
- .analyze();
367
-
368
- // Extract comprehensive page info
369
- const pageInfo = await page.evaluate(() => ({
370
- title: document.title,
371
- url: window.location.href,
372
- lang: document.documentElement.lang,
373
- images: {
374
- total: document.querySelectorAll('img').length,
375
- withAlt: document.querySelectorAll('img[alt]').length,
376
- withoutAlt: document.querySelectorAll('img:not([alt])').length,
377
- emptyAlt: document.querySelectorAll('img[alt=""]').length
378
- },
379
- headings: {
380
- h1: Array.from(document.querySelectorAll('h1')).map(h => h.textContent.trim().slice(0,60)),
381
- h2: document.querySelectorAll('h2').length,
382
- h3: document.querySelectorAll('h3').length,
383
- total: document.querySelectorAll('h1,h2,h3,h4,h5,h6').length
384
- },
385
- forms: {
386
- total: document.querySelectorAll('form').length,
387
- inputs: document.querySelectorAll('input, select, textarea').length,
388
- buttons: document.querySelectorAll('button').length
389
- },
390
- links: { total: document.querySelectorAll('a').length },
391
- aria: {
392
- ariaLabels: document.querySelectorAll('[aria-label]').length,
393
- roles: document.querySelectorAll('[role]').length
394
- },
395
- landmarks: {
396
- main: document.querySelectorAll('main').length,
397
- nav: document.querySelectorAll('nav').length,
398
- header: document.querySelectorAll('header').length,
399
- footer: document.querySelectorAll('footer').length
400
- },
401
- media: {
402
- videos: document.querySelectorAll('video').length,
403
- iframes: document.querySelectorAll('iframe').length,
404
- videoUrls: Array.from(document.querySelectorAll('video')).map(v => {
405
- const src = v.src || (v.querySelector('source') ? v.querySelector('source').src : '');
406
- return {
407
- src: src,
408
- hasCaptions: !!v.querySelector('track[kind="captions"]')
409
- };
410
- })
411
- }
412
- }));
413
-
414
- const violations = axeResults.violations.map(v => ({
415
- tool: 'axe-core',
416
- id: v.id,
417
- impact: v.impact,
418
- description: v.description,
419
- help: v.help,
420
- helpUrl: v.helpUrl,
421
- tags: v.tags,
422
- nodeCount: v.nodes.length,
423
- nodes: v.nodes.slice(0, 5).map(n => ({
424
- html: n.html.slice(0, 200),
425
- target: n.target,
426
- failureSummary: n.failureSummary
427
- }))
428
- }));
429
-
430
- return {
431
- success: true,
432
- pageInfo,
433
- violations,
434
- passesCount: axeResults.passes.length
435
- };
436
- } finally {
437
- await context.close();
438
- await browser.close();
439
- }
440
- }
441
-
442
- // TOOL 2: Pa11y
443
- async function runPa11y(url) {
444
- console.log('[pa11y] Starting...');
445
- const results = await pa11y(url, {
446
- standard: 'WCAG2AA',
447
- timeout: 45000,
448
- wait: 2000,
449
- chromeLaunchConfig: {
450
- executablePath: SYSTEM_CHROMIUM,
451
- args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage']
452
- }
453
- });
454
-
455
- const violations = results.issues.map(issue => ({
456
- tool: 'pa11y',
457
- id: issue.code,
458
- impact: issue.type === 'error' ? 'serious' : issue.type === 'warning' ? 'moderate' : 'minor',
459
- description: issue.message,
460
- selector: issue.selector,
461
- context: (issue.context || '').slice(0, 200)
462
- }));
463
-
464
- return { success: true, violations, total: results.issues.length };
465
- }
466
-
467
- // TOOL 3: Lighthouse
468
- async function runLighthouse(url) {
469
- console.log('[lighthouse] Starting...');
470
- const chrome = await launchChrome({
471
- chromePath: SYSTEM_CHROMIUM,
472
- chromeFlags: ['--headless', '--no-sandbox', '--disable-gpu', '--disable-dev-shm-usage']
473
- });
474
-
475
- try {
476
- const result = await lighthouse(url, {
477
- port: chrome.port,
478
- onlyCategories: ['accessibility'],
479
- output: 'json'
480
- });
481
-
482
- const lhr = result.lhr;
483
- const score = Math.round(lhr.categories.accessibility.score * 100);
484
- const violations = Object.values(lhr.audits)
485
- .filter(audit => audit.score !== null && audit.score < 1)
486
- .map(audit => ({
487
- tool: 'lighthouse',
488
- id: audit.id,
489
- impact: audit.score === 0 ? 'critical' : audit.score < 0.5 ? 'serious' : 'moderate',
490
- score: audit.score,
491
- description: audit.title
492
- }));
493
-
494
- return { success: true, score, violations };
495
- } finally {
496
- await chrome.kill();
497
- }
498
- }
499
-
500
- // ========== MAIN: PARALLEL EXECUTION WITH GRACEFUL DEGRADATION ==========
501
-
502
- (async () => {
503
- console.log('=== MULTI-TOOL ACCESSIBILITY SCAN (v7.0 PARALLEL + RESILIENT) ===');
504
- console.log('Target:', TARGET_URL);
505
- console.log('Strategy: Promise.allSettled with per-tool timeouts\n');
506
-
507
- const startTime = Date.now();
508
-
509
- // Run ALL tools in PARALLEL with individual timeouts
510
- const [axeResult, pa11yResult, lighthouseResult] = await Promise.allSettled([
511
- withTimeout(
512
- withRetry(() => runAxeCore(TARGET_URL), 'axe-core', 2, 3000),
513
- 60000, 'axe-core'
514
- ),
515
- withTimeout(
516
- withRetry(() => runPa11y(TARGET_URL), 'pa11y', 2, 3000),
517
- 60000, 'pa11y'
518
- ),
519
- withTimeout(
520
- withRetry(() => runLighthouse(TARGET_URL), 'lighthouse', 2, 3000),
521
- 90000, 'lighthouse'
522
- )
523
- ]);
524
-
525
- // ========== PROCESS RESULTS (Graceful Degradation) ==========
526
- const results = {
527
- url: TARGET_URL,
528
- timestamp: new Date().toISOString(),
529
- duration: `${((Date.now() - startTime) / 1000).toFixed(1)}s`,
530
- toolsSucceeded: 0,
531
- toolsFailed: 0,
532
- pageInfo: null,
533
- violations: [],
534
- byTool: {}
535
- };
536
-
537
- // Process axe-core results
538
- if (axeResult.status === 'fulfilled') {
539
- results.toolsSucceeded++;
540
- results.pageInfo = axeResult.value.pageInfo;
541
- results.violations.push(...axeResult.value.violations);
542
- results.byTool['axe-core'] = {
543
- success: true,
544
- count: axeResult.value.violations.length,
545
- passes: axeResult.value.passesCount
546
- };
547
- progressiveOutput('axe-core', axeResult.value);
548
- } else {
549
- results.toolsFailed++;
550
- results.byTool['axe-core'] = { success: false, error: axeResult.reason.message };
551
- console.log('\n[axe-core] FAILED:', axeResult.reason.message);
552
- }
553
-
554
- // Process pa11y results
555
- if (pa11yResult.status === 'fulfilled') {
556
- results.toolsSucceeded++;
557
- results.violations.push(...pa11yResult.value.violations);
558
- results.byTool['pa11y'] = {
559
- success: true,
560
- count: pa11yResult.value.violations.length
561
- };
562
- progressiveOutput('pa11y', pa11yResult.value);
563
- } else {
564
- results.toolsFailed++;
565
- results.byTool['pa11y'] = { success: false, error: pa11yResult.reason.message };
566
- console.log('\n[pa11y] FAILED:', pa11yResult.reason.message);
567
- }
568
-
569
- // Process lighthouse results
570
- if (lighthouseResult.status === 'fulfilled') {
571
- results.toolsSucceeded++;
572
- results.violations.push(...lighthouseResult.value.violations);
573
- results.byTool['lighthouse'] = {
574
- success: true,
575
- score: lighthouseResult.value.score,
576
- count: lighthouseResult.value.violations.length
577
- };
578
- progressiveOutput('lighthouse', lighthouseResult.value);
579
- } else {
580
- results.toolsFailed++;
581
- results.byTool['lighthouse'] = { success: false, error: lighthouseResult.reason.message };
582
- console.log('\n[lighthouse] FAILED:', lighthouseResult.reason.message);
583
- }
584
-
585
- // ========== DEDUPLICATE VIOLATIONS ==========
586
- const seen = new Set();
587
- const uniqueViolations = [];
588
- for (const v of results.violations) {
589
- const key = (v.description || '').toLowerCase().slice(0, 50);
590
- if (!seen.has(key)) {
591
- seen.add(key);
592
- uniqueViolations.push(v);
593
- }
594
- }
595
- results.uniqueViolations = uniqueViolations;
596
- results.totalUnique = uniqueViolations.length;
597
-
598
- // ========== FINAL OUTPUT ==========
599
- console.log('\n' + '='.repeat(60));
600
- console.log('=== SCAN COMPLETE ===');
601
- console.log('='.repeat(60));
602
- console.log(`Tools succeeded: ${results.toolsSucceeded}/3`);
603
- console.log(`Tools failed: ${results.toolsFailed}/3`);
604
- console.log(`Duration: ${results.duration}`);
605
- console.log(`Total unique violations: ${results.totalUnique}`);
606
-
607
- if (results.toolsSucceeded === 0) {
608
- console.log('\n⚠️ ALL TOOLS FAILED - Consider:');
609
- console.log(' 1. Site may have strong bot protection');
610
- console.log(' 2. Try Vibium MCP browser instead');
611
- console.log(' 3. Check network connectivity');
612
- } else if (results.toolsSucceeded < 3) {
613
- console.log(`\n⚠️ Partial coverage (${results.toolsSucceeded}/3 tools) - Results still usable`);
614
- } else {
615
- console.log('\n✅ Full coverage achieved (3/3 tools)');
616
- }
617
-
618
- console.log('\n=== PAGE INFO ===');
619
- console.log(JSON.stringify(results.pageInfo, null, 2));
620
-
621
- console.log('\n=== VIOLATIONS BY TOOL ===');
622
- console.log(JSON.stringify(results.byTool, null, 2));
623
-
624
- console.log('\n=== UNIQUE VIOLATIONS ===');
625
- console.log(JSON.stringify(results.uniqueViolations, null, 2));
626
-
627
- // Save final results
628
- fs.writeFileSync(OUTPUT_FILE, JSON.stringify(results, null, 2));
629
- console.log(`\nResults saved to: ${OUTPUT_FILE}`);
630
- })();
631
- ```
632
-
633
- ### 2.2: Read Scan Results
634
-
635
- After running the scan, read the results file:
636
- ```bash
637
- cat /tmp/a11y-work/scan-results.json
638
- ```
639
-
640
- The results include:
641
- - **pageInfo**: Page structure, images, headings, media
642
- - **violations**: All violations from all tools (deduplicated)
643
- - **byTool**: Success/failure status per tool
644
- - **toolsSucceeded**: Number of tools that completed (1-3)
645
-
646
- ### 2.3: Graceful Degradation Decision Tree
647
-
648
- | Tools Succeeded | Action |
649
- |-----------------|--------|
650
- | **3/3** | Full coverage - proceed with all results |
651
- | **2/3** | ⚠️ Good coverage - note which tool failed in report |
652
- | **1/3** | ⚠️ Basic coverage - proceed but flag limited confidence |
653
- | **0/3** | ❌ Retry with Vibium MCP, or document failure |
654
-
655
- ### 2.4: MANDATORY - Check for Videos and Trigger Pipeline
656
-
657
- After reading scan results, check `pageInfo.media.videoUrls`:
658
-
659
- ```javascript
660
- // Check scan-results.json for videos
661
- const results = JSON.parse(fs.readFileSync('/tmp/a11y-work/scan-results.json'));
662
- if (results.pageInfo && results.pageInfo.media.videoUrls.length > 0) {
663
- console.log('=== VIDEOS DETECTED - TRIGGERING VIDEO PIPELINE ===');
664
- for (const video of results.pageInfo.media.videoUrls) {
665
- console.log(`Video: ${video.src}`);
666
- console.log(` Has captions: ${video.hasCaptions}`);
667
- }
668
- // PROCEED TO STEP 7 IMMEDIATELY
669
- }
670
- ```
671
-
672
- **IF videos detected AND hasCaptions=false → STEP 7 is MANDATORY before generating reports.**
673
-
674
- ---
675
-
676
- ## STEP 3: CONTEXT-AWARE REMEDIATION (LLM-POWERED)
677
-
678
- **THIS IS WHERE CLAUDE'S INTELLIGENCE MATTERS.**
679
-
680
- Generic tools output: `aria-label="[DESCRIPTION]"`
681
- You output: `aria-label="Add to shopping cart"` because you understand context.
682
-
683
- ### 3.1: Context Analysis (Use Your Reasoning)
684
-
685
- For EACH violation, Claude must:
686
-
687
- 1. **READ THE HTML CONTEXT** - Don't just see `<button class="btn">`, see:
688
- ```html
689
- <div class="product-card" data-product="Adidas Superstar">
690
- <img src="superstar.jpg" alt="White sneakers">
691
- <span class="price">$99</span>
692
- <button class="btn add-to-cart"> <!-- THIS IS THE VIOLATION -->
693
- <svg class="icon-cart">...</svg>
694
- </button>
695
- </div>
696
- ```
697
-
698
- 2. **INFER PURPOSE** from:
699
- - Class names: `add-to-cart`, `wishlist`, `menu-toggle`
700
- - Parent context: Inside `.product-card` with product data
701
- - Icon classes: `icon-cart`, `icon-heart`, `icon-search`
702
- - Nearby text: Product name, price, "Add to bag"
703
- - Page section: Header nav vs product grid vs checkout
704
-
705
- 3. **GENERATE SPECIFIC FIX**:
706
- ```html
707
- <!-- NOT THIS (generic template) -->
708
- <button aria-label="[DESCRIPTION]">
709
-
710
- <!-- THIS (context-aware) -->
711
- <button aria-label="Add Adidas Superstar to cart - $99">
712
- ```
713
-
714
- ### 3.2: Confidence Scoring
715
-
716
- Rate your confidence in each fix:
717
- - **0.9+**: Clear context (class="add-to-cart" near product name)
718
- - **0.7-0.9**: Reasonable inference (icon-cart class alone)
719
- - **<0.7**: Needs human review (ambiguous context)
720
-
721
- Include confidence in remediation.md:
722
- ```markdown
723
- ### Button: `.product-card .btn` (Confidence: 0.95)
724
- **Context:** Inside product card for "Adidas Superstar", has cart icon
725
- **Fix:** `aria-label="Add Adidas Superstar to cart"`
726
- ```
727
-
728
- ### 3.2: Remediation Templates by Violation Type
729
-
730
- **Form Labels (WCAG 1.3.1, 3.3.2, 4.1.2)**
731
- ```html
732
- <!-- Context: Input inside payment form, near "Card Number" text -->
733
- <!-- Confidence: 0.95 -->
734
-
735
- <!-- BEFORE -->
736
- <input type="text" name="cardNumber" placeholder="1234 5678 9012 3456">
737
-
738
- <!-- AFTER -->
739
- <label for="card-number">Credit Card Number</label>
740
- <input type="text"
741
- id="card-number"
742
- name="cardNumber"
743
- placeholder="1234 5678 9012 3456"
744
- aria-describedby="card-hint"
745
- autocomplete="cc-number"
746
- inputmode="numeric"
747
- pattern="[0-9\s]{13,19}">
748
- <span id="card-hint" class="visually-hidden">Enter 16-digit card number</span>
749
-
750
- <!-- RATIONALE -->
751
- - Visible label aids all users
752
- - aria-describedby provides additional context
753
- - autocomplete enables autofill
754
- - inputmode shows numeric keyboard on mobile
755
- - pattern enables browser validation
756
- ```
757
-
758
- **Icon Buttons (WCAG 4.1.2)**
759
- ```html
760
- <!-- Context: Button with SVG inside nav, classes include "menu-toggle" -->
761
- <!-- Confidence: 0.92 -->
762
-
763
- <!-- BEFORE -->
764
- <button class="menu-toggle">
765
- <svg>...</svg>
766
- </button>
767
-
768
- <!-- AFTER -->
769
- <button class="menu-toggle"
770
- type="button"
771
- aria-expanded="false"
772
- aria-controls="main-menu"
773
- aria-label="Open navigation menu">
774
- <svg aria-hidden="true" focusable="false">...</svg>
775
- </button>
776
-
777
- <!-- RATIONALE -->
778
- - aria-label describes action, not icon
779
- - aria-expanded communicates state
780
- - aria-controls links to menu element
781
- - SVG hidden from assistive tech (decorative)
782
- ```
783
-
784
- **Color Contrast (WCAG 1.4.3)**
785
- ```html
786
- <!-- Context: Gray text (#767676) on white background -->
787
- <!-- Current ratio: 4.48:1 (FAILS AA for normal text) -->
788
- <!-- Required: 4.5:1 (AA) or 7:1 (AAA) -->
789
-
790
- <!-- BEFORE -->
791
- .low-contrast { color: #767676; background: #ffffff; }
792
-
793
- <!-- AFTER (Option 1: Darken text - minimal change) -->
794
- .accessible { color: #757575; background: #ffffff; } /* 4.6:1 - PASSES AA */
795
-
796
- <!-- AFTER (Option 2: Higher contrast for AAA) -->
797
- .high-contrast { color: #595959; background: #ffffff; } /* 7.0:1 - PASSES AAA */
798
-
799
- <!-- COLOR ALTERNATIVES -->
800
- | Original | AA Pass | AAA Pass | Notes |
801
- |----------|---------|----------|-------|
802
- | #767676 | #757575 | #595959 | Gray text |
803
- | #0066cc | #0055b3 | #003d82 | Link blue |
804
- | #cc0000 | #b30000 | #8b0000 | Error red |
805
- ```
806
-
807
- **Heading Hierarchy (WCAG 1.3.1)**
808
- ```html
809
- <!-- Context: Page has 10 H1 elements, skipped H2 levels -->
810
-
811
- <!-- BEFORE (broken) -->
812
- <h1>Welcome</h1>
813
- <h1>Products</h1> <!-- ERROR: Multiple H1s -->
814
- <h4>Shoes</h4> <!-- ERROR: Skipped H2, H3 -->
815
- <h1>Contact</h1>
816
-
817
- <!-- AFTER (correct) -->
818
- <h1>Site Name - Main Page Title</h1>
819
- <main>
820
- <section aria-labelledby="products-heading">
821
- <h2 id="products-heading">Products</h2>
822
- <h3>Shoes</h3>
823
- <h3>Clothing</h3>
824
- </section>
825
- <section aria-labelledby="contact-heading">
826
- <h2 id="contact-heading">Contact</h2>
827
- </section>
828
- </main>
829
-
830
- <!-- HEADING STRUCTURE VISUALIZATION -->
831
- h1: Site Name - Main Page Title
832
- ├── h2: Products
833
- │ ├── h3: Shoes
834
- │ └── h3: Clothing
835
- └── h2: Contact
836
- ```
837
-
838
- **Skip Links (WCAG 2.4.1)**
839
- ```html
840
- <!-- Add as FIRST element inside <body> -->
841
- <body>
842
- <a href="#main-content" class="skip-link">Skip to main content</a>
843
- <a href="#main-nav" class="skip-link">Skip to navigation</a>
844
-
845
- <header>
846
- <nav id="main-nav" aria-label="Main navigation">...</nav>
847
- </header>
848
-
849
- <main id="main-content" tabindex="-1">
850
- <!-- Main content -->
851
- </main>
852
- </body>
853
-
854
- <style>
855
- .skip-link {
856
- position: absolute;
857
- top: -100%;
858
- left: 16px;
859
- background: #000;
860
- color: #fff;
861
- padding: 12px 24px;
862
- z-index: 10000;
863
- text-decoration: none;
864
- font-weight: bold;
865
- border-radius: 0 0 4px 4px;
866
- transition: top 0.2s;
867
- }
868
- .skip-link:focus {
869
- top: 0;
870
- outline: 3px solid #ffcc00;
871
- outline-offset: 2px;
872
- }
873
- </style>
874
- ```
875
-
876
- **Focus Indicators (WCAG 2.4.7)**
877
- ```css
878
- /* NEVER do this */
879
- *:focus { outline: none; } /* WCAG FAIL */
880
-
881
- /* DO THIS - Custom focus styles */
882
- :focus-visible {
883
- outline: 3px solid #005fcc;
884
- outline-offset: 2px;
885
- }
886
-
887
- /* Remove outline only for mouse users */
888
- :focus:not(:focus-visible) {
889
- outline: none;
890
- }
891
-
892
- /* High contrast for interactive elements */
893
- a:focus-visible,
894
- button:focus-visible,
895
- input:focus-visible,
896
- select:focus-visible,
897
- textarea:focus-visible,
898
- [role="button"]:focus-visible {
899
- outline: 3px solid #005fcc;
900
- outline-offset: 2px;
901
- box-shadow: 0 0 0 6px rgba(0, 95, 204, 0.2);
902
- }
903
-
904
- /* Dark backgrounds need light focus */
905
- .dark-bg :focus-visible {
906
- outline-color: #ffffff;
907
- box-shadow: 0 0 0 6px rgba(255, 255, 255, 0.3);
908
- }
909
- ```
910
-
911
- **Keyboard Navigation (WCAG 2.1.1, 2.1.2)**
912
- ```html
913
- <!-- Custom interactive element needs keyboard support -->
914
-
915
- <!-- BEFORE (inaccessible) -->
916
- <div class="dropdown" onclick="toggleMenu()">
917
- Menu
918
- </div>
919
-
920
- <!-- AFTER (accessible) -->
921
- <button type="button"
922
- class="dropdown-trigger"
923
- aria-expanded="false"
924
- aria-controls="dropdown-menu"
925
- onclick="toggleMenu()"
926
- onkeydown="handleKeydown(event)">
927
- Menu
928
- </button>
929
- <ul id="dropdown-menu" role="menu" hidden>
930
- <li role="none"><a role="menuitem" href="/page1">Page 1</a></li>
931
- <li role="none"><a role="menuitem" href="/page2">Page 2</a></li>
932
- </ul>
933
-
934
- <script>
935
- function handleKeydown(event) {
936
- switch(event.key) {
937
- case 'Enter':
938
- case ' ':
939
- event.preventDefault();
940
- toggleMenu();
941
- break;
942
- case 'Escape':
943
- closeMenu();
944
- break;
945
- case 'ArrowDown':
946
- event.preventDefault();
947
- focusFirstMenuItem();
948
- break;
949
- }
950
- }
951
- </script>
952
- ```
953
-
954
- **Modal Focus Trap (WCAG 2.4.3)**
955
- ```javascript
956
- // Focus trap for modals - REQUIRED for WCAG compliance
957
- function trapFocus(modal) {
958
- const focusable = modal.querySelectorAll(
959
- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
960
- );
961
- const first = focusable[0];
962
- const last = focusable[focusable.length - 1];
963
-
964
- // Focus first element when modal opens
965
- first?.focus();
966
-
967
- modal.addEventListener('keydown', (e) => {
968
- if (e.key === 'Tab') {
969
- if (e.shiftKey && document.activeElement === first) {
970
- e.preventDefault();
971
- last.focus();
972
- } else if (!e.shiftKey && document.activeElement === last) {
973
- e.preventDefault();
974
- first.focus();
975
- }
976
- }
977
- if (e.key === 'Escape') {
978
- closeModal();
979
- }
980
- });
981
- }
982
-
983
- // Return focus when modal closes
984
- function closeModal() {
985
- modal.hidden = true;
986
- triggerButton.focus(); // Return focus to trigger
987
- }
988
- ```
989
-
990
- **iframe Titles (WCAG 4.1.2)**
991
- ```html
992
- <!-- All iframes MUST have descriptive titles -->
993
- <iframe src="map.html" title="Store location map showing 5 nearby stores"></iframe>
994
- <iframe src="video.html" title="Product demonstration video with captions"></iframe>
995
- <iframe src="chat.html" title="Customer support chat window"></iframe>
996
- ```
997
-
998
- ---
999
-
1000
- ## STEP 4: USER IMPACT ANALYSIS
1001
-
1002
- For each violation, calculate user impact:
1003
-
1004
- ### 4.1: Affected User Groups
1005
- | Violation Type | Affected Groups | % of Users |
1006
- |----------------|-----------------|------------|
1007
- | Missing alt text | Blind, low-vision | 7-10% |
1008
- | Missing form labels | Blind, screen reader users | 5-8% |
1009
- | Low color contrast | Low-vision, color blind | 8-12% |
1010
- | No keyboard access | Motor impaired, power users | 10-15% |
1011
- | Missing captions | Deaf, hard-of-hearing | 5-7% |
1012
- | Flashing content | Seizure sensitive | 0.5-1% |
1013
- | Complex language | Cognitive impairment | 10-15% |
1014
-
1015
- ### 4.2: Impact Severity Classification
1016
- ```
1017
- BLOCKS-USAGE: User cannot complete task at all
1018
- - Missing form labels on required fields
1019
- - Keyboard traps
1020
- - Critical buttons without accessible names
1021
-
1022
- IMPAIRS-USAGE: User can complete task with difficulty
1023
- - Low contrast (can read with effort)
1024
- - Missing skip links (tedious navigation)
1025
- - Incorrect heading structure (confusing)
1026
-
1027
- MINOR-INCONVENIENCE: Suboptimal but functional
1028
- - Empty alt on decorative images
1029
- - Redundant ARIA
1030
- - Non-semantic HTML that works
1031
- ```
1032
-
1033
- ---
1034
-
1035
- ## STEP 5: ROI-BASED PRIORITIZATION
1036
-
1037
- Calculate priority for each remediation:
1038
-
1039
- ### 5.1: Priority Formula
1040
- ```
1041
- PRIORITY_SCORE = (IMPACT_WEIGHT × USERS_AFFECTED) / EFFORT_HOURS
1042
-
1043
- Where:
1044
- - IMPACT_WEIGHT: Critical=10, Serious=7, Moderate=4, Minor=1
1045
- - USERS_AFFECTED: Estimated % of users impacted
1046
- - EFFORT_HOURS: Estimated fix time (0.25 to 8 hours)
1047
- ```
1048
-
1049
- ### 5.2: Effort Estimation Guide
1050
- | Fix Type | Effort | Complexity |
1051
- |----------|--------|------------|
1052
- | Add aria-label | 0.25h | Trivial |
1053
- | Add alt text | 0.25h | Trivial |
1054
- | Add form label | 0.5h | Simple |
1055
- | Fix color contrast | 0.5h | Simple |
1056
- | Add skip links | 1h | Simple |
1057
- | Fix heading structure | 2h | Medium |
1058
- | Add keyboard navigation | 4h | High |
1059
- | Implement focus trap | 4h | High |
1060
- | Add video captions | 8h | High |
1061
-
1062
- ### 5.3: Priority Output Format
1063
- ```
1064
- | Rank | Violation | Impact | Users | Effort | ROI Score |
1065
- |------|-----------|--------|-------|--------|-----------|
1066
- | 1 | Form labels missing | Critical | 15% | 0.5h | 300 |
1067
- | 2 | Keyboard trap | Critical | 12% | 4h | 30 |
1068
- | 3 | Low contrast | Serious | 10% | 0.5h | 140 |
1069
- | 4 | Missing alt text | Serious | 8% | 0.25h | 224 |
1070
- ```
1071
-
1072
- ---
1073
-
1074
- ## STEP 6: PRODUCTION READINESS ASSESSMENT
1075
-
1076
- ### 6.1: Compliance Scoring
1077
- ```
1078
- COMPLIANCE_SCORE = (PASSED_CRITERIA / TOTAL_CRITERIA) × 100
1079
-
1080
- Production Ready if:
1081
- ✓ Score ≥ 85%
1082
- Zero critical violations
1083
- ✓ Fewer than 3 serious violations
1084
- All user journeys keyboard accessible
1085
- ```
1086
-
1087
- ### 6.2: POUR Analysis (Perceivable, Operable, Understandable, Robust)
1088
- ```
1089
- | Principle | Guidelines | Pass | Fail | Score |
1090
- |-----------|-----------|------|------|-------|
1091
- | Perceivable | 1.1-1.4 | 12 | 3 | 80% |
1092
- | Operable | 2.1-2.5 | 18 | 2 | 90% |
1093
- | Understandable | 3.1-3.3 | 8 | 1 | 89% |
1094
- | Robust | 4.1 | 4 | 1 | 80% |
1095
- | **TOTAL** | | **42** | **7** | **86%** |
1096
- ```
1097
-
1098
- ---
1099
-
1100
- ## STEP 7: VIDEO ACCESSIBILITY PIPELINE
1101
-
1102
- **Execute for EACH video detected on page.**
1103
-
1104
- ### 7.1: Detect and Extract Video URLs (MANDATORY)
1105
-
1106
- **This step MUST be integrated into STEP 2 multi-tool scan.**
1107
-
1108
- Add this to the page.evaluate() in the multi-tool scan:
1109
- ```javascript
1110
- // In pageInfo extraction (STEP 2), add:
1111
- videos: {
1112
- elements: [...document.querySelectorAll('video')].map(v => ({
1113
- src: v.src || v.querySelector('source')?.src,
1114
- fullUrl: new URL(v.src || v.querySelector('source')?.src || '', window.location.href).href,
1115
- poster: v.poster,
1116
- hasCaptions: v.querySelector('track[kind="captions"]') !== null,
1117
- hasDescriptions: v.querySelector('track[kind="descriptions"]') !== null,
1118
- duration: v.duration || 'unknown',
1119
- autoplay: v.autoplay,
1120
- muted: v.muted
1121
- })),
1122
- iframes: [...document.querySelectorAll('iframe')].map(iframe => {
1123
- const src = iframe.src;
1124
- const isVideo = /youtube|vimeo|dailymotion|wistia/.test(src);
1125
- return isVideo ? { src, platform: src.match(/(youtube|vimeo|dailymotion|wistia)/)?.[1] } : null;
1126
- }).filter(Boolean)
1127
- }
1128
- ```
1129
-
1130
- **MANDATORY OUTPUT:** Log all video URLs found:
1131
- ```
1132
- === VIDEOS DETECTED ===
1133
- Video 1: https://example.com/promo.mp4 (no captions, no descriptions)
1134
- YouTube iframe: https://youtube.com/embed/xxx
1135
- ```
1136
-
1137
- ### 7.2: Download and Extract Frames (MANDATORY for each video)
1138
-
1139
- **For EACH video URL found in 7.1:**
1140
-
1141
- ```bash
1142
- # Create output directory
1143
- mkdir -p /tmp/a11y-work/frames
1144
-
1145
- # Download video (with retry and user-agent)
1146
- curl -L -A "Mozilla/5.0" --retry 3 -o /tmp/a11y-work/video.mp4 "FULL_VIDEO_URL"
1147
-
1148
- # Verify download succeeded
1149
- if [ -f /tmp/a11y-work/video.mp4 ] && [ -s /tmp/a11y-work/video.mp4 ]; then
1150
- echo "Video downloaded successfully"
1151
- ffmpeg -i /tmp/a11y-work/video.mp4 -vf "fps=1/3" -frames:v 10 /tmp/a11y-work/frames/frame_%02d.jpg 2>/dev/null
1152
- echo "Extracted $(ls /tmp/a11y-work/frames/*.jpg 2>/dev/null | wc -l) frames"
1153
- else
1154
- echo "VIDEO DOWNLOAD FAILED - Document this in audit-summary.md"
1155
- fi
1156
- ```
1157
-
1158
- **IF VIDEO DOWNLOAD FAILS:**
1159
- 1. Document the failure reason in audit-summary.md
1160
- 2. Still create video-captions violation in violations.json
1161
- 3. Add remediation instructions WITHOUT generated captions
1162
- 4. Mark video pipeline as "blocked" not "skipped"
1163
-
1164
- ### 7.3: Analyze Each Frame with Claude Vision (MANDATORY)
1165
-
1166
- **USE THE READ TOOL ON EACH FRAME IMAGE.**
1167
-
1168
- Claude Code has native vision capabilities. When you Read an image file, you SEE it.
1169
-
1170
- ```
1171
- Read /tmp/a11y-work/frames/frame_01.jpg
1172
- Read /tmp/a11y-work/frames/frame_02.jpg
1173
- Read /tmp/a11y-work/frames/frame_03.jpg
1174
- ... (continue for all frames)
1175
- ```
1176
-
1177
- **For EACH frame, describe:**
1178
- - **SCENE**: Setting, environment, lighting, location
1179
- - **PEOPLE**: Who appears, what they're doing, expressions, clothing
1180
- - **PRODUCTS**: Items shown (for e-commerce: product names, colors, styles)
1181
- - **TEXT**: Any visible text, logos, signs, prices
1182
- - **ACTION**: Movement, transitions, what's happening
1183
-
1184
- **Example output after reading frame_01.jpg:**
1185
- ```
1186
- Frame 1 (0:00-0:03): A woman in white Adidas sneakers running on a forest trail.
1187
- Morning light filters through trees. She wears black athletic leggings and a
1188
- gray tank top. The Adidas three-stripe logo is visible on her shoes.
1189
- ```
1190
-
1191
- **THIS IS THE LLM VALUE.** Generic tools output "[DESCRIBE CONTENT]".
1192
- You output actual descriptions because you can SEE the image.
1193
-
1194
- ---
1195
-
1196
- **FALLBACK: If Read tool fails on images**
1197
-
1198
- Try Anthropic API directly:
1199
- ```javascript
1200
- const Anthropic = require('@anthropic-ai/sdk');
1201
- const fs = require('fs');
1202
- const client = new Anthropic();
1203
-
1204
- const imageData = fs.readFileSync('/tmp/a11y-work/frames/frame_01.jpg').toString('base64');
1205
- const response = await client.messages.create({
1206
- model: 'claude-sonnet-4-20250514',
1207
- max_tokens: 500,
1208
- messages: [{
1209
- role: 'user',
1210
- content: [
1211
- { type: 'image', source: { type: 'base64', media_type: 'image/jpeg', data: imageData } },
1212
- { type: 'text', text: 'Describe this video frame for accessibility captions.' }
1213
- ]
1214
- }]
1215
- });
1216
- ```
1217
-
1218
- **LAST RESORT: Context-Based Inference (No Vision)**
1219
- If vision completely unavailable, infer from:
1220
- - Video filename: "product-demo.mp4" → product demonstration
1221
- - Page context: product page → product showcase
1222
- - Surrounding text: nearby headings and descriptions
1223
-
1224
- **Document for each frame:**
1225
- - **SCENE**: Setting, environment, lighting
1226
- - **PEOPLE**: Who, actions, expressions, clothing
1227
- - **OBJECTS**: Products, props, equipment
1228
- - **TEXT**: Visible text, logos, signs
1229
- - **ACTION**: Movement, transitions
1230
- - **COLORS**: Dominant colors, accessibility-relevant
1231
-
1232
- ### 7.4: Generate WebVTT Captions
1233
- ```vtt
1234
- WEBVTT
1235
- Kind: captions
1236
- Language: {detected-language}
1237
-
1238
- 00:00:00.000 --> 00:00:03.000
1239
- [Description from frame_01 analysis]
1240
-
1241
- 00:00:03.000 --> 00:00:06.000
1242
- [Description from frame_02 analysis]
1243
- ```
1244
-
1245
- ### 7.5: Generate Audio Descriptions
1246
- ```vtt
1247
- WEBVTT
1248
- Kind: descriptions
1249
- Language: en
1250
-
1251
- 00:00:00.000 --> 00:00:03.000
1252
- SCENE: [Detailed scene for blind users]
1253
- VISUAL: [What's on screen]
1254
- TEXT: [Any readable text]
1255
- ACTION: [What's happening]
1256
- ```
1257
-
1258
- ---
1259
-
1260
- ## STEP 8: GENERATE COMPREHENSIVE REPORTS
1261
-
1262
- ### 8.1: Required Output Files
1263
- Save ALL files to `docs/accessibility-scans/{page-slug}/`:
1264
-
1265
- | File | Contents |
1266
- |------|----------|
1267
- | `audit-summary.md` | Executive summary, scores, top issues, user impact |
1268
- | `remediation.md` | **ALL copy-paste code fixes** with context |
1269
- | `violations.json` | Machine-readable violation data |
1270
- | `implementation.md` | Video integration guide (if videos) |
1271
- | `*.vtt` | Caption and audio description files |
1272
-
1273
- ### 8.2: audit-summary.md Template
1274
- ```markdown
1275
- # Accessibility Audit Report: {Site Name}
1276
-
1277
- **URL:** {url}
1278
- **Date:** {date}
1279
- **Standard:** WCAG 2.2 Level AA
1280
-
1281
- ## Executive Summary
1282
-
1283
- | Metric | Value |
1284
- |--------|-------|
1285
- | **Compliance Score** | {score}% |
1286
- | **Production Ready** | {Yes/No} |
1287
- | **Critical Issues** | {count} |
1288
- | **Total Violations** | {count} |
1289
- | **Estimated Fix Time** | {hours}h |
1290
-
1291
- ## POUR Analysis
1292
- {table}
1293
-
1294
- ## Top 10 Issues by Priority
1295
- {priority table with ROI scores}
1296
-
1297
- ## User Impact Summary
1298
- {affected user groups and percentages}
1299
-
1300
- ## Recommendations
1301
- {prioritized action items}
1302
- ```
1303
-
1304
- ### 8.3: remediation.md Template
1305
- ```markdown
1306
- # Accessibility Remediation Guide: {Site Name}
1307
-
1308
- ## Quick Wins (Copy-Paste Ready)
1309
-
1310
- ### 1. Form Labels ({count} issues)
1311
- {For EACH unlabeled input: context, before/after code, rationale, confidence}
1312
-
1313
- ### 2. Heading Structure ({count} issues)
1314
- {Current structure visualization, fixed structure, code changes}
1315
-
1316
- ### 3. Color Contrast ({count} issues)
1317
- {For EACH: current colors, ratio, suggested colors, CSS fixes}
1318
-
1319
- ### 4. Missing Alt Text ({count} issues)
1320
- {For EACH image: context-inferred alt text suggestions}
1321
-
1322
- ### 5. Keyboard Navigation ({count} issues)
1323
- {For EACH: element, issue, fix code, test instructions}
1324
-
1325
- ### 6. Focus Indicators
1326
- {Global CSS to add}
1327
-
1328
- ### 7. Skip Links
1329
- {Full HTML + CSS to add}
1330
-
1331
- ### 8. ARIA Fixes ({count} issues)
1332
- {For EACH: context, specific aria attributes to add}
1333
-
1334
- ### 9. iframe Titles ({count} issues)
1335
- {For EACH: suggested title based on content}
1336
-
1337
- ### 10. Video Accessibility ({count} videos)
1338
- {Links to generated VTT files, implementation code}
1339
-
1340
- ## Testing Checklist
1341
- - [ ] Tab through entire page - all interactive elements reachable
1342
- - [ ] Screen reader announces all content correctly
1343
- - [ ] Color contrast passes (use axe DevTools)
1344
- - [ ] Works without mouse
1345
- - [ ] Works at 200% zoom
1346
- - [ ] Video captions synchronized and accurate
1347
- ```
1348
-
1349
- ---
1350
-
1351
- ## STEP 9: LEARNING PROTOCOL (When MCP Available)
1352
-
1353
- Integrate with the learning system to improve over time.
1354
-
1355
- ### 9.1: Query Previous Patterns BEFORE Audit
1356
-
1357
- Check if similar sites were audited before:
1358
- ```javascript
1359
- // Load MCP tools
1360
- ToolSearch("select:mcp__claude-flow_alpha__memory_retrieve")
1361
- ToolSearch("select:mcp__claude-flow_alpha__hooks_intelligence_pattern_search")
1362
-
1363
- // Retrieve domain-specific patterns
1364
- mcp__claude-flow_alpha__memory_retrieve({
1365
- key: `accessibility/patterns/${domain}`,
1366
- namespace: "learning"
1367
- })
1368
-
1369
- // Search for similar violation patterns
1370
- mcp__claude-flow_alpha__hooks_intelligence_pattern_search({
1371
- query: "accessibility remediation",
1372
- type: "accessibility-fix",
1373
- limit: 10
1374
- })
1375
- ```
1376
-
1377
- ### 9.2: Store Successful Patterns AFTER Audit
1378
-
1379
- Store patterns that worked for future reuse:
1380
- ```javascript
1381
- ToolSearch("select:mcp__claude-flow_alpha__memory_store")
1382
- ToolSearch("select:mcp__claude-flow_alpha__hooks_intelligence_pattern_store")
1383
-
1384
- // Store audit outcome
1385
- mcp__claude-flow_alpha__memory_store({
1386
- key: `accessibility-audit/${domain}-${Date.now()}`,
1387
- namespace: "learning",
1388
- value: {
1389
- url: auditedUrl,
1390
- timestamp: new Date().toISOString(),
1391
- violationsFound: violations.length,
1392
- criticalCount: violations.filter(v => v.impact === 'critical').length,
1393
- toolsUsed: ['axe-core', 'pa11y', 'lighthouse'],
1394
- patterns: {
1395
- commonViolations: extractTopViolationTypes(violations),
1396
- effectiveFixes: extractFixesThatWorked(remediations)
1397
- }
1398
- }
1399
- })
1400
-
1401
- // Store reusable remediation patterns
1402
- mcp__claude-flow_alpha__hooks_intelligence_pattern_store({
1403
- pattern: "form-label-contextual-fix",
1404
- confidence: 0.92,
1405
- type: "accessibility-remediation",
1406
- metadata: {
1407
- wcagCriteria: "1.3.1, 3.3.2, 4.1.2",
1408
- violationType: "missing-form-label",
1409
- codeTemplate: "<label for=\"{id}\">{inferredLabel}</label>",
1410
- contextSignals: ["placeholder", "nearby-text", "field-name"]
1411
- }
1412
- })
1413
- ```
1414
-
1415
- ### 9.3: Calculate Audit Quality Score
1416
-
1417
- Self-assess audit completeness (for learning feedback):
1418
-
1419
- | Criteria | Points | Your Score |
1420
- |----------|--------|------------|
1421
- | Multi-tool testing used (3 tools) | 20 | |
1422
- | All WCAG 2.2 AA criteria checked | 15 | |
1423
- | Context-aware fixes generated | 20 | |
1424
- | Confidence scores included | 10 | |
1425
- | ROI prioritization calculated | 10 | |
1426
- | Video pipeline completed (if applicable) | 15 | |
1427
- | EU compliance mapping included | 10 | |
1428
- | **Total** | **100** | |
1429
-
1430
- **Quality Levels:**
1431
- - 90-100: Excellent (1.0 reward)
1432
- - 70-89: Good (0.8 reward)
1433
- - 50-69: Acceptable (0.5 reward)
1434
- - <50: Incomplete (0.0 reward - redo required)
1435
-
1436
- ---
1437
-
1438
- ## STEP 10: SCREEN READER TESTING GUIDE
1439
-
1440
- Manual testing instructions (cannot be fully automated):
1441
-
1442
- ### 10.1: NVDA (Windows - Free)
1443
- ```
1444
- 1. Download: https://www.nvaccess.org/download/
1445
- 2. Install and start NVDA (Ctrl+Alt+N)
1446
- 3. Navigate to audited page
1447
-
1448
- Key Commands:
1449
- - H: Jump through headings
1450
- - F: Jump through form fields
1451
- - B: Jump through buttons
1452
- - T: Jump through tables
1453
- - K: Jump through links
1454
- - D: Jump through landmarks
1455
- - Tab: Move through focusable elements
1456
-
1457
- Verify:
1458
- - [ ] All headings announced with correct level
1459
- - [ ] Form fields announce labels
1460
- - [ ] Buttons announce purpose
1461
- - [ ] Images announce alt text or "decorative"
1462
- - [ ] Dynamic content changes announced (aria-live)
1463
- ```
1464
-
1465
- ### 10.2: VoiceOver (macOS - Built-in)
1466
- ```
1467
- 1. Enable: System Preferences Accessibility VoiceOver
1468
- 2. Toggle: Cmd+F5
1469
- 3. Navigate to audited page
1470
-
1471
- Key Commands:
1472
- - VO+U: Open rotor (headings, links, forms, landmarks)
1473
- - VO+Space: Activate element
1474
- - VO+Right/Left: Move through content
1475
- - VO+Cmd+H: Jump to next heading
1476
-
1477
- Verify:
1478
- - [ ] Rotor shows all headings hierarchically
1479
- - [ ] Forms are navigable and labels announced
1480
- - [ ] Focus order matches visual order
1481
- - [ ] All content is reachable
1482
- ```
1483
-
1484
- ### 10.3: JAWS (Windows - Commercial)
1485
- ```
1486
- 1. Trial: https://www.freedomscientific.com/products/software/jaws/
1487
- 2. Start JAWS and navigate to page
1488
-
1489
- Key Commands:
1490
- - H: Next heading
1491
- - F: Next form field
1492
- - B: Next button
1493
- - T: Next table
1494
- - Ins+F6: Heading list
1495
- - Ins+F7: Link list
1496
-
1497
- Verify:
1498
- - [ ] Virtual cursor mode works correctly
1499
- - [ ] Forms mode activates in forms
1500
- - [ ] All ARIA roles announced properly
1501
- ```
1502
-
1503
- ### 10.4: Screen Reader Testing Checklist
1504
-
1505
- | Test | NVDA | VoiceOver | JAWS |
1506
- |------|------|-----------|------|
1507
- | Headings hierarchy correct | [ ] | [ ] | [ ] |
1508
- | Form labels announced | [ ] | [ ] | [ ] |
1509
- | Button purposes clear | [ ] | [ ] | [ ] |
1510
- | Image alt text correct | [ ] | [ ] | [ ] |
1511
- | Links announce destination | [ ] | [ ] | [ ] |
1512
- | Landmarks navigable | [ ] | [ ] | [ ] |
1513
- | Focus order logical | [ ] | [ ] | [ ] |
1514
- | Dynamic updates announced | [ ] | [ ] | [ ] |
1515
- | No keyboard traps | [ ] | [ ] | [ ] |
1516
- | Skip links work | [ ] | [ ] | [ ] |
1517
-
1518
- ---
1519
-
1520
- ## VALIDATION CHECKLIST
1521
-
1522
- Before completing, verify ALL items:
1523
-
1524
- ### Content Fetching (v7.0 Resilient)
1525
- - [ ] Browser launched (Vibium/agent-browser/Playwright)
1526
- - [ ] Page loaded and analyzed
1527
- - [ ] Multi-tool scan ran with **parallel execution**
1528
- - [ ] At least **1 of 3 tools succeeded** (graceful degradation)
1529
- - [ ] If tools failed, documented **which tools and why**
1530
- - [ ] Results saved to `/tmp/a11y-work/scan-results.json`
1531
-
1532
- ### Violation Analysis
1533
- - [ ] All violations extracted with WCAG criteria
1534
- - [ ] Context analyzed for each violation
1535
- - [ ] User impact calculated
1536
-
1537
- ### Remediation Generation
1538
- - [ ] Form label fixes with context and rationale
1539
- - [ ] Heading hierarchy fix with visualization
1540
- - [ ] Color contrast fixes with hex codes
1541
- - [ ] Alt text suggestions with confidence scores
1542
- - [ ] Skip link code (full HTML + CSS)
1543
- - [ ] Focus indicator CSS
1544
- - [ ] ARIA fixes with specific attributes
1545
- - [ ] Keyboard navigation fixes
1546
- - [ ] iframe titles
1547
-
1548
- ### Video Accessibility (MANDATORY if pageInfo.media.videos > 0)
1549
- - [ ] Video URLs extracted (full URLs, not relative)
1550
- - [ ] Download attempted for EACH video
1551
- - [ ] IF download succeeded: Frames extracted with ffmpeg
1552
- - [ ] IF download succeeded: Each frame analyzed with Read tool
1553
- - [ ] IF download succeeded: captions.vtt generated from ACTUAL frame descriptions
1554
- - [ ] IF download succeeded: audiodesc.vtt generated
1555
- - [ ] IF download FAILED: Failure documented in audit-summary.md with reason
1556
- - [ ] IF download FAILED: Manual captioning instructions in remediation.md
1557
-
1558
- **BLOCKING:** Cannot generate final reports until video pipeline attempted.
1559
-
1560
- ### Output Files
1561
- - [ ] audit-summary.md with scores, priorities, and user impact
1562
- - [ ] remediation.md with ALL copy-paste code fixes
1563
- - [ ] violations.json with violation data
1564
- - [ ] VTT files (if videos)
1565
- - [ ] implementation.md (if videos)
1566
-
1567
- ### Quality Checks
1568
- - [ ] Compliance score calculated
1569
- - [ ] Production readiness assessed
1570
- - [ ] ROI prioritization completed
1571
- - [ ] POUR analysis included
1572
-
1573
- **IF ANY CHECKBOX IS NO = TASK INCOMPLETE**
1574
- </default_to_action>
1575
-
1576
- ---
1577
-
1578
- ## Quick Reference Card
1579
-
1580
- ### Usage
1581
- ```
1582
- /a11y-ally https://example.com
1583
- ```
1584
-
1585
- ### v7.0 Resilience Features
1586
- | Feature | Description |
1587
- |---------|-------------|
1588
- | **Parallel Execution** | All 3 tools run simultaneously via Promise.allSettled |
1589
- | **Per-Tool Timeouts** | axe: 60s, pa11y: 60s, Lighthouse: 90s |
1590
- | **Retry with Backoff** | 2 retries per tool with exponential backoff |
1591
- | **Graceful Degradation** | Continue if 1+ tools succeed |
1592
- | **Progressive Output** | Results stream as tools complete |
1593
- | **Bot Protection** | Stealth mode, random delays, cookie dismissal |
1594
-
1595
- ### Expected Output Structure
1596
- ```
1597
- docs/accessibility-scans/{page-slug}/
1598
- ├── audit-summary.md # Executive summary with scores
1599
- ├── remediation.md # ALL copy-paste code fixes
1600
- ├── violations.json # Machine-readable data
1601
- ├── implementation.md # Video integration (if videos)
1602
- ├── video-*-captions.vtt # Captions (if videos)
1603
- └── video-*-audiodesc.vtt # Audio descriptions (if videos)
1604
- ```
1605
-
1606
- ### Compliance Thresholds
1607
- | Level | Min Score | Critical | Serious |
1608
- |-------|-----------|----------|---------|
1609
- | A | 70% | 0 | ≤5 |
1610
- | AA | 85% | 0 | ≤3 |
1611
- | AAA | 95% | 0 | 0 |
1612
-
1613
- ### Tool Coverage by Success
1614
- | Tools Succeeded | Detection Rate | Status |
1615
- |-----------------|---------------|--------|
1616
- | 3/3 | ~95% | Optimal |
1617
- | 2/3 | ~85% | ⚠️ Good |
1618
- | 1/3 | ~70% | ⚠️ Acceptable |
1619
- | 0/3 | | ❌ Retry needed |
1620
-
1621
- ### ROI Formula
1622
- ```
1623
- ROI = (Impact × Users%) / Effort_Hours
1624
- ```
1625
-
1626
- ---
1627
-
1628
- ## EU Compliance Mapping
1629
-
1630
- | WCAG | EN 301 549 | EU Accessibility Act |
1631
- |------|------------|---------------------|
1632
- | 1.1.1 | 9.1.1.1 | EAA-I.1 Perceivable |
1633
- | 1.4.3 | 9.1.4.3 | EAA-I.1 Perceivable |
1634
- | 2.1.1 | 9.2.1.1 | EAA-I.2 Operable |
1635
- | 2.4.7 | 9.2.4.7 | EAA-I.2 Operable |
1636
- | 3.3.2 | 9.3.3.2 | EAA-I.3 Understandable |
1637
- | 4.1.2 | 9.4.1.2 | EAA-I.4 Robust |
1638
-
1639
- ---
1640
-
1641
- ## Critical Rules
1642
-
1643
- ### Execution Rules (v7.0)
1644
- 1. **ALWAYS** run multi-tool scan with **parallel execution** (Promise.allSettled)
1645
- 2. **ALWAYS** continue if at least **1 of 3 tools** succeeds (graceful degradation)
1646
- 3. **ALWAYS** document which tools failed and why in audit-summary.md
1647
- 4. **ALWAYS** use per-tool timeouts (60s/60s/90s) not global timeout
1648
- 5. **ALWAYS** retry failed tools with exponential backoff before giving up
1649
-
1650
- ### Quality Rules
1651
- 6. **ALWAYS** analyze context before generating fixes
1652
- 7. **ALWAYS** include confidence scores with remediation
1653
- 8. **ALWAYS** calculate user impact and ROI
1654
- 9. **ALWAYS** generate copy-paste ready code
1655
- 10. **NEVER** generate placeholder/template fixes
1656
- 11. **NEVER** skip video pipeline if videos detected
1657
- 12. **NEVER** complete without remediation.md
1658
- 13. **NEVER** fail audit just because 1-2 tools failed (use graceful degradation)
1
+ ---
2
+ name: a11y-ally
3
+ description: "Comprehensive WCAG accessibility auditing with multi-tool testing (axe-core + pa11y + Lighthouse), TRUE PARALLEL execution with Promise.allSettled, graceful degradation, retry with backoff, context-aware remediation, learning integration, and video accessibility. Uses 3-tier browser cascade: Vibium → agent-browser → Playwright+Stealth."
4
+ category: specialized-testing
5
+ priority: critical
6
+ tokenEstimate: 10000
7
+ agents: []
8
+ implementation_status: active
9
+ optimization_version: 7.0
10
+ last_optimized: 2026-01-26
11
+ dependencies: [playwright, playwright-extra, puppeteer-extra-plugin-stealth, "@axe-core/playwright", pa11y, lighthouse]
12
+ quick_reference_card: true
13
+ tags: [accessibility, wcag, a11y, video, captions, audiodesc, vtt, eu-compliance, context-aware, remediation, axe-core, pa11y, lighthouse, parallel, resilient, graceful-degradation, retry]
14
+ trust_tier: 3
15
+ validation:
16
+ schema_path: schemas/output.json
17
+ validator_path: scripts/validate-config.json
18
+ eval_path: evals/a11y-ally.yaml
19
+
20
+ ---
21
+
22
+ # /a11y-ally - Comprehensive Accessibility Audit
23
+
24
+ <default_to_action>
25
+ When this skill is invoked with a URL, Claude executes ALL steps automatically without waiting for user prompts between steps.
26
+
27
+ ## THIS IS AN LLM-POWERED SKILL
28
+
29
+ The value of this skill is **Claude's intelligence**, not just running automated tools:
30
+
31
+ | Automated Tools Do | Claude (This Skill) Does |
32
+ |--------------------|--------------------------|
33
+ | Flag "button has no name" | Analyze context: icon class, parent element, nearby text → generate "Add to wishlist" |
34
+ | Flag "image missing alt" | Use Vision to see the image → describe actual content |
35
+ | Flag "video has no captions" | Download video, extract frames, analyze each frame with Vision → generate real captions |
36
+ | Output generic templates | Generate context-specific, copy-paste ready fixes |
37
+
38
+ **IF YOU SKIP THE LLM ANALYSIS, THIS SKILL HAS NO VALUE.**
39
+
40
+ ---
41
+
42
+ ## EXECUTION MODEL
43
+
44
+ **CLAUDE EXECUTES ALL STEPS WITHOUT STOPPING.**
45
+
46
+ Do NOT wait for user prompts between steps. Execute the full pipeline:
47
+
48
+ 1. **Data Collection**: Run multi-tool scan (axe-core, pa11y, Lighthouse) via Bash
49
+ 2. **LLM Analysis**: Read results and analyze context for each violation
50
+ 3. **Vision Pipeline**: If videos detected download → extract frames → Read each frame → describe
51
+ 4. **Intelligent Remediation**: Generate context-specific fixes using your reasoning
52
+ 5. **Generate Reports**: Write all output files to `docs/accessibility-scans/{page-slug}/`
53
+
54
+ **WRONG:**
55
+ ```
56
+ Claude: "I found 5 violations. Should I analyze them?"
57
+ User: "Yes"
58
+ Claude: "I see a video. Should I run the video pipeline?"
59
+ User: "Yes"
60
+ ```
61
+
62
+ **RIGHT:**
63
+ ```
64
+ Claude: [Runs scan] → [Analyzes violations] → [Downloads video] → [Extracts frames] →
65
+ [Reads each frame with Vision] [Generates captions] → [Writes all files]
66
+ "Audit complete. Generated 4 files in docs/accessibility-scans/example/"
67
+ ```
68
+
69
+ ---
70
+
71
+ ## STEP 1: BROWSER AUTOMATION - Content Fetching
72
+
73
+ ### 1.1: Try VIBIUM First (Primary)
74
+ ```javascript
75
+ ToolSearch("select:mcp__vibium__browser_launch")
76
+ ToolSearch("select:mcp__vibium__browser_navigate")
77
+ mcp__vibium__browser_launch({ headless: true })
78
+ mcp__vibium__browser_navigate({ url: "TARGET_URL" })
79
+ ```
80
+
81
+ **If Vibium fails** → Go to STEP 1b
82
+
83
+ ### 1b: Try AGENT-BROWSER Fallback
84
+ ```javascript
85
+ ToolSearch("select:mcp__claude-flow_alpha__browser_open")
86
+ mcp__claude-flow_alpha__browser_open({ url: "TARGET_URL", waitUntil: "networkidle" })
87
+ ```
88
+
89
+ **If agent-browser fails** Go to STEP 1c
90
+
91
+ ### 1c: PLAYWRIGHT + STEALTH (Final Fallback)
92
+ ```bash
93
+ mkdir -p /tmp/a11y-work && cd /tmp/a11y-work
94
+ npm init -y 2>/dev/null
95
+ npm install playwright-extra puppeteer-extra-plugin-stealth @axe-core/playwright pa11y lighthouse chrome-launcher 2>/dev/null
96
+ ```
97
+
98
+ Create and run scan script - see STEP 2 for full multi-tool scan code.
99
+
100
+ ### 1d: PARALLEL MULTI-PAGE AUDIT (Optional)
101
+
102
+ For auditing multiple URLs simultaneously, use parallel execution:
103
+
104
+ ```javascript
105
+ // /tmp/a11y-work/parallel-audit.js
106
+ const { chromium } = require('playwright-extra');
107
+ const stealth = require('puppeteer-extra-plugin-stealth')();
108
+ const { AxeBuilder } = require('@axe-core/playwright');
109
+
110
+ chromium.use(stealth);
111
+
112
+ const MAX_CONCURRENT = 6; // Maximum parallel auditors
113
+
114
+ async function auditUrl(browser, url) {
115
+ const context = await browser.newContext();
116
+ const page = await context.newPage();
117
+
118
+ try {
119
+ await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 });
120
+ await page.waitForTimeout(2000);
121
+
122
+ const axeResults = await new AxeBuilder({ page })
123
+ .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22aa'])
124
+ .analyze();
125
+
126
+ return { url, success: true, violations: axeResults.violations };
127
+ } catch (error) {
128
+ return { url, success: false, error: error.message };
129
+ } finally {
130
+ await context.close();
131
+ }
132
+ }
133
+
134
+ async function parallelAudit(urls) {
135
+ const browser = await chromium.launch({ headless: true });
136
+ const results = [];
137
+
138
+ // Process in chunks of MAX_CONCURRENT
139
+ for (let i = 0; i < urls.length; i += MAX_CONCURRENT) {
140
+ const chunk = urls.slice(i, i + MAX_CONCURRENT);
141
+ console.log(`Auditing batch ${Math.floor(i/MAX_CONCURRENT) + 1}: ${chunk.length} URLs`);
142
+
143
+ const chunkResults = await Promise.all(
144
+ chunk.map(url => auditUrl(browser, url))
145
+ );
146
+ results.push(...chunkResults);
147
+ }
148
+
149
+ await browser.close();
150
+ return results;
151
+ }
152
+
153
+ // Usage: node parallel-audit.js url1 url2 url3 ...
154
+ const urls = process.argv.slice(2);
155
+ if (urls.length > 0) {
156
+ parallelAudit(urls).then(results => {
157
+ console.log(JSON.stringify(results, null, 2));
158
+ });
159
+ }
160
+ ```
161
+
162
+ **Usage for multi-page audit:**
163
+ ```bash
164
+ node parallel-audit.js https://example.com https://example.com/about https://example.com/contact
165
+ ```
166
+
167
+ ### 1e: SITE CRAWL MODE (Optional)
168
+
169
+ For comprehensive site audits, crawl and audit all pages:
170
+
171
+ ```javascript
172
+ // /tmp/a11y-work/crawl-audit.js
173
+ async function crawlAndAudit(startUrl, maxPages = 50) {
174
+ const browser = await chromium.launch({ headless: true });
175
+ const visited = new Set();
176
+ const toVisit = [startUrl];
177
+ const results = [];
178
+ const baseUrl = new URL(startUrl).origin;
179
+
180
+ while (toVisit.length > 0 && results.length < maxPages) {
181
+ const url = toVisit.shift();
182
+ if (visited.has(url)) continue;
183
+ visited.add(url);
184
+
185
+ console.log(`[${results.length + 1}/${maxPages}] Auditing: ${url}`);
186
+
187
+ const context = await browser.newContext();
188
+ const page = await context.newPage();
189
+
190
+ try {
191
+ await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 });
192
+
193
+ // Extract same-domain links for crawling
194
+ const links = await page.evaluate((base) => {
195
+ return [...document.querySelectorAll('a[href]')]
196
+ .map(a => a.href)
197
+ .filter(href => href.startsWith(base) && !href.includes('#'))
198
+ .filter(href => !href.match(/\.(pdf|jpg|png|gif|css|js)$/i));
199
+ }, baseUrl);
200
+
201
+ // Add new links to queue
202
+ links.forEach(link => {
203
+ if (!visited.has(link) && !toVisit.includes(link)) {
204
+ toVisit.push(link);
205
+ }
206
+ });
207
+
208
+ // Run accessibility audit
209
+ const axeResults = await new AxeBuilder({ page })
210
+ .withTags(['wcag2a', 'wcag2aa', 'wcag22aa'])
211
+ .analyze();
212
+
213
+ results.push({ url, violations: axeResults.violations });
214
+ } catch (e) {
215
+ results.push({ url, error: e.message });
216
+ }
217
+
218
+ await context.close();
219
+ }
220
+
221
+ await browser.close();
222
+ return { pagesAudited: results.length, results };
223
+ }
224
+
225
+ // Usage: node crawl-audit.js https://example.com 50
226
+ const [startUrl, maxPages] = process.argv.slice(2);
227
+ crawlAndAudit(startUrl, parseInt(maxPages) || 50).then(r => console.log(JSON.stringify(r, null, 2)));
228
+ ```
229
+
230
+ ---
231
+
232
+ ## STEP 2: COMPREHENSIVE WCAG SCAN (Multi-Tool, Parallel, Resilient)
233
+
234
+ **IMPORTANT:** This step uses THREE accessibility testing tools for maximum coverage:
235
+ - **axe-core**: Industry standard, excellent for ARIA and semantic issues
236
+ - **pa11y**: Strong on contrast, links, and HTML validation
237
+ - **Lighthouse**: Google's accessibility scoring with performance correlation
238
+
239
+ Combined detection rate is ~15% higher than any single tool.
240
+
241
+ ### 2.0: RESILIENCE ARCHITECTURE (v7.0 Enhancement)
242
+
243
+ **Key improvements over v6.0:**
244
+
245
+ | Feature | v6.0 (Old) | v7.0 (New) |
246
+ |---------|------------|------------|
247
+ | Tool execution | Sequential | **Parallel (Promise.allSettled)** |
248
+ | Timeout handling | Global 60s | **Per-tool (60s/60s/90s)** |
249
+ | Failure mode | All-or-nothing | **Graceful degradation** |
250
+ | Retry logic | None | **Exponential backoff (3 retries)** |
251
+ | Output style | Wait for all | **Progressive (stream as ready)** |
252
+ | Minimum tools | 3 required | **1 of 3 sufficient** |
253
+
254
+ **Coverage by tools succeeded:**
255
+ - 3/3 tools: ~95% detection (optimal)
256
+ - 2/3 tools: ~85% detection (good)
257
+ - 1/3 tools: ~70% detection (acceptable)
258
+ - 0/3 tools: FAIL - retry with different strategy
259
+
260
+ ### 2.1: Run Multi-Tool Analysis (PARALLEL + RESILIENT)
261
+ Create and run `/tmp/a11y-work/multi-tool-scan.js`:
262
+ ```javascript
263
+ const { chromium } = require('playwright-extra');
264
+ const stealth = require('puppeteer-extra-plugin-stealth')();
265
+ const { AxeBuilder } = require('@axe-core/playwright');
266
+ const pa11y = require('pa11y');
267
+ const lighthouse = require('lighthouse').default || require('lighthouse');
268
+ const { launch: launchChrome } = require('chrome-launcher');
269
+ const fs = require('fs');
270
+
271
+ chromium.use(stealth);
272
+
273
+ const TARGET_URL = process.argv[2] || 'TARGET_URL';
274
+ const OUTPUT_FILE = '/tmp/a11y-work/scan-results.json';
275
+ const SYSTEM_CHROMIUM = '/usr/bin/chromium';
276
+
277
+ // ========== RESILIENCE UTILITIES ==========
278
+
279
+ // Timeout wrapper - wraps any promise with a timeout
280
+ function withTimeout(promise, ms, name) {
281
+ return Promise.race([
282
+ promise,
283
+ new Promise((_, reject) =>
284
+ setTimeout(() => reject(new Error(`${name} timed out after ${ms}ms`)), ms)
285
+ )
286
+ ]);
287
+ }
288
+
289
+ // Retry wrapper - retries with exponential backoff
290
+ async function withRetry(fn, name, maxRetries = 3, baseDelay = 2000) {
291
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
292
+ try {
293
+ return await fn();
294
+ } catch (error) {
295
+ const isLastAttempt = attempt === maxRetries;
296
+ console.log(`[${name}] Attempt ${attempt}/${maxRetries} failed: ${error.message}`);
297
+ if (isLastAttempt) throw error;
298
+ const delay = baseDelay * Math.pow(2, attempt - 1); // Exponential backoff
299
+ console.log(`[${name}] Retrying in ${delay}ms...`);
300
+ await new Promise(r => setTimeout(r, delay));
301
+ }
302
+ }
303
+ }
304
+
305
+ // Sleep utility
306
+ const sleep = (ms) => new Promise(r => setTimeout(r, ms));
307
+
308
+ // Progressive output - append results as they arrive
309
+ function progressiveOutput(tool, data) {
310
+ console.log(`\n=== ${tool.toUpperCase()} COMPLETE ===`);
311
+ console.log(JSON.stringify(data, null, 2));
312
+
313
+ // Append to results file for progressive access
314
+ try {
315
+ let results = {};
316
+ if (fs.existsSync(OUTPUT_FILE)) {
317
+ results = JSON.parse(fs.readFileSync(OUTPUT_FILE, 'utf8'));
318
+ }
319
+ results[tool] = data;
320
+ results.lastUpdated = new Date().toISOString();
321
+ fs.writeFileSync(OUTPUT_FILE, JSON.stringify(results, null, 2));
322
+ } catch (e) { /* ignore file errors */ }
323
+ }
324
+
325
+ // ========== TOOL RUNNERS ==========
326
+
327
+ // TOOL 1: Axe-core (with page info extraction)
328
+ async function runAxeCore(url) {
329
+ console.log('[axe-core] Starting...');
330
+ const browser = await chromium.launch({
331
+ headless: true,
332
+ executablePath: SYSTEM_CHROMIUM,
333
+ args: [
334
+ '--no-sandbox',
335
+ '--disable-setuid-sandbox',
336
+ '--disable-dev-shm-usage',
337
+ '--disable-blink-features=AutomationControlled'
338
+ ]
339
+ });
340
+
341
+ const context = await browser.newContext({
342
+ userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
343
+ locale: 'en-US',
344
+ viewport: { width: 1920, height: 1080 }
345
+ });
346
+
347
+ const page = await context.newPage();
348
+
349
+ try {
350
+ // Use domcontentloaded (faster, more reliable than networkidle)
351
+ await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 });
352
+
353
+ // Random delay to appear human
354
+ await sleep(2000 + Math.random() * 2000);
355
+
356
+ // Try to dismiss cookie banners
357
+ try {
358
+ const cookieSelectors = [
359
+ 'button:has-text("Accept")', 'button:has-text("Akzeptieren")',
360
+ 'button:has-text("Alle akzeptieren")', '[data-testid="cookie-accept"]',
361
+ '#onetrust-accept-btn-handler', '.cookie-consent-accept'
362
+ ];
363
+ for (const selector of cookieSelectors) {
364
+ const btn = await page.$(selector);
365
+ if (btn) { await btn.click(); await sleep(500); break; }
366
+ }
367
+ } catch (e) { /* ignore cookie errors */ }
368
+
369
+ // Run axe-core analysis
370
+ const axeResults = await new AxeBuilder({ page })
371
+ .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22aa'])
372
+ .analyze();
373
+
374
+ // Extract comprehensive page info
375
+ const pageInfo = await page.evaluate(() => ({
376
+ title: document.title,
377
+ url: window.location.href,
378
+ lang: document.documentElement.lang,
379
+ images: {
380
+ total: document.querySelectorAll('img').length,
381
+ withAlt: document.querySelectorAll('img[alt]').length,
382
+ withoutAlt: document.querySelectorAll('img:not([alt])').length,
383
+ emptyAlt: document.querySelectorAll('img[alt=""]').length
384
+ },
385
+ headings: {
386
+ h1: Array.from(document.querySelectorAll('h1')).map(h => h.textContent.trim().slice(0,60)),
387
+ h2: document.querySelectorAll('h2').length,
388
+ h3: document.querySelectorAll('h3').length,
389
+ total: document.querySelectorAll('h1,h2,h3,h4,h5,h6').length
390
+ },
391
+ forms: {
392
+ total: document.querySelectorAll('form').length,
393
+ inputs: document.querySelectorAll('input, select, textarea').length,
394
+ buttons: document.querySelectorAll('button').length
395
+ },
396
+ links: { total: document.querySelectorAll('a').length },
397
+ aria: {
398
+ ariaLabels: document.querySelectorAll('[aria-label]').length,
399
+ roles: document.querySelectorAll('[role]').length
400
+ },
401
+ landmarks: {
402
+ main: document.querySelectorAll('main').length,
403
+ nav: document.querySelectorAll('nav').length,
404
+ header: document.querySelectorAll('header').length,
405
+ footer: document.querySelectorAll('footer').length
406
+ },
407
+ media: {
408
+ videos: document.querySelectorAll('video').length,
409
+ iframes: document.querySelectorAll('iframe').length,
410
+ videoUrls: Array.from(document.querySelectorAll('video')).map(v => {
411
+ const src = v.src || (v.querySelector('source') ? v.querySelector('source').src : '');
412
+ return {
413
+ src: src,
414
+ hasCaptions: !!v.querySelector('track[kind="captions"]')
415
+ };
416
+ })
417
+ }
418
+ }));
419
+
420
+ const violations = axeResults.violations.map(v => ({
421
+ tool: 'axe-core',
422
+ id: v.id,
423
+ impact: v.impact,
424
+ description: v.description,
425
+ help: v.help,
426
+ helpUrl: v.helpUrl,
427
+ tags: v.tags,
428
+ nodeCount: v.nodes.length,
429
+ nodes: v.nodes.slice(0, 5).map(n => ({
430
+ html: n.html.slice(0, 200),
431
+ target: n.target,
432
+ failureSummary: n.failureSummary
433
+ }))
434
+ }));
435
+
436
+ return {
437
+ success: true,
438
+ pageInfo,
439
+ violations,
440
+ passesCount: axeResults.passes.length
441
+ };
442
+ } finally {
443
+ await context.close();
444
+ await browser.close();
445
+ }
446
+ }
447
+
448
+ // TOOL 2: Pa11y
449
+ async function runPa11y(url) {
450
+ console.log('[pa11y] Starting...');
451
+ const results = await pa11y(url, {
452
+ standard: 'WCAG2AA',
453
+ timeout: 45000,
454
+ wait: 2000,
455
+ chromeLaunchConfig: {
456
+ executablePath: SYSTEM_CHROMIUM,
457
+ args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage']
458
+ }
459
+ });
460
+
461
+ const violations = results.issues.map(issue => ({
462
+ tool: 'pa11y',
463
+ id: issue.code,
464
+ impact: issue.type === 'error' ? 'serious' : issue.type === 'warning' ? 'moderate' : 'minor',
465
+ description: issue.message,
466
+ selector: issue.selector,
467
+ context: (issue.context || '').slice(0, 200)
468
+ }));
469
+
470
+ return { success: true, violations, total: results.issues.length };
471
+ }
472
+
473
+ // TOOL 3: Lighthouse
474
+ async function runLighthouse(url) {
475
+ console.log('[lighthouse] Starting...');
476
+ const chrome = await launchChrome({
477
+ chromePath: SYSTEM_CHROMIUM,
478
+ chromeFlags: ['--headless', '--no-sandbox', '--disable-gpu', '--disable-dev-shm-usage']
479
+ });
480
+
481
+ try {
482
+ const result = await lighthouse(url, {
483
+ port: chrome.port,
484
+ onlyCategories: ['accessibility'],
485
+ output: 'json'
486
+ });
487
+
488
+ const lhr = result.lhr;
489
+ const score = Math.round(lhr.categories.accessibility.score * 100);
490
+ const violations = Object.values(lhr.audits)
491
+ .filter(audit => audit.score !== null && audit.score < 1)
492
+ .map(audit => ({
493
+ tool: 'lighthouse',
494
+ id: audit.id,
495
+ impact: audit.score === 0 ? 'critical' : audit.score < 0.5 ? 'serious' : 'moderate',
496
+ score: audit.score,
497
+ description: audit.title
498
+ }));
499
+
500
+ return { success: true, score, violations };
501
+ } finally {
502
+ await chrome.kill();
503
+ }
504
+ }
505
+
506
+ // ========== MAIN: PARALLEL EXECUTION WITH GRACEFUL DEGRADATION ==========
507
+
508
+ (async () => {
509
+ console.log('=== MULTI-TOOL ACCESSIBILITY SCAN (v7.0 PARALLEL + RESILIENT) ===');
510
+ console.log('Target:', TARGET_URL);
511
+ console.log('Strategy: Promise.allSettled with per-tool timeouts\n');
512
+
513
+ const startTime = Date.now();
514
+
515
+ // Run ALL tools in PARALLEL with individual timeouts
516
+ const [axeResult, pa11yResult, lighthouseResult] = await Promise.allSettled([
517
+ withTimeout(
518
+ withRetry(() => runAxeCore(TARGET_URL), 'axe-core', 2, 3000),
519
+ 60000, 'axe-core'
520
+ ),
521
+ withTimeout(
522
+ withRetry(() => runPa11y(TARGET_URL), 'pa11y', 2, 3000),
523
+ 60000, 'pa11y'
524
+ ),
525
+ withTimeout(
526
+ withRetry(() => runLighthouse(TARGET_URL), 'lighthouse', 2, 3000),
527
+ 90000, 'lighthouse'
528
+ )
529
+ ]);
530
+
531
+ // ========== PROCESS RESULTS (Graceful Degradation) ==========
532
+ const results = {
533
+ url: TARGET_URL,
534
+ timestamp: new Date().toISOString(),
535
+ duration: `${((Date.now() - startTime) / 1000).toFixed(1)}s`,
536
+ toolsSucceeded: 0,
537
+ toolsFailed: 0,
538
+ pageInfo: null,
539
+ violations: [],
540
+ byTool: {}
541
+ };
542
+
543
+ // Process axe-core results
544
+ if (axeResult.status === 'fulfilled') {
545
+ results.toolsSucceeded++;
546
+ results.pageInfo = axeResult.value.pageInfo;
547
+ results.violations.push(...axeResult.value.violations);
548
+ results.byTool['axe-core'] = {
549
+ success: true,
550
+ count: axeResult.value.violations.length,
551
+ passes: axeResult.value.passesCount
552
+ };
553
+ progressiveOutput('axe-core', axeResult.value);
554
+ } else {
555
+ results.toolsFailed++;
556
+ results.byTool['axe-core'] = { success: false, error: axeResult.reason.message };
557
+ console.log('\n[axe-core] FAILED:', axeResult.reason.message);
558
+ }
559
+
560
+ // Process pa11y results
561
+ if (pa11yResult.status === 'fulfilled') {
562
+ results.toolsSucceeded++;
563
+ results.violations.push(...pa11yResult.value.violations);
564
+ results.byTool['pa11y'] = {
565
+ success: true,
566
+ count: pa11yResult.value.violations.length
567
+ };
568
+ progressiveOutput('pa11y', pa11yResult.value);
569
+ } else {
570
+ results.toolsFailed++;
571
+ results.byTool['pa11y'] = { success: false, error: pa11yResult.reason.message };
572
+ console.log('\n[pa11y] FAILED:', pa11yResult.reason.message);
573
+ }
574
+
575
+ // Process lighthouse results
576
+ if (lighthouseResult.status === 'fulfilled') {
577
+ results.toolsSucceeded++;
578
+ results.violations.push(...lighthouseResult.value.violations);
579
+ results.byTool['lighthouse'] = {
580
+ success: true,
581
+ score: lighthouseResult.value.score,
582
+ count: lighthouseResult.value.violations.length
583
+ };
584
+ progressiveOutput('lighthouse', lighthouseResult.value);
585
+ } else {
586
+ results.toolsFailed++;
587
+ results.byTool['lighthouse'] = { success: false, error: lighthouseResult.reason.message };
588
+ console.log('\n[lighthouse] FAILED:', lighthouseResult.reason.message);
589
+ }
590
+
591
+ // ========== DEDUPLICATE VIOLATIONS ==========
592
+ const seen = new Set();
593
+ const uniqueViolations = [];
594
+ for (const v of results.violations) {
595
+ const key = (v.description || '').toLowerCase().slice(0, 50);
596
+ if (!seen.has(key)) {
597
+ seen.add(key);
598
+ uniqueViolations.push(v);
599
+ }
600
+ }
601
+ results.uniqueViolations = uniqueViolations;
602
+ results.totalUnique = uniqueViolations.length;
603
+
604
+ // ========== FINAL OUTPUT ==========
605
+ console.log('\n' + '='.repeat(60));
606
+ console.log('=== SCAN COMPLETE ===');
607
+ console.log('='.repeat(60));
608
+ console.log(`Tools succeeded: ${results.toolsSucceeded}/3`);
609
+ console.log(`Tools failed: ${results.toolsFailed}/3`);
610
+ console.log(`Duration: ${results.duration}`);
611
+ console.log(`Total unique violations: ${results.totalUnique}`);
612
+
613
+ if (results.toolsSucceeded === 0) {
614
+ console.log('\n⚠️ ALL TOOLS FAILED - Consider:');
615
+ console.log(' 1. Site may have strong bot protection');
616
+ console.log(' 2. Try Vibium MCP browser instead');
617
+ console.log(' 3. Check network connectivity');
618
+ } else if (results.toolsSucceeded < 3) {
619
+ console.log(`\n⚠️ Partial coverage (${results.toolsSucceeded}/3 tools) - Results still usable`);
620
+ } else {
621
+ console.log('\n Full coverage achieved (3/3 tools)');
622
+ }
623
+
624
+ console.log('\n=== PAGE INFO ===');
625
+ console.log(JSON.stringify(results.pageInfo, null, 2));
626
+
627
+ console.log('\n=== VIOLATIONS BY TOOL ===');
628
+ console.log(JSON.stringify(results.byTool, null, 2));
629
+
630
+ console.log('\n=== UNIQUE VIOLATIONS ===');
631
+ console.log(JSON.stringify(results.uniqueViolations, null, 2));
632
+
633
+ // Save final results
634
+ fs.writeFileSync(OUTPUT_FILE, JSON.stringify(results, null, 2));
635
+ console.log(`\nResults saved to: ${OUTPUT_FILE}`);
636
+ })();
637
+ ```
638
+
639
+ ### 2.2: Read Scan Results
640
+
641
+ After running the scan, read the results file:
642
+ ```bash
643
+ cat /tmp/a11y-work/scan-results.json
644
+ ```
645
+
646
+ The results include:
647
+ - **pageInfo**: Page structure, images, headings, media
648
+ - **violations**: All violations from all tools (deduplicated)
649
+ - **byTool**: Success/failure status per tool
650
+ - **toolsSucceeded**: Number of tools that completed (1-3)
651
+
652
+ ### 2.3: Graceful Degradation Decision Tree
653
+
654
+ | Tools Succeeded | Action |
655
+ |-----------------|--------|
656
+ | **3/3** | ✅ Full coverage - proceed with all results |
657
+ | **2/3** | ⚠️ Good coverage - note which tool failed in report |
658
+ | **1/3** | ⚠️ Basic coverage - proceed but flag limited confidence |
659
+ | **0/3** | ❌ Retry with Vibium MCP, or document failure |
660
+
661
+ ### 2.4: MANDATORY - Check for Videos and Trigger Pipeline
662
+
663
+ After reading scan results, check `pageInfo.media.videoUrls`:
664
+
665
+ ```javascript
666
+ // Check scan-results.json for videos
667
+ const results = JSON.parse(fs.readFileSync('/tmp/a11y-work/scan-results.json'));
668
+ if (results.pageInfo && results.pageInfo.media.videoUrls.length > 0) {
669
+ console.log('=== VIDEOS DETECTED - TRIGGERING VIDEO PIPELINE ===');
670
+ for (const video of results.pageInfo.media.videoUrls) {
671
+ console.log(`Video: ${video.src}`);
672
+ console.log(` Has captions: ${video.hasCaptions}`);
673
+ }
674
+ // PROCEED TO STEP 7 IMMEDIATELY
675
+ }
676
+ ```
677
+
678
+ **IF videos detected AND hasCaptions=false → STEP 7 is MANDATORY before generating reports.**
679
+
680
+ ---
681
+
682
+ ## STEP 3: CONTEXT-AWARE REMEDIATION (LLM-POWERED)
683
+
684
+ **THIS IS WHERE CLAUDE'S INTELLIGENCE MATTERS.**
685
+
686
+ Generic tools output: `aria-label="[DESCRIPTION]"`
687
+ You output: `aria-label="Add to shopping cart"` because you understand context.
688
+
689
+ ### 3.1: Context Analysis (Use Your Reasoning)
690
+
691
+ For EACH violation, Claude must:
692
+
693
+ 1. **READ THE HTML CONTEXT** - Don't just see `<button class="btn">`, see:
694
+ ```html
695
+ <div class="product-card" data-product="Adidas Superstar">
696
+ <img src="superstar.jpg" alt="White sneakers">
697
+ <span class="price">$99</span>
698
+ <button class="btn add-to-cart"> <!-- THIS IS THE VIOLATION -->
699
+ <svg class="icon-cart">...</svg>
700
+ </button>
701
+ </div>
702
+ ```
703
+
704
+ 2. **INFER PURPOSE** from:
705
+ - Class names: `add-to-cart`, `wishlist`, `menu-toggle`
706
+ - Parent context: Inside `.product-card` with product data
707
+ - Icon classes: `icon-cart`, `icon-heart`, `icon-search`
708
+ - Nearby text: Product name, price, "Add to bag"
709
+ - Page section: Header nav vs product grid vs checkout
710
+
711
+ 3. **GENERATE SPECIFIC FIX**:
712
+ ```html
713
+ <!-- NOT THIS (generic template) -->
714
+ <button aria-label="[DESCRIPTION]">
715
+
716
+ <!-- THIS (context-aware) -->
717
+ <button aria-label="Add Adidas Superstar to cart - $99">
718
+ ```
719
+
720
+ ### 3.2: Confidence Scoring
721
+
722
+ Rate your confidence in each fix:
723
+ - **0.9+**: Clear context (class="add-to-cart" near product name)
724
+ - **0.7-0.9**: Reasonable inference (icon-cart class alone)
725
+ - **<0.7**: Needs human review (ambiguous context)
726
+
727
+ Include confidence in remediation.md:
728
+ ```markdown
729
+ ### Button: `.product-card .btn` (Confidence: 0.95)
730
+ **Context:** Inside product card for "Adidas Superstar", has cart icon
731
+ **Fix:** `aria-label="Add Adidas Superstar to cart"`
732
+ ```
733
+
734
+ ### 3.2: Remediation Templates by Violation Type
735
+
736
+ **Form Labels (WCAG 1.3.1, 3.3.2, 4.1.2)**
737
+ ```html
738
+ <!-- Context: Input inside payment form, near "Card Number" text -->
739
+ <!-- Confidence: 0.95 -->
740
+
741
+ <!-- BEFORE -->
742
+ <input type="text" name="cardNumber" placeholder="1234 5678 9012 3456">
743
+
744
+ <!-- AFTER -->
745
+ <label for="card-number">Credit Card Number</label>
746
+ <input type="text"
747
+ id="card-number"
748
+ name="cardNumber"
749
+ placeholder="1234 5678 9012 3456"
750
+ aria-describedby="card-hint"
751
+ autocomplete="cc-number"
752
+ inputmode="numeric"
753
+ pattern="[0-9\s]{13,19}">
754
+ <span id="card-hint" class="visually-hidden">Enter 16-digit card number</span>
755
+
756
+ <!-- RATIONALE -->
757
+ - Visible label aids all users
758
+ - aria-describedby provides additional context
759
+ - autocomplete enables autofill
760
+ - inputmode shows numeric keyboard on mobile
761
+ - pattern enables browser validation
762
+ ```
763
+
764
+ **Icon Buttons (WCAG 4.1.2)**
765
+ ```html
766
+ <!-- Context: Button with SVG inside nav, classes include "menu-toggle" -->
767
+ <!-- Confidence: 0.92 -->
768
+
769
+ <!-- BEFORE -->
770
+ <button class="menu-toggle">
771
+ <svg>...</svg>
772
+ </button>
773
+
774
+ <!-- AFTER -->
775
+ <button class="menu-toggle"
776
+ type="button"
777
+ aria-expanded="false"
778
+ aria-controls="main-menu"
779
+ aria-label="Open navigation menu">
780
+ <svg aria-hidden="true" focusable="false">...</svg>
781
+ </button>
782
+
783
+ <!-- RATIONALE -->
784
+ - aria-label describes action, not icon
785
+ - aria-expanded communicates state
786
+ - aria-controls links to menu element
787
+ - SVG hidden from assistive tech (decorative)
788
+ ```
789
+
790
+ **Color Contrast (WCAG 1.4.3)**
791
+ ```html
792
+ <!-- Context: Gray text (#767676) on white background -->
793
+ <!-- Current ratio: 4.48:1 (FAILS AA for normal text) -->
794
+ <!-- Required: 4.5:1 (AA) or 7:1 (AAA) -->
795
+
796
+ <!-- BEFORE -->
797
+ .low-contrast { color: #767676; background: #ffffff; }
798
+
799
+ <!-- AFTER (Option 1: Darken text - minimal change) -->
800
+ .accessible { color: #757575; background: #ffffff; } /* 4.6:1 - PASSES AA */
801
+
802
+ <!-- AFTER (Option 2: Higher contrast for AAA) -->
803
+ .high-contrast { color: #595959; background: #ffffff; } /* 7.0:1 - PASSES AAA */
804
+
805
+ <!-- COLOR ALTERNATIVES -->
806
+ | Original | AA Pass | AAA Pass | Notes |
807
+ |----------|---------|----------|-------|
808
+ | #767676 | #757575 | #595959 | Gray text |
809
+ | #0066cc | #0055b3 | #003d82 | Link blue |
810
+ | #cc0000 | #b30000 | #8b0000 | Error red |
811
+ ```
812
+
813
+ **Heading Hierarchy (WCAG 1.3.1)**
814
+ ```html
815
+ <!-- Context: Page has 10 H1 elements, skipped H2 levels -->
816
+
817
+ <!-- BEFORE (broken) -->
818
+ <h1>Welcome</h1>
819
+ <h1>Products</h1> <!-- ERROR: Multiple H1s -->
820
+ <h4>Shoes</h4> <!-- ERROR: Skipped H2, H3 -->
821
+ <h1>Contact</h1>
822
+
823
+ <!-- AFTER (correct) -->
824
+ <h1>Site Name - Main Page Title</h1>
825
+ <main>
826
+ <section aria-labelledby="products-heading">
827
+ <h2 id="products-heading">Products</h2>
828
+ <h3>Shoes</h3>
829
+ <h3>Clothing</h3>
830
+ </section>
831
+ <section aria-labelledby="contact-heading">
832
+ <h2 id="contact-heading">Contact</h2>
833
+ </section>
834
+ </main>
835
+
836
+ <!-- HEADING STRUCTURE VISUALIZATION -->
837
+ h1: Site Name - Main Page Title
838
+ ├── h2: Products
839
+ │ ├── h3: Shoes
840
+ │ └── h3: Clothing
841
+ └── h2: Contact
842
+ ```
843
+
844
+ **Skip Links (WCAG 2.4.1)**
845
+ ```html
846
+ <!-- Add as FIRST element inside <body> -->
847
+ <body>
848
+ <a href="#main-content" class="skip-link">Skip to main content</a>
849
+ <a href="#main-nav" class="skip-link">Skip to navigation</a>
850
+
851
+ <header>
852
+ <nav id="main-nav" aria-label="Main navigation">...</nav>
853
+ </header>
854
+
855
+ <main id="main-content" tabindex="-1">
856
+ <!-- Main content -->
857
+ </main>
858
+ </body>
859
+
860
+ <style>
861
+ .skip-link {
862
+ position: absolute;
863
+ top: -100%;
864
+ left: 16px;
865
+ background: #000;
866
+ color: #fff;
867
+ padding: 12px 24px;
868
+ z-index: 10000;
869
+ text-decoration: none;
870
+ font-weight: bold;
871
+ border-radius: 0 0 4px 4px;
872
+ transition: top 0.2s;
873
+ }
874
+ .skip-link:focus {
875
+ top: 0;
876
+ outline: 3px solid #ffcc00;
877
+ outline-offset: 2px;
878
+ }
879
+ </style>
880
+ ```
881
+
882
+ **Focus Indicators (WCAG 2.4.7)**
883
+ ```css
884
+ /* NEVER do this */
885
+ *:focus { outline: none; } /* WCAG FAIL */
886
+
887
+ /* DO THIS - Custom focus styles */
888
+ :focus-visible {
889
+ outline: 3px solid #005fcc;
890
+ outline-offset: 2px;
891
+ }
892
+
893
+ /* Remove outline only for mouse users */
894
+ :focus:not(:focus-visible) {
895
+ outline: none;
896
+ }
897
+
898
+ /* High contrast for interactive elements */
899
+ a:focus-visible,
900
+ button:focus-visible,
901
+ input:focus-visible,
902
+ select:focus-visible,
903
+ textarea:focus-visible,
904
+ [role="button"]:focus-visible {
905
+ outline: 3px solid #005fcc;
906
+ outline-offset: 2px;
907
+ box-shadow: 0 0 0 6px rgba(0, 95, 204, 0.2);
908
+ }
909
+
910
+ /* Dark backgrounds need light focus */
911
+ .dark-bg :focus-visible {
912
+ outline-color: #ffffff;
913
+ box-shadow: 0 0 0 6px rgba(255, 255, 255, 0.3);
914
+ }
915
+ ```
916
+
917
+ **Keyboard Navigation (WCAG 2.1.1, 2.1.2)**
918
+ ```html
919
+ <!-- Custom interactive element needs keyboard support -->
920
+
921
+ <!-- BEFORE (inaccessible) -->
922
+ <div class="dropdown" onclick="toggleMenu()">
923
+ Menu
924
+ </div>
925
+
926
+ <!-- AFTER (accessible) -->
927
+ <button type="button"
928
+ class="dropdown-trigger"
929
+ aria-expanded="false"
930
+ aria-controls="dropdown-menu"
931
+ onclick="toggleMenu()"
932
+ onkeydown="handleKeydown(event)">
933
+ Menu
934
+ </button>
935
+ <ul id="dropdown-menu" role="menu" hidden>
936
+ <li role="none"><a role="menuitem" href="/page1">Page 1</a></li>
937
+ <li role="none"><a role="menuitem" href="/page2">Page 2</a></li>
938
+ </ul>
939
+
940
+ <script>
941
+ function handleKeydown(event) {
942
+ switch(event.key) {
943
+ case 'Enter':
944
+ case ' ':
945
+ event.preventDefault();
946
+ toggleMenu();
947
+ break;
948
+ case 'Escape':
949
+ closeMenu();
950
+ break;
951
+ case 'ArrowDown':
952
+ event.preventDefault();
953
+ focusFirstMenuItem();
954
+ break;
955
+ }
956
+ }
957
+ </script>
958
+ ```
959
+
960
+ **Modal Focus Trap (WCAG 2.4.3)**
961
+ ```javascript
962
+ // Focus trap for modals - REQUIRED for WCAG compliance
963
+ function trapFocus(modal) {
964
+ const focusable = modal.querySelectorAll(
965
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
966
+ );
967
+ const first = focusable[0];
968
+ const last = focusable[focusable.length - 1];
969
+
970
+ // Focus first element when modal opens
971
+ first?.focus();
972
+
973
+ modal.addEventListener('keydown', (e) => {
974
+ if (e.key === 'Tab') {
975
+ if (e.shiftKey && document.activeElement === first) {
976
+ e.preventDefault();
977
+ last.focus();
978
+ } else if (!e.shiftKey && document.activeElement === last) {
979
+ e.preventDefault();
980
+ first.focus();
981
+ }
982
+ }
983
+ if (e.key === 'Escape') {
984
+ closeModal();
985
+ }
986
+ });
987
+ }
988
+
989
+ // Return focus when modal closes
990
+ function closeModal() {
991
+ modal.hidden = true;
992
+ triggerButton.focus(); // Return focus to trigger
993
+ }
994
+ ```
995
+
996
+ **iframe Titles (WCAG 4.1.2)**
997
+ ```html
998
+ <!-- All iframes MUST have descriptive titles -->
999
+ <iframe src="map.html" title="Store location map showing 5 nearby stores"></iframe>
1000
+ <iframe src="video.html" title="Product demonstration video with captions"></iframe>
1001
+ <iframe src="chat.html" title="Customer support chat window"></iframe>
1002
+ ```
1003
+
1004
+ ---
1005
+
1006
+ ## STEP 4: USER IMPACT ANALYSIS
1007
+
1008
+ For each violation, calculate user impact:
1009
+
1010
+ ### 4.1: Affected User Groups
1011
+ | Violation Type | Affected Groups | % of Users |
1012
+ |----------------|-----------------|------------|
1013
+ | Missing alt text | Blind, low-vision | 7-10% |
1014
+ | Missing form labels | Blind, screen reader users | 5-8% |
1015
+ | Low color contrast | Low-vision, color blind | 8-12% |
1016
+ | No keyboard access | Motor impaired, power users | 10-15% |
1017
+ | Missing captions | Deaf, hard-of-hearing | 5-7% |
1018
+ | Flashing content | Seizure sensitive | 0.5-1% |
1019
+ | Complex language | Cognitive impairment | 10-15% |
1020
+
1021
+ ### 4.2: Impact Severity Classification
1022
+ ```
1023
+ BLOCKS-USAGE: User cannot complete task at all
1024
+ - Missing form labels on required fields
1025
+ - Keyboard traps
1026
+ - Critical buttons without accessible names
1027
+
1028
+ IMPAIRS-USAGE: User can complete task with difficulty
1029
+ - Low contrast (can read with effort)
1030
+ - Missing skip links (tedious navigation)
1031
+ - Incorrect heading structure (confusing)
1032
+
1033
+ MINOR-INCONVENIENCE: Suboptimal but functional
1034
+ - Empty alt on decorative images
1035
+ - Redundant ARIA
1036
+ - Non-semantic HTML that works
1037
+ ```
1038
+
1039
+ ---
1040
+
1041
+ ## STEP 5: ROI-BASED PRIORITIZATION
1042
+
1043
+ Calculate priority for each remediation:
1044
+
1045
+ ### 5.1: Priority Formula
1046
+ ```
1047
+ PRIORITY_SCORE = (IMPACT_WEIGHT × USERS_AFFECTED) / EFFORT_HOURS
1048
+
1049
+ Where:
1050
+ - IMPACT_WEIGHT: Critical=10, Serious=7, Moderate=4, Minor=1
1051
+ - USERS_AFFECTED: Estimated % of users impacted
1052
+ - EFFORT_HOURS: Estimated fix time (0.25 to 8 hours)
1053
+ ```
1054
+
1055
+ ### 5.2: Effort Estimation Guide
1056
+ | Fix Type | Effort | Complexity |
1057
+ |----------|--------|------------|
1058
+ | Add aria-label | 0.25h | Trivial |
1059
+ | Add alt text | 0.25h | Trivial |
1060
+ | Add form label | 0.5h | Simple |
1061
+ | Fix color contrast | 0.5h | Simple |
1062
+ | Add skip links | 1h | Simple |
1063
+ | Fix heading structure | 2h | Medium |
1064
+ | Add keyboard navigation | 4h | High |
1065
+ | Implement focus trap | 4h | High |
1066
+ | Add video captions | 8h | High |
1067
+
1068
+ ### 5.3: Priority Output Format
1069
+ ```
1070
+ | Rank | Violation | Impact | Users | Effort | ROI Score |
1071
+ |------|-----------|--------|-------|--------|-----------|
1072
+ | 1 | Form labels missing | Critical | 15% | 0.5h | 300 |
1073
+ | 2 | Keyboard trap | Critical | 12% | 4h | 30 |
1074
+ | 3 | Low contrast | Serious | 10% | 0.5h | 140 |
1075
+ | 4 | Missing alt text | Serious | 8% | 0.25h | 224 |
1076
+ ```
1077
+
1078
+ ---
1079
+
1080
+ ## STEP 6: PRODUCTION READINESS ASSESSMENT
1081
+
1082
+ ### 6.1: Compliance Scoring
1083
+ ```
1084
+ COMPLIANCE_SCORE = (PASSED_CRITERIA / TOTAL_CRITERIA) × 100
1085
+
1086
+ Production Ready if:
1087
+ Score 85%
1088
+ ✓ Zero critical violations
1089
+ Fewer than 3 serious violations
1090
+ ✓ All user journeys keyboard accessible
1091
+ ```
1092
+
1093
+ ### 6.2: POUR Analysis (Perceivable, Operable, Understandable, Robust)
1094
+ ```
1095
+ | Principle | Guidelines | Pass | Fail | Score |
1096
+ |-----------|-----------|------|------|-------|
1097
+ | Perceivable | 1.1-1.4 | 12 | 3 | 80% |
1098
+ | Operable | 2.1-2.5 | 18 | 2 | 90% |
1099
+ | Understandable | 3.1-3.3 | 8 | 1 | 89% |
1100
+ | Robust | 4.1 | 4 | 1 | 80% |
1101
+ | **TOTAL** | | **42** | **7** | **86%** |
1102
+ ```
1103
+
1104
+ ---
1105
+
1106
+ ## STEP 7: VIDEO ACCESSIBILITY PIPELINE
1107
+
1108
+ **Execute for EACH video detected on page.**
1109
+
1110
+ ### 7.1: Detect and Extract Video URLs (MANDATORY)
1111
+
1112
+ **This step MUST be integrated into STEP 2 multi-tool scan.**
1113
+
1114
+ Add this to the page.evaluate() in the multi-tool scan:
1115
+ ```javascript
1116
+ // In pageInfo extraction (STEP 2), add:
1117
+ videos: {
1118
+ elements: [...document.querySelectorAll('video')].map(v => ({
1119
+ src: v.src || v.querySelector('source')?.src,
1120
+ fullUrl: new URL(v.src || v.querySelector('source')?.src || '', window.location.href).href,
1121
+ poster: v.poster,
1122
+ hasCaptions: v.querySelector('track[kind="captions"]') !== null,
1123
+ hasDescriptions: v.querySelector('track[kind="descriptions"]') !== null,
1124
+ duration: v.duration || 'unknown',
1125
+ autoplay: v.autoplay,
1126
+ muted: v.muted
1127
+ })),
1128
+ iframes: [...document.querySelectorAll('iframe')].map(iframe => {
1129
+ const src = iframe.src;
1130
+ const isVideo = /youtube|vimeo|dailymotion|wistia/.test(src);
1131
+ return isVideo ? { src, platform: src.match(/(youtube|vimeo|dailymotion|wistia)/)?.[1] } : null;
1132
+ }).filter(Boolean)
1133
+ }
1134
+ ```
1135
+
1136
+ **MANDATORY OUTPUT:** Log all video URLs found:
1137
+ ```
1138
+ === VIDEOS DETECTED ===
1139
+ Video 1: https://example.com/promo.mp4 (no captions, no descriptions)
1140
+ YouTube iframe: https://youtube.com/embed/xxx
1141
+ ```
1142
+
1143
+ ### 7.2: Download and Extract Frames (MANDATORY for each video)
1144
+
1145
+ **For EACH video URL found in 7.1:**
1146
+
1147
+ ```bash
1148
+ # Create output directory
1149
+ mkdir -p /tmp/a11y-work/frames
1150
+
1151
+ # Download video (with retry and user-agent)
1152
+ curl -L -A "Mozilla/5.0" --retry 3 -o /tmp/a11y-work/video.mp4 "FULL_VIDEO_URL"
1153
+
1154
+ # Verify download succeeded
1155
+ if [ -f /tmp/a11y-work/video.mp4 ] && [ -s /tmp/a11y-work/video.mp4 ]; then
1156
+ echo "Video downloaded successfully"
1157
+ ffmpeg -i /tmp/a11y-work/video.mp4 -vf "fps=1/3" -frames:v 10 /tmp/a11y-work/frames/frame_%02d.jpg 2>/dev/null
1158
+ echo "Extracted $(ls /tmp/a11y-work/frames/*.jpg 2>/dev/null | wc -l) frames"
1159
+ else
1160
+ echo "VIDEO DOWNLOAD FAILED - Document this in audit-summary.md"
1161
+ fi
1162
+ ```
1163
+
1164
+ **IF VIDEO DOWNLOAD FAILS:**
1165
+ 1. Document the failure reason in audit-summary.md
1166
+ 2. Still create video-captions violation in violations.json
1167
+ 3. Add remediation instructions WITHOUT generated captions
1168
+ 4. Mark video pipeline as "blocked" not "skipped"
1169
+
1170
+ ### 7.3: Analyze Each Frame with Claude Vision (MANDATORY)
1171
+
1172
+ **USE THE READ TOOL ON EACH FRAME IMAGE.**
1173
+
1174
+ Claude Code has native vision capabilities. When you Read an image file, you SEE it.
1175
+
1176
+ ```
1177
+ Read /tmp/a11y-work/frames/frame_01.jpg
1178
+ Read /tmp/a11y-work/frames/frame_02.jpg
1179
+ Read /tmp/a11y-work/frames/frame_03.jpg
1180
+ ... (continue for all frames)
1181
+ ```
1182
+
1183
+ **For EACH frame, describe:**
1184
+ - **SCENE**: Setting, environment, lighting, location
1185
+ - **PEOPLE**: Who appears, what they're doing, expressions, clothing
1186
+ - **PRODUCTS**: Items shown (for e-commerce: product names, colors, styles)
1187
+ - **TEXT**: Any visible text, logos, signs, prices
1188
+ - **ACTION**: Movement, transitions, what's happening
1189
+
1190
+ **Example output after reading frame_01.jpg:**
1191
+ ```
1192
+ Frame 1 (0:00-0:03): A woman in white Adidas sneakers running on a forest trail.
1193
+ Morning light filters through trees. She wears black athletic leggings and a
1194
+ gray tank top. The Adidas three-stripe logo is visible on her shoes.
1195
+ ```
1196
+
1197
+ **THIS IS THE LLM VALUE.** Generic tools output "[DESCRIBE CONTENT]".
1198
+ You output actual descriptions because you can SEE the image.
1199
+
1200
+ ---
1201
+
1202
+ **FALLBACK: If Read tool fails on images**
1203
+
1204
+ Try Anthropic API directly:
1205
+ ```javascript
1206
+ const Anthropic = require('@anthropic-ai/sdk');
1207
+ const fs = require('fs');
1208
+ const client = new Anthropic();
1209
+
1210
+ const imageData = fs.readFileSync('/tmp/a11y-work/frames/frame_01.jpg').toString('base64');
1211
+ const response = await client.messages.create({
1212
+ model: 'claude-sonnet-4-20250514',
1213
+ max_tokens: 500,
1214
+ messages: [{
1215
+ role: 'user',
1216
+ content: [
1217
+ { type: 'image', source: { type: 'base64', media_type: 'image/jpeg', data: imageData } },
1218
+ { type: 'text', text: 'Describe this video frame for accessibility captions.' }
1219
+ ]
1220
+ }]
1221
+ });
1222
+ ```
1223
+
1224
+ **LAST RESORT: Context-Based Inference (No Vision)**
1225
+ If vision completely unavailable, infer from:
1226
+ - Video filename: "product-demo.mp4" product demonstration
1227
+ - Page context: product page → product showcase
1228
+ - Surrounding text: nearby headings and descriptions
1229
+
1230
+ **Document for each frame:**
1231
+ - **SCENE**: Setting, environment, lighting
1232
+ - **PEOPLE**: Who, actions, expressions, clothing
1233
+ - **OBJECTS**: Products, props, equipment
1234
+ - **TEXT**: Visible text, logos, signs
1235
+ - **ACTION**: Movement, transitions
1236
+ - **COLORS**: Dominant colors, accessibility-relevant
1237
+
1238
+ ### 7.4: Generate WebVTT Captions
1239
+ ```vtt
1240
+ WEBVTT
1241
+ Kind: captions
1242
+ Language: {detected-language}
1243
+
1244
+ 00:00:00.000 --> 00:00:03.000
1245
+ [Description from frame_01 analysis]
1246
+
1247
+ 00:00:03.000 --> 00:00:06.000
1248
+ [Description from frame_02 analysis]
1249
+ ```
1250
+
1251
+ ### 7.5: Generate Audio Descriptions
1252
+ ```vtt
1253
+ WEBVTT
1254
+ Kind: descriptions
1255
+ Language: en
1256
+
1257
+ 00:00:00.000 --> 00:00:03.000
1258
+ SCENE: [Detailed scene for blind users]
1259
+ VISUAL: [What's on screen]
1260
+ TEXT: [Any readable text]
1261
+ ACTION: [What's happening]
1262
+ ```
1263
+
1264
+ ---
1265
+
1266
+ ## STEP 8: GENERATE COMPREHENSIVE REPORTS
1267
+
1268
+ ### 8.1: Required Output Files
1269
+ Save ALL files to `docs/accessibility-scans/{page-slug}/`:
1270
+
1271
+ | File | Contents |
1272
+ |------|----------|
1273
+ | `audit-summary.md` | Executive summary, scores, top issues, user impact |
1274
+ | `remediation.md` | **ALL copy-paste code fixes** with context |
1275
+ | `violations.json` | Machine-readable violation data |
1276
+ | `implementation.md` | Video integration guide (if videos) |
1277
+ | `*.vtt` | Caption and audio description files |
1278
+
1279
+ ### 8.2: audit-summary.md Template
1280
+ ```markdown
1281
+ # Accessibility Audit Report: {Site Name}
1282
+
1283
+ **URL:** {url}
1284
+ **Date:** {date}
1285
+ **Standard:** WCAG 2.2 Level AA
1286
+
1287
+ ## Executive Summary
1288
+
1289
+ | Metric | Value |
1290
+ |--------|-------|
1291
+ | **Compliance Score** | {score}% |
1292
+ | **Production Ready** | {Yes/No} |
1293
+ | **Critical Issues** | {count} |
1294
+ | **Total Violations** | {count} |
1295
+ | **Estimated Fix Time** | {hours}h |
1296
+
1297
+ ## POUR Analysis
1298
+ {table}
1299
+
1300
+ ## Top 10 Issues by Priority
1301
+ {priority table with ROI scores}
1302
+
1303
+ ## User Impact Summary
1304
+ {affected user groups and percentages}
1305
+
1306
+ ## Recommendations
1307
+ {prioritized action items}
1308
+ ```
1309
+
1310
+ ### 8.3: remediation.md Template
1311
+ ```markdown
1312
+ # Accessibility Remediation Guide: {Site Name}
1313
+
1314
+ ## Quick Wins (Copy-Paste Ready)
1315
+
1316
+ ### 1. Form Labels ({count} issues)
1317
+ {For EACH unlabeled input: context, before/after code, rationale, confidence}
1318
+
1319
+ ### 2. Heading Structure ({count} issues)
1320
+ {Current structure visualization, fixed structure, code changes}
1321
+
1322
+ ### 3. Color Contrast ({count} issues)
1323
+ {For EACH: current colors, ratio, suggested colors, CSS fixes}
1324
+
1325
+ ### 4. Missing Alt Text ({count} issues)
1326
+ {For EACH image: context-inferred alt text suggestions}
1327
+
1328
+ ### 5. Keyboard Navigation ({count} issues)
1329
+ {For EACH: element, issue, fix code, test instructions}
1330
+
1331
+ ### 6. Focus Indicators
1332
+ {Global CSS to add}
1333
+
1334
+ ### 7. Skip Links
1335
+ {Full HTML + CSS to add}
1336
+
1337
+ ### 8. ARIA Fixes ({count} issues)
1338
+ {For EACH: context, specific aria attributes to add}
1339
+
1340
+ ### 9. iframe Titles ({count} issues)
1341
+ {For EACH: suggested title based on content}
1342
+
1343
+ ### 10. Video Accessibility ({count} videos)
1344
+ {Links to generated VTT files, implementation code}
1345
+
1346
+ ## Testing Checklist
1347
+ - [ ] Tab through entire page - all interactive elements reachable
1348
+ - [ ] Screen reader announces all content correctly
1349
+ - [ ] Color contrast passes (use axe DevTools)
1350
+ - [ ] Works without mouse
1351
+ - [ ] Works at 200% zoom
1352
+ - [ ] Video captions synchronized and accurate
1353
+ ```
1354
+
1355
+ ---
1356
+
1357
+ ## STEP 9: LEARNING PROTOCOL (When MCP Available)
1358
+
1359
+ Integrate with the learning system to improve over time.
1360
+
1361
+ ### 9.1: Query Previous Patterns BEFORE Audit
1362
+
1363
+ Check if similar sites were audited before:
1364
+ ```javascript
1365
+ // Load MCP tools
1366
+ ToolSearch("select:mcp__claude-flow_alpha__memory_retrieve")
1367
+ ToolSearch("select:mcp__claude-flow_alpha__hooks_intelligence_pattern_search")
1368
+
1369
+ // Retrieve domain-specific patterns
1370
+ mcp__claude-flow_alpha__memory_retrieve({
1371
+ key: `accessibility/patterns/${domain}`,
1372
+ namespace: "learning"
1373
+ })
1374
+
1375
+ // Search for similar violation patterns
1376
+ mcp__claude-flow_alpha__hooks_intelligence_pattern_search({
1377
+ query: "accessibility remediation",
1378
+ type: "accessibility-fix",
1379
+ limit: 10
1380
+ })
1381
+ ```
1382
+
1383
+ ### 9.2: Store Successful Patterns AFTER Audit
1384
+
1385
+ Store patterns that worked for future reuse:
1386
+ ```javascript
1387
+ ToolSearch("select:mcp__claude-flow_alpha__memory_store")
1388
+ ToolSearch("select:mcp__claude-flow_alpha__hooks_intelligence_pattern_store")
1389
+
1390
+ // Store audit outcome
1391
+ mcp__claude-flow_alpha__memory_store({
1392
+ key: `accessibility-audit/${domain}-${Date.now()}`,
1393
+ namespace: "learning",
1394
+ value: {
1395
+ url: auditedUrl,
1396
+ timestamp: new Date().toISOString(),
1397
+ violationsFound: violations.length,
1398
+ criticalCount: violations.filter(v => v.impact === 'critical').length,
1399
+ toolsUsed: ['axe-core', 'pa11y', 'lighthouse'],
1400
+ patterns: {
1401
+ commonViolations: extractTopViolationTypes(violations),
1402
+ effectiveFixes: extractFixesThatWorked(remediations)
1403
+ }
1404
+ }
1405
+ })
1406
+
1407
+ // Store reusable remediation patterns
1408
+ mcp__claude-flow_alpha__hooks_intelligence_pattern_store({
1409
+ pattern: "form-label-contextual-fix",
1410
+ confidence: 0.92,
1411
+ type: "accessibility-remediation",
1412
+ metadata: {
1413
+ wcagCriteria: "1.3.1, 3.3.2, 4.1.2",
1414
+ violationType: "missing-form-label",
1415
+ codeTemplate: "<label for=\"{id}\">{inferredLabel}</label>",
1416
+ contextSignals: ["placeholder", "nearby-text", "field-name"]
1417
+ }
1418
+ })
1419
+ ```
1420
+
1421
+ ### 9.3: Calculate Audit Quality Score
1422
+
1423
+ Self-assess audit completeness (for learning feedback):
1424
+
1425
+ | Criteria | Points | Your Score |
1426
+ |----------|--------|------------|
1427
+ | Multi-tool testing used (3 tools) | 20 | |
1428
+ | All WCAG 2.2 AA criteria checked | 15 | |
1429
+ | Context-aware fixes generated | 20 | |
1430
+ | Confidence scores included | 10 | |
1431
+ | ROI prioritization calculated | 10 | |
1432
+ | Video pipeline completed (if applicable) | 15 | |
1433
+ | EU compliance mapping included | 10 | |
1434
+ | **Total** | **100** | |
1435
+
1436
+ **Quality Levels:**
1437
+ - 90-100: Excellent (1.0 reward)
1438
+ - 70-89: Good (0.8 reward)
1439
+ - 50-69: Acceptable (0.5 reward)
1440
+ - <50: Incomplete (0.0 reward - redo required)
1441
+
1442
+ ---
1443
+
1444
+ ## STEP 10: SCREEN READER TESTING GUIDE
1445
+
1446
+ Manual testing instructions (cannot be fully automated):
1447
+
1448
+ ### 10.1: NVDA (Windows - Free)
1449
+ ```
1450
+ 1. Download: https://www.nvaccess.org/download/
1451
+ 2. Install and start NVDA (Ctrl+Alt+N)
1452
+ 3. Navigate to audited page
1453
+
1454
+ Key Commands:
1455
+ - H: Jump through headings
1456
+ - F: Jump through form fields
1457
+ - B: Jump through buttons
1458
+ - T: Jump through tables
1459
+ - K: Jump through links
1460
+ - D: Jump through landmarks
1461
+ - Tab: Move through focusable elements
1462
+
1463
+ Verify:
1464
+ - [ ] All headings announced with correct level
1465
+ - [ ] Form fields announce labels
1466
+ - [ ] Buttons announce purpose
1467
+ - [ ] Images announce alt text or "decorative"
1468
+ - [ ] Dynamic content changes announced (aria-live)
1469
+ ```
1470
+
1471
+ ### 10.2: VoiceOver (macOS - Built-in)
1472
+ ```
1473
+ 1. Enable: System Preferences → Accessibility → VoiceOver
1474
+ 2. Toggle: Cmd+F5
1475
+ 3. Navigate to audited page
1476
+
1477
+ Key Commands:
1478
+ - VO+U: Open rotor (headings, links, forms, landmarks)
1479
+ - VO+Space: Activate element
1480
+ - VO+Right/Left: Move through content
1481
+ - VO+Cmd+H: Jump to next heading
1482
+
1483
+ Verify:
1484
+ - [ ] Rotor shows all headings hierarchically
1485
+ - [ ] Forms are navigable and labels announced
1486
+ - [ ] Focus order matches visual order
1487
+ - [ ] All content is reachable
1488
+ ```
1489
+
1490
+ ### 10.3: JAWS (Windows - Commercial)
1491
+ ```
1492
+ 1. Trial: https://www.freedomscientific.com/products/software/jaws/
1493
+ 2. Start JAWS and navigate to page
1494
+
1495
+ Key Commands:
1496
+ - H: Next heading
1497
+ - F: Next form field
1498
+ - B: Next button
1499
+ - T: Next table
1500
+ - Ins+F6: Heading list
1501
+ - Ins+F7: Link list
1502
+
1503
+ Verify:
1504
+ - [ ] Virtual cursor mode works correctly
1505
+ - [ ] Forms mode activates in forms
1506
+ - [ ] All ARIA roles announced properly
1507
+ ```
1508
+
1509
+ ### 10.4: Screen Reader Testing Checklist
1510
+
1511
+ | Test | NVDA | VoiceOver | JAWS |
1512
+ |------|------|-----------|------|
1513
+ | Headings hierarchy correct | [ ] | [ ] | [ ] |
1514
+ | Form labels announced | [ ] | [ ] | [ ] |
1515
+ | Button purposes clear | [ ] | [ ] | [ ] |
1516
+ | Image alt text correct | [ ] | [ ] | [ ] |
1517
+ | Links announce destination | [ ] | [ ] | [ ] |
1518
+ | Landmarks navigable | [ ] | [ ] | [ ] |
1519
+ | Focus order logical | [ ] | [ ] | [ ] |
1520
+ | Dynamic updates announced | [ ] | [ ] | [ ] |
1521
+ | No keyboard traps | [ ] | [ ] | [ ] |
1522
+ | Skip links work | [ ] | [ ] | [ ] |
1523
+
1524
+ ---
1525
+
1526
+ ## VALIDATION CHECKLIST
1527
+
1528
+ Before completing, verify ALL items:
1529
+
1530
+ ### Content Fetching (v7.0 Resilient)
1531
+ - [ ] Browser launched (Vibium/agent-browser/Playwright)
1532
+ - [ ] Page loaded and analyzed
1533
+ - [ ] Multi-tool scan ran with **parallel execution**
1534
+ - [ ] At least **1 of 3 tools succeeded** (graceful degradation)
1535
+ - [ ] If tools failed, documented **which tools and why**
1536
+ - [ ] Results saved to `/tmp/a11y-work/scan-results.json`
1537
+
1538
+ ### Violation Analysis
1539
+ - [ ] All violations extracted with WCAG criteria
1540
+ - [ ] Context analyzed for each violation
1541
+ - [ ] User impact calculated
1542
+
1543
+ ### Remediation Generation
1544
+ - [ ] Form label fixes with context and rationale
1545
+ - [ ] Heading hierarchy fix with visualization
1546
+ - [ ] Color contrast fixes with hex codes
1547
+ - [ ] Alt text suggestions with confidence scores
1548
+ - [ ] Skip link code (full HTML + CSS)
1549
+ - [ ] Focus indicator CSS
1550
+ - [ ] ARIA fixes with specific attributes
1551
+ - [ ] Keyboard navigation fixes
1552
+ - [ ] iframe titles
1553
+
1554
+ ### Video Accessibility (MANDATORY if pageInfo.media.videos > 0)
1555
+ - [ ] Video URLs extracted (full URLs, not relative)
1556
+ - [ ] Download attempted for EACH video
1557
+ - [ ] IF download succeeded: Frames extracted with ffmpeg
1558
+ - [ ] IF download succeeded: Each frame analyzed with Read tool
1559
+ - [ ] IF download succeeded: captions.vtt generated from ACTUAL frame descriptions
1560
+ - [ ] IF download succeeded: audiodesc.vtt generated
1561
+ - [ ] IF download FAILED: Failure documented in audit-summary.md with reason
1562
+ - [ ] IF download FAILED: Manual captioning instructions in remediation.md
1563
+
1564
+ **BLOCKING:** Cannot generate final reports until video pipeline attempted.
1565
+
1566
+ ### Output Files
1567
+ - [ ] audit-summary.md with scores, priorities, and user impact
1568
+ - [ ] remediation.md with ALL copy-paste code fixes
1569
+ - [ ] violations.json with violation data
1570
+ - [ ] VTT files (if videos)
1571
+ - [ ] implementation.md (if videos)
1572
+
1573
+ ### Quality Checks
1574
+ - [ ] Compliance score calculated
1575
+ - [ ] Production readiness assessed
1576
+ - [ ] ROI prioritization completed
1577
+ - [ ] POUR analysis included
1578
+
1579
+ **IF ANY CHECKBOX IS NO = TASK INCOMPLETE**
1580
+ </default_to_action>
1581
+
1582
+ ---
1583
+
1584
+ ## Quick Reference Card
1585
+
1586
+ ### Usage
1587
+ ```
1588
+ /a11y-ally https://example.com
1589
+ ```
1590
+
1591
+ ### v7.0 Resilience Features
1592
+ | Feature | Description |
1593
+ |---------|-------------|
1594
+ | **Parallel Execution** | All 3 tools run simultaneously via Promise.allSettled |
1595
+ | **Per-Tool Timeouts** | axe: 60s, pa11y: 60s, Lighthouse: 90s |
1596
+ | **Retry with Backoff** | 2 retries per tool with exponential backoff |
1597
+ | **Graceful Degradation** | Continue if 1+ tools succeed |
1598
+ | **Progressive Output** | Results stream as tools complete |
1599
+ | **Bot Protection** | Stealth mode, random delays, cookie dismissal |
1600
+
1601
+ ### Expected Output Structure
1602
+ ```
1603
+ docs/accessibility-scans/{page-slug}/
1604
+ ├── audit-summary.md # Executive summary with scores
1605
+ ├── remediation.md # ALL copy-paste code fixes
1606
+ ├── violations.json # Machine-readable data
1607
+ ├── implementation.md # Video integration (if videos)
1608
+ ├── video-*-captions.vtt # Captions (if videos)
1609
+ └── video-*-audiodesc.vtt # Audio descriptions (if videos)
1610
+ ```
1611
+
1612
+ ### Compliance Thresholds
1613
+ | Level | Min Score | Critical | Serious |
1614
+ |-------|-----------|----------|---------|
1615
+ | A | 70% | 0 | ≤5 |
1616
+ | AA | 85% | 0 | ≤3 |
1617
+ | AAA | 95% | 0 | 0 |
1618
+
1619
+ ### Tool Coverage by Success
1620
+ | Tools Succeeded | Detection Rate | Status |
1621
+ |-----------------|---------------|--------|
1622
+ | 3/3 | ~95% | ✅ Optimal |
1623
+ | 2/3 | ~85% | ⚠️ Good |
1624
+ | 1/3 | ~70% | ⚠️ Acceptable |
1625
+ | 0/3 | — | ❌ Retry needed |
1626
+
1627
+ ### ROI Formula
1628
+ ```
1629
+ ROI = (Impact × Users%) / Effort_Hours
1630
+ ```
1631
+
1632
+ ---
1633
+
1634
+ ## EU Compliance Mapping
1635
+
1636
+ | WCAG | EN 301 549 | EU Accessibility Act |
1637
+ |------|------------|---------------------|
1638
+ | 1.1.1 | 9.1.1.1 | EAA-I.1 Perceivable |
1639
+ | 1.4.3 | 9.1.4.3 | EAA-I.1 Perceivable |
1640
+ | 2.1.1 | 9.2.1.1 | EAA-I.2 Operable |
1641
+ | 2.4.7 | 9.2.4.7 | EAA-I.2 Operable |
1642
+ | 3.3.2 | 9.3.3.2 | EAA-I.3 Understandable |
1643
+ | 4.1.2 | 9.4.1.2 | EAA-I.4 Robust |
1644
+
1645
+ ---
1646
+
1647
+ ## Critical Rules
1648
+
1649
+ ### Execution Rules (v7.0)
1650
+ 1. **ALWAYS** run multi-tool scan with **parallel execution** (Promise.allSettled)
1651
+ 2. **ALWAYS** continue if at least **1 of 3 tools** succeeds (graceful degradation)
1652
+ 3. **ALWAYS** document which tools failed and why in audit-summary.md
1653
+ 4. **ALWAYS** use per-tool timeouts (60s/60s/90s) not global timeout
1654
+ 5. **ALWAYS** retry failed tools with exponential backoff before giving up
1655
+
1656
+ ### Quality Rules
1657
+ 6. **ALWAYS** analyze context before generating fixes
1658
+ 7. **ALWAYS** include confidence scores with remediation
1659
+ 8. **ALWAYS** calculate user impact and ROI
1660
+ 9. **ALWAYS** generate copy-paste ready code
1661
+ 10. **NEVER** generate placeholder/template fixes
1662
+ 11. **NEVER** skip video pipeline if videos detected
1663
+ 12. **NEVER** complete without remediation.md
1664
+ 13. **NEVER** fail audit just because 1-2 tools failed (use graceful degradation)