qa360 2.1.7 → 2.2.1

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 (909) hide show
  1. package/.BETA_TESTING_FEEDBACK.md +256 -0
  2. package/.claude/settings.local.json +154 -0
  3. package/.editorconfig +21 -0
  4. package/.github/CODEOWNERS +23 -0
  5. package/.github/ISSUE_TEMPLATE/bug_report.yml +108 -0
  6. package/.github/ISSUE_TEMPLATE/feedback_dx.yml +121 -0
  7. package/.github/dependabot.yml +35 -0
  8. package/.github/workflows/mcp-dx.yml +106 -0
  9. package/.github/workflows/release.yml +26 -0
  10. package/.github/workflows/test.yml +93 -0
  11. package/.nvmrc +1 -0
  12. package/.qa360/vault.db +0 -0
  13. package/.qa360/vault.db-shm +0 -0
  14. package/.qa360/vault.db-wal +0 -0
  15. package/.qa360-artifacts/.gitkeep +0 -0
  16. package/.qa360-artifacts/baselines/.gitkeep +0 -0
  17. package/.qa360-artifacts/cache/.gitkeep +0 -0
  18. package/.qa360-artifacts/reports/.gitkeep +0 -0
  19. package/.qa360-artifacts/screenshots/.gitkeep +0 -0
  20. package/.qa360-baselines/www_xyqo_ai.baseline.json +33 -0
  21. package/CHANGELOG.md +234 -0
  22. package/CODEOWNERS +43 -0
  23. package/CONTRIBUTING.md +273 -0
  24. package/NOVICE_USER_GUIDE.md +272 -0
  25. package/QUICK_START.md +191 -0
  26. package/README.md +191 -163
  27. package/adapters/README.md +46 -0
  28. package/check-branches.sh +32 -0
  29. package/cli/.qa360/keys/ed25519.key +1 -0
  30. package/cli/.qa360/keys/ed25519.pub +1 -0
  31. package/cli/CHANGELOG.md +84 -0
  32. package/cli/LICENSE +24 -0
  33. package/cli/README.md +222 -0
  34. package/cli/examples/README.md +160 -0
  35. package/cli/package.json +76 -0
  36. package/cli/scripts/bundle-for-npm.sh +51 -0
  37. package/cli/scripts/validate-package.js +116 -0
  38. package/cli/src/__tests__/commands/doctor.test.ts +108 -0
  39. package/cli/src/__tests__/index.test.ts +15 -0
  40. package/cli/src/cli-minimal.ts +44 -0
  41. package/cli/src/commands/__tests__/crawl.test.ts +412 -0
  42. package/cli/src/commands/__tests__/doctor-qa360-home.test.ts +156 -0
  43. package/cli/src/commands/__tests__/e2e-ui-tests.test.ts +494 -0
  44. package/cli/src/commands/__tests__/e2e.test.ts +187 -0
  45. package/cli/src/commands/__tests__/flakiness.test.ts +528 -0
  46. package/cli/src/commands/__tests__/generate.test.ts +507 -0
  47. package/cli/src/commands/__tests__/history.integration.test.ts +358 -0
  48. package/cli/src/commands/__tests__/history.test.ts +433 -0
  49. package/cli/src/commands/__tests__/monitor-realworld.test.ts +199 -0
  50. package/cli/src/commands/__tests__/monitor.test.ts +81 -0
  51. package/cli/src/commands/__tests__/ollama.test.ts +529 -0
  52. package/cli/src/commands/__tests__/repair.test.ts +225 -0
  53. package/cli/src/commands/__tests__/report.integration.test.ts +167 -0
  54. package/cli/src/commands/__tests__/report.test.ts +294 -0
  55. package/cli/src/commands/__tests__/report.vitest.ts +288 -0
  56. package/cli/src/commands/__tests__/retry.test.ts +78 -0
  57. package/cli/src/commands/__tests__/run.integration.test.ts +240 -0
  58. package/cli/src/commands/__tests__/run.test.ts +346 -0
  59. package/cli/src/commands/__tests__/run.vitest.ts +301 -0
  60. package/cli/src/commands/__tests__/secrets.test.ts +114 -0
  61. package/cli/src/commands/__tests__/serve.test.ts +80 -0
  62. package/cli/src/commands/__tests__/verify.test.ts +103 -0
  63. package/cli/src/commands/ai.ts +702 -0
  64. package/cli/src/commands/ask.ts +678 -0
  65. package/cli/src/commands/coverage.ts +305 -0
  66. package/cli/src/commands/crawl.ts +155 -0
  67. package/cli/src/commands/doctor.ts +610 -0
  68. package/cli/src/commands/examples.ts +248 -0
  69. package/cli/src/commands/explain.ts +710 -0
  70. package/cli/src/commands/flakiness.ts +560 -0
  71. package/cli/src/commands/generate.ts +566 -0
  72. package/cli/src/commands/history.ts +914 -0
  73. package/cli/src/commands/init.ts +777 -0
  74. package/cli/src/commands/monitor.ts +270 -0
  75. package/cli/src/commands/ollama.ts +337 -0
  76. package/cli/src/commands/pack.ts +497 -0
  77. package/cli/src/commands/regression.ts +400 -0
  78. package/cli/src/commands/repair.ts +356 -0
  79. package/cli/src/commands/report.ts +463 -0
  80. package/cli/src/commands/retry.ts +380 -0
  81. package/cli/src/commands/run.ts +220 -0
  82. package/cli/src/commands/scan.ts +177 -0
  83. package/cli/src/commands/secrets.ts +340 -0
  84. package/cli/src/commands/serve.ts +194 -0
  85. package/cli/src/commands/slo.ts +387 -0
  86. package/cli/src/commands/verify-temp-note.md +11 -0
  87. package/cli/src/commands/verify.ts +322 -0
  88. package/cli/src/generators/index.ts +6 -0
  89. package/cli/src/generators/json-reporter.ts +15 -0
  90. package/cli/src/generators/test-generator.ts +90 -0
  91. package/cli/src/index.ts +289 -0
  92. package/cli/src/scanners/dom-scanner.ts +360 -0
  93. package/cli/src/scanners/index.ts +5 -0
  94. package/cli/src/types/scan.ts +84 -0
  95. package/cli/src/utils/config.ts +145 -0
  96. package/cli/tsconfig.bundle.json +12 -0
  97. package/cli/tsconfig.json +23 -0
  98. package/cli/vitest.config.ts +59 -0
  99. package/core/LICENSE +24 -0
  100. package/core/README.md +64 -0
  101. package/core/package.json +81 -0
  102. package/core/src/__tests__/adapters-contract/adapters-contract.test.md +156 -0
  103. package/core/src/__tests__/index.test.ts +31 -0
  104. package/core/src/__tests__/integration/phase3.test.ts +405 -0
  105. package/core/src/__tests__/pack/validator.test.ts +312 -0
  106. package/core/src/__tests__/secrets/crypto.test.ts +190 -0
  107. package/core/src/__tests__/secrets/manager.test.ts +316 -0
  108. package/core/src/__tests__/security/redactor-phase3.test.ts +233 -0
  109. package/core/src/__tests__/serve/health-checker.test.ts +155 -0
  110. package/core/src/__tests__/serve/process-manager.test.ts +213 -0
  111. package/core/src/__tests__/serve/server.test.ts +103 -0
  112. package/core/src/__tests__/vault/cas.test.ts +178 -0
  113. package/core/src/__tests__/vault/vault.test.ts +296 -0
  114. package/core/src/adapters/__tests__/fixtures/jest-coverage.json +8 -0
  115. package/core/src/adapters/__tests__/fixtures/jest-results.json +41 -0
  116. package/core/src/adapters/__tests__/fixtures/pytest-junit.xml +16 -0
  117. package/core/src/adapters/__tests__/fixtures/vitest-coverage.json +8 -0
  118. package/core/src/adapters/__tests__/fixtures/vitest-results.json +50 -0
  119. package/core/src/adapters/__tests__/gitleaks-secrets.test.ts +452 -0
  120. package/core/src/adapters/__tests__/jest-adapter.test.ts +276 -0
  121. package/core/src/adapters/__tests__/k6-perf.test.ts +538 -0
  122. package/core/src/adapters/__tests__/osv-deps.test.ts +471 -0
  123. package/core/src/adapters/__tests__/playwright-native-api.test.ts +792 -0
  124. package/core/src/adapters/__tests__/playwright-ui-e2e.test.ts +431 -0
  125. package/core/src/adapters/__tests__/playwright-ui.test.ts +1073 -0
  126. package/core/src/adapters/__tests__/pytest-adapter.test.ts +207 -0
  127. package/core/src/adapters/__tests__/semgrep-sast.test.ts +436 -0
  128. package/core/src/adapters/__tests__/vitest-adapter.test.ts +208 -0
  129. package/core/src/adapters/__tests__/zap-dast.test.ts +453 -0
  130. package/core/src/adapters/gitleaks-secrets.ts +521 -0
  131. package/core/src/adapters/jest-adapter.ts +306 -0
  132. package/core/src/adapters/k6-perf.ts +479 -0
  133. package/core/src/adapters/osv-deps.ts +467 -0
  134. package/core/src/adapters/playwright-native-adapter.ts +472 -0
  135. package/core/src/adapters/playwright-native-api.ts +619 -0
  136. package/core/src/adapters/playwright-ui.ts +1088 -0
  137. package/core/src/adapters/pytest-adapter.ts +472 -0
  138. package/core/src/adapters/semgrep-sast.ts +410 -0
  139. package/core/src/adapters/unit-test-types.ts +106 -0
  140. package/core/src/adapters/vitest-adapter.ts +295 -0
  141. package/core/src/adapters/zap-dast.ts +551 -0
  142. package/core/src/ai/__tests__/deepseek-provider.test.ts +586 -0
  143. package/core/src/ai/__tests__/ollama-provider.test.ts +641 -0
  144. package/core/src/ai/anthropic-provider.ts +262 -0
  145. package/core/src/ai/deepseek-provider.ts +315 -0
  146. package/core/src/ai/index.ts +87 -0
  147. package/core/src/ai/llm-client.ts +52 -0
  148. package/core/src/ai/mock-provider.ts +146 -0
  149. package/core/src/ai/ollama-provider.ts +269 -0
  150. package/core/src/ai/openai-provider.ts +240 -0
  151. package/core/src/ai/provider-factory.ts +408 -0
  152. package/core/src/artifacts/README.md +78 -0
  153. package/core/src/artifacts/index.ts +16 -0
  154. package/core/src/artifacts/ui-artifacts.ts +412 -0
  155. package/core/src/assertions/__tests__/engine.test.ts +360 -0
  156. package/core/src/assertions/engine.ts +577 -0
  157. package/core/src/assertions/index.ts +13 -0
  158. package/core/src/assertions/types.ts +229 -0
  159. package/core/src/auth/__tests__/api-key-provider.test.ts +282 -0
  160. package/core/src/auth/__tests__/auth-manager.test.ts +430 -0
  161. package/core/src/auth/__tests__/basic-auth-provider.test.ts +364 -0
  162. package/core/src/auth/__tests__/cloud-providers.test.ts +751 -0
  163. package/core/src/auth/__tests__/jwt-provider.test.ts +400 -0
  164. package/core/src/auth/__tests__/oauth2-provider.test.ts +383 -0
  165. package/core/src/auth/__tests__/totp-provider.test.ts +294 -0
  166. package/core/src/auth/__tests__/ui-login-provider.test.ts +323 -0
  167. package/core/src/auth/api-key-provider.ts +75 -0
  168. package/core/src/auth/aws-iam-provider.ts +212 -0
  169. package/core/src/auth/azure-ad-provider.ts +126 -0
  170. package/core/src/auth/basic-auth-provider.ts +133 -0
  171. package/core/src/auth/gcp-adc-provider.ts +146 -0
  172. package/core/src/auth/index.ts +342 -0
  173. package/core/src/auth/jwt-provider.ts +193 -0
  174. package/core/src/auth/manager.ts +281 -0
  175. package/core/src/auth/oauth2-provider.ts +141 -0
  176. package/core/src/auth/totp-provider.ts +163 -0
  177. package/core/src/auth/ui-login-provider.ts +242 -0
  178. package/core/src/cache/__tests__/lru-cache.test.ts +564 -0
  179. package/core/src/cache/index.ts +13 -0
  180. package/core/src/cache/lru-cache.ts +536 -0
  181. package/core/src/crawler/__tests__/journey-generator.test.ts +344 -0
  182. package/core/src/crawler/__tests__/selector-generator.test.ts +211 -0
  183. package/core/src/crawler/index.ts +335 -0
  184. package/core/src/crawler/journey-generator.ts +471 -0
  185. package/core/src/crawler/page-analyzer.ts +857 -0
  186. package/core/src/crawler/selector-generator.ts +280 -0
  187. package/core/src/crawler/types.ts +475 -0
  188. package/core/src/dashboard/__tests__/real-world.test.ts +430 -0
  189. package/core/src/dashboard/__tests__/server.test.ts +283 -0
  190. package/core/src/dashboard/__tests__/types.test.ts +208 -0
  191. package/core/src/dashboard/assets.ts +692 -0
  192. package/core/src/dashboard/index.ts +17 -0
  193. package/core/src/dashboard/server.ts +401 -0
  194. package/core/src/dashboard/types.ts +78 -0
  195. package/core/src/discoverer/__tests__/test-discoverer.test.ts +444 -0
  196. package/core/src/discoverer/index.ts +374 -0
  197. package/core/src/fixtures/__tests__/loader.test.ts +246 -0
  198. package/core/src/fixtures/__tests__/resolver.test.ts +334 -0
  199. package/core/src/fixtures/index.ts +9 -0
  200. package/core/src/fixtures/loader.ts +200 -0
  201. package/core/src/fixtures/resolver.ts +221 -0
  202. package/core/src/fixtures/types.ts +86 -0
  203. package/core/src/flakiness/__tests__/flakiness.test.ts +554 -0
  204. package/core/src/flakiness/index.ts +536 -0
  205. package/core/src/generation/__tests__/code-formatter.test.ts +170 -0
  206. package/core/src/generation/__tests__/code-generator-contract.test.ts +207 -0
  207. package/core/src/generation/__tests__/code-generator.test.ts +586 -0
  208. package/core/src/generation/__tests__/crawler-pack-generator.test.ts +479 -0
  209. package/core/src/generation/__tests__/generation-e2e-b2bshop.test.ts +718 -0
  210. package/core/src/generation/__tests__/generation-integration.test.ts +655 -0
  211. package/core/src/generation/__tests__/pack-generator.test.ts +408 -0
  212. package/core/src/generation/__tests__/prompt-builder.test.ts +200 -0
  213. package/core/src/generation/__tests__/real-provider-integration.test.ts +414 -0
  214. package/core/src/generation/__tests__/source-analyzer.test.ts +774 -0
  215. package/core/src/generation/__tests__/test-optimizer.test.ts +255 -0
  216. package/core/src/generation/code-formatter.ts +408 -0
  217. package/core/src/generation/code-generator.ts +470 -0
  218. package/core/src/generation/crawler-pack-generator.ts +289 -0
  219. package/core/src/generation/generator.ts +113 -0
  220. package/core/src/generation/index.ts +59 -0
  221. package/core/src/generation/pack-generator.ts +527 -0
  222. package/core/src/generation/prompt-builder.ts +772 -0
  223. package/core/src/generation/source-analyzer.ts +830 -0
  224. package/core/src/generation/test-optimizer.ts +474 -0
  225. package/core/src/generation/types.ts +217 -0
  226. package/core/src/hooks/__tests__/compose.test.ts +636 -0
  227. package/core/src/hooks/__tests__/runner.test.ts +478 -0
  228. package/core/src/hooks/compose.ts +268 -0
  229. package/core/src/hooks/runner.ts +364 -0
  230. package/core/src/index.ts +255 -0
  231. package/core/src/pack/__tests__/migrator.test.ts +594 -0
  232. package/core/src/pack/__tests__/validator.test.ts +759 -0
  233. package/core/src/pack/migrator.ts +353 -0
  234. package/core/src/pack/validator.ts +359 -0
  235. package/core/src/pack-v2/__tests__/loader.test.ts +533 -0
  236. package/core/src/pack-v2/__tests__/migrator.test.ts +455 -0
  237. package/core/src/pack-v2/__tests__/validator.test.ts +609 -0
  238. package/core/src/pack-v2/index.ts +41 -0
  239. package/core/src/pack-v2/loader.ts +358 -0
  240. package/core/src/pack-v2/migrator.ts +540 -0
  241. package/core/src/pack-v2/validator.ts +731 -0
  242. package/core/src/parallel/README.md +143 -0
  243. package/core/src/parallel/index.ts +16 -0
  244. package/core/src/parallel/parallel-runner.ts +282 -0
  245. package/core/src/pom/__tests__/loader.test.ts +378 -0
  246. package/core/src/pom/base-page.ts +425 -0
  247. package/core/src/pom/index.ts +45 -0
  248. package/core/src/pom/loader.ts +480 -0
  249. package/core/src/pom/types.ts +146 -0
  250. package/core/src/proof/__tests__/proof-roundtrip.test.ts +149 -0
  251. package/core/src/proof/__tests__/schema-validation-manual.mjs +211 -0
  252. package/core/src/proof/__tests__/schema-validation.test.ts +336 -0
  253. package/core/src/proof/__tests__/signer.test.ts +486 -0
  254. package/core/src/proof/__tests__/temporal-regression.test.ts +537 -0
  255. package/core/src/proof/__tests__/verifier-advanced.test.ts +588 -0
  256. package/core/src/proof/__tests__/verifier.test.ts +413 -0
  257. package/core/src/proof/bundle.ts +290 -0
  258. package/core/src/proof/canonicalize.ts +116 -0
  259. package/core/src/proof/index.ts +74 -0
  260. package/core/src/proof/schema.ts +285 -0
  261. package/core/src/proof/signer.ts +293 -0
  262. package/core/src/proof/verifier.ts +380 -0
  263. package/core/src/regression/__tests__/detector.test.ts +396 -0
  264. package/core/src/regression/__tests__/trend-analyzer.test.ts +300 -0
  265. package/core/src/regression/detector.ts +629 -0
  266. package/core/src/regression/index.ts +34 -0
  267. package/core/src/regression/trend-analyzer.ts +468 -0
  268. package/core/src/regression/types.ts +295 -0
  269. package/core/src/regression/vault.ts +419 -0
  270. package/core/src/repair/__tests__/repairer.test.ts +572 -0
  271. package/core/src/repair/__tests__/types.test.ts +302 -0
  272. package/core/src/repair/engine/__tests__/fixer.test.ts +482 -0
  273. package/core/src/repair/engine/__tests__/suggestion-engine.test.ts +395 -0
  274. package/core/src/repair/engine/fixer.ts +271 -0
  275. package/core/src/repair/engine/suggestion-engine.ts +234 -0
  276. package/core/src/repair/index.ts +53 -0
  277. package/core/src/repair/repairer.ts +376 -0
  278. package/core/src/repair/types.ts +119 -0
  279. package/core/src/repair/utils/__tests__/error-analyzer.test.ts +454 -0
  280. package/core/src/repair/utils/error-analyzer.ts +308 -0
  281. package/core/src/reporting/README.md +144 -0
  282. package/core/src/reporting/html-reporter.ts +835 -0
  283. package/core/src/reporting/index.ts +16 -0
  284. package/core/src/retry/README.md +192 -0
  285. package/core/src/retry/__tests__/flakiness-integration.test.ts +475 -0
  286. package/core/src/retry/__tests__/retry-engine.test.ts +424 -0
  287. package/core/src/retry/flakiness-integration.ts +267 -0
  288. package/core/src/retry/index.ts +48 -0
  289. package/core/src/retry/retry-engine.ts +368 -0
  290. package/core/src/retry/types.ts +208 -0
  291. package/core/src/retry/vault.ts +413 -0
  292. package/core/src/runner/__tests__/flakiness-integration.test.ts +566 -0
  293. package/core/src/runner/__tests__/phase3-e2e-b2bshop.test.ts +218 -0
  294. package/core/src/runner/__tests__/phase3-e2e-reqres.test.ts +199 -0
  295. package/core/src/runner/__tests__/phase3-runner.test.ts +1118 -0
  296. package/core/src/runner/e2e-helpers.ts +216 -0
  297. package/core/src/runner/phase3-runner.ts +1536 -0
  298. package/core/src/schemas/gherkin-report.json +122 -0
  299. package/core/src/secrets/__tests__/crypto.test.ts +180 -0
  300. package/core/src/secrets/crypto.ts +289 -0
  301. package/core/src/secrets/manager.ts +272 -0
  302. package/core/src/security/__tests__/hardening.test.ts +480 -0
  303. package/core/src/security/redaction-patterns-extended.ts +278 -0
  304. package/core/src/security/redactor.ts +326 -0
  305. package/core/src/self-healing/assertion-healer.ts +485 -0
  306. package/core/src/self-healing/engine.ts +626 -0
  307. package/core/src/self-healing/index.ts +33 -0
  308. package/core/src/self-healing/selector-healer.ts +488 -0
  309. package/core/src/self-healing/types.ts +193 -0
  310. package/core/src/serve/diagnostics-collector.ts +201 -0
  311. package/core/src/serve/health-checker.ts +274 -0
  312. package/core/src/serve/index.ts +9 -0
  313. package/core/src/serve/metrics-collector.ts +386 -0
  314. package/core/src/serve/process-manager.ts +265 -0
  315. package/core/src/serve/server.ts +230 -0
  316. package/core/src/slo/config.ts +408 -0
  317. package/core/src/slo/index.ts +68 -0
  318. package/core/src/slo/sli-calculator.ts +474 -0
  319. package/core/src/slo/slo-tracker.ts +481 -0
  320. package/core/src/slo/types.ts +408 -0
  321. package/core/src/slo/vault.ts +600 -0
  322. package/core/src/tui/__tests__/monitor.test.ts +336 -0
  323. package/core/src/tui/__tests__/real-world.test.ts +376 -0
  324. package/core/src/tui/__tests__/renderer.test.ts +201 -0
  325. package/core/src/tui/__tests__/types.test.ts +295 -0
  326. package/core/src/tui/index.ts +19 -0
  327. package/core/src/tui/monitor.ts +331 -0
  328. package/core/src/tui/renderer.ts +269 -0
  329. package/core/src/tui/types.ts +68 -0
  330. package/core/src/types/pack-v1.ts +305 -0
  331. package/core/src/types/pack-v2.ts +525 -0
  332. package/core/src/types/trust-score.ts +258 -0
  333. package/core/src/vault/__tests__/flakiness-vault.test.ts +562 -0
  334. package/core/src/vault/__tests__/vault.test.ts +259 -0
  335. package/core/src/vault/cas.ts +323 -0
  336. package/core/src/vault/index.ts +1361 -0
  337. package/core/src/vault/schema.sql +168 -0
  338. package/core/src/visual/README.md +185 -0
  339. package/core/src/visual/index.ts +14 -0
  340. package/core/src/visual/visual-regression.ts +347 -0
  341. package/core/src/watch/__tests__/watch-mode.test.ts +192 -0
  342. package/core/src/watch/index.ts +14 -0
  343. package/core/src/watch/watch-mode.ts +565 -0
  344. package/core/tsconfig.json +12 -0
  345. package/core/vitest.config.ts +52 -0
  346. package/docs/ARCHITECTURE.md +901 -0
  347. package/docs/AUDIT-GLOBAL-DEC2025.md +271 -0
  348. package/docs/BETA_TESTING.md +257 -0
  349. package/docs/BETA_TESTING_PLAN.md +727 -0
  350. package/docs/CERTIFICATION-REPORT.md +142 -0
  351. package/docs/COMPLETE_AUDIT_REFACTORING.md +965 -0
  352. package/docs/DEVELOPMENT.md +545 -0
  353. package/docs/DEVELOPMENT_HISTORY.md +345 -0
  354. package/docs/LIMITATIONS.md +176 -0
  355. package/docs/MIGRATION.md +303 -0
  356. package/docs/OPTION_3_4_EXPLORATION.md +1257 -0
  357. package/docs/PHASE1_PERFORMANCE.md +144 -0
  358. package/docs/QA360_Cloud.postman_collection.json +89 -0
  359. package/docs/QA360_TESTING_PHILOSOPHY.md +769 -0
  360. package/docs/QA_TEST_PLAN.md +727 -0
  361. package/docs/README.md +50 -0
  362. package/docs/STATUS.md +198 -0
  363. package/docs/STRATEGIC_STUDY_GOOSE_INTEGRATION.md +615 -0
  364. package/docs/USER_GUIDE.md +687 -0
  365. package/docs/WORK-DONE-ADAPTER-TESTS.md +136 -0
  366. package/docs/adapters-security.md +485 -0
  367. package/docs/architecture-diagram.mmd +168 -0
  368. package/docs/archive/ARCH-01-DAY6-BUILD-FIXES.md +396 -0
  369. package/docs/archive/ARCH-01-DAY6-FINAL-STATUS.md +324 -0
  370. package/docs/archive/ARCH-01_MCP_MERGE_ANALYSIS.md +644 -0
  371. package/docs/archive/ARCH-01_NEXT_STEPS.md +60 -0
  372. package/docs/archive/BRANCH_PROTECTION.md +183 -0
  373. package/docs/archive/CI_LOCKDOWN_CHECKLIST.md +222 -0
  374. package/docs/archive/HANDOFF_TEST-01.md +669 -0
  375. package/docs/archive/LEGAL_READY_PLACEHOLDERS.md +372 -0
  376. package/docs/archive/NODE_UPGRADE_GUIDE.md +188 -0
  377. package/docs/archive/PHASE1_COMPLETION.md +386 -0
  378. package/docs/archive/PHASE2_COMPLETION.md +404 -0
  379. package/docs/archive/PHASE3_AND_4_FINAL.md +360 -0
  380. package/docs/archive/PHASE3_COMPLETE.md +301 -0
  381. package/docs/archive/PHASE3_STATUS.md +255 -0
  382. package/docs/archive/PRE-WEEK2-AUDIT.md +364 -0
  383. package/docs/archive/README.md +16 -0
  384. package/docs/archive/SCHEMA_AJV_2020_FIX.md +245 -0
  385. package/docs/archive/TEST-01_AUDIT_REPORT.md +240 -0
  386. package/docs/archive/TEST-01_COVERAGE_PLAN.md +423 -0
  387. package/docs/archive/obsolete-proposals/dom-element-discovery-mode.md +250 -0
  388. package/docs/archive/obsolete-proposals/qa360-comprehensive-test-plan.md +1249 -0
  389. package/docs/archive/obsolete-proposals/qa360-quick-start-guide.md +298 -0
  390. package/docs/archive/obsolete-proposals/technical-plan-dom-discovery.md +870 -0
  391. package/docs/budgets-advanced.md +308 -0
  392. package/docs/examples/history-export-gc.md +285 -0
  393. package/docs/examples/pack-v2-complete.yaml +158 -0
  394. package/docs/examples/pack-v2-quickstart.yaml +24 -0
  395. package/docs/examples/pack-v2-ui-login.yaml +81 -0
  396. package/docs/examples/qa360-report.json +50 -0
  397. package/docs/history.md +565 -0
  398. package/docs/hooks.md +304 -0
  399. package/docs/llm-providers.md +512 -0
  400. package/docs/mcp-server.md +651 -0
  401. package/docs/mcp-tools.md +1131 -0
  402. package/docs/pack-v1.md +383 -0
  403. package/docs/pack-v2.md +558 -0
  404. package/docs/page-objects.md +366 -0
  405. package/docs/proofs.md +670 -0
  406. package/docs/quickstart-5min.md +257 -0
  407. package/docs/readiness-ci.md +654 -0
  408. package/docs/rfc/README.md +20 -0
  409. package/docs/rfc/proof-bundle-v1.md +787 -0
  410. package/docs/secrets.md +392 -0
  411. package/docs/serve.md +494 -0
  412. package/docs/unit-test-adapters.md +168 -0
  413. package/docs/vault.md +491 -0
  414. package/e2e/qa360-e2e.test.ts +696 -0
  415. package/e2e/vitest.config.ts +18 -0
  416. package/examples/README.md +30 -140
  417. package/examples/ci/docker-compose-serve.yml +375 -0
  418. package/examples/ci/github-actions-serve.yml +345 -0
  419. package/examples/ci/gitlab-ci-serve.yml +407 -0
  420. package/examples/datasets/README.md +101 -0
  421. package/examples/datasets/b2bshop.ts +155 -0
  422. package/examples/datasets/index.ts +57 -0
  423. package/examples/datasets/reqres.ts +195 -0
  424. package/examples/fixtures-demo/fixtures/users.yml +39 -0
  425. package/examples/fixtures-demo/pack.yml +71 -0
  426. package/examples/future-api/README.md +16 -0
  427. package/examples/future-api/diag.js +7 -0
  428. package/examples/future-api/health.js +4 -0
  429. package/examples/future-api/packs.js +13 -0
  430. package/examples/future-api/runpack.js +10 -0
  431. package/examples/generation/README.md +148 -0
  432. package/examples/generation/pack-generator-example.js +115 -0
  433. package/examples/generation/source-analyzer-example.js +115 -0
  434. package/examples/httpbin/pack.yml +59 -0
  435. package/examples/load-testing/mcp-load.yml +115 -0
  436. package/examples/load-testing/mcp-stdio.yml +95 -0
  437. package/examples/mcp/claude-desktop-config.json +33 -0
  438. package/examples/mcp/claude-desktop.json +16 -0
  439. package/examples/mcp/conversation-sample.md +131 -0
  440. package/examples/mcp/demo-60s.md +330 -0
  441. package/examples/mcp/sample-conversation.jsonl +21 -0
  442. package/examples/mcp/vscode-settings.json +22 -0
  443. package/examples/pack-v2-complete.yml +242 -0
  444. package/examples/pack-v2-examples.md +244 -0
  445. package/examples/pack-v2-quickstart.yml +55 -0
  446. package/examples/packs-business/ecommerce-api.yml +121 -0
  447. package/examples/packs-business/saas-dashboard-ui.yml +133 -0
  448. package/examples/packs-conformance/compose-multi.yml +174 -0
  449. package/examples/packs-conformance/full.yml +152 -0
  450. package/examples/packs-conformance/heavy-artifacts.yml +152 -0
  451. package/examples/packs-conformance/minimal.yml +71 -0
  452. package/examples/packs-conformance/secrets-missing.yml +97 -0
  453. package/examples/packs-conformance/timeouts.yml +77 -0
  454. package/examples/pom-demo/README.md +104 -0
  455. package/examples/pom-demo/pack.yml +60 -0
  456. package/examples/pom-demo/pages/DashboardPage.page.ts +73 -0
  457. package/examples/pom-demo/pages/LoginPage.page.ts +76 -0
  458. package/examples/proofs/e2e-playwright-proof.json +75 -0
  459. package/examples/proofs/httpbin-proof.json +69 -0
  460. package/examples/proofs/multi-adapter-proof.json +117 -0
  461. package/examples/proofs/test-proof.json +26 -0
  462. package/examples/restful-api-dev/README.md +102 -0
  463. package/examples/restful-api-dev/restful-api-advanced.yml +29 -0
  464. package/examples/restful-api-dev/restful-api-basic.yml +29 -0
  465. package/examples/web-lite/.github/workflows/qa360-phase3.yml +73 -0
  466. package/examples/web-lite/api-mock/server.js +258 -0
  467. package/examples/web-lite/pack.yml +71 -0
  468. package/examples/web-lite/services.yml +43 -0
  469. package/examples/web-lite/web-content/healthz +1 -0
  470. package/examples/web-lite/web-content/index.html +259 -0
  471. package/package.json +56 -45
  472. package/packages/mcp/CHANGELOG.md +109 -0
  473. package/packages/mcp/IMPLEMENTATION_SUMMARY.md +350 -0
  474. package/packages/mcp/LICENSE +21 -0
  475. package/packages/mcp/QUICK_START.md +291 -0
  476. package/packages/mcp/README.md +294 -0
  477. package/packages/mcp/TELEMETRY.md +220 -0
  478. package/packages/mcp/package.json +91 -0
  479. package/packages/mcp/scripts/generate-sbom-fallback.cjs +84 -0
  480. package/packages/mcp/scripts/safe-postinstall.cjs +32 -0
  481. package/packages/mcp/src/__tests__/contract.test.ts +902 -0
  482. package/packages/mcp/src/cli/cli.ts +137 -0
  483. package/packages/mcp/src/cli/doctor.ts +286 -0
  484. package/packages/mcp/src/cli/fix.ts +99 -0
  485. package/packages/mcp/src/cli/init.ts +233 -0
  486. package/packages/mcp/src/cli/postinstall.ts +14 -0
  487. package/packages/mcp/src/cli/reset.ts +44 -0
  488. package/packages/mcp/src/cli/telemetry.ts +166 -0
  489. package/packages/mcp/src/cli/test-dx.ts +94 -0
  490. package/packages/mcp/src/cli/uninstall.ts +80 -0
  491. package/packages/mcp/src/cli/up.ts +178 -0
  492. package/packages/mcp/src/index.ts +12 -0
  493. package/packages/mcp/src/scripts/e2e-local.ts +337 -0
  494. package/packages/mcp/src/scripts/verify-settings.ts +242 -0
  495. package/packages/mcp/src/security/audit.ts +244 -0
  496. package/packages/mcp/src/security/manager.ts +242 -0
  497. package/packages/mcp/src/server/full-server.ts +212 -0
  498. package/packages/mcp/src/server/minimal-server.ts +134 -0
  499. package/packages/mcp/src/tools/history.ts +388 -0
  500. package/packages/mcp/src/tools/pack.ts +449 -0
  501. package/packages/mcp/src/tools/registry.ts +638 -0
  502. package/packages/mcp/src/tools/report.ts +100 -0
  503. package/packages/mcp/src/tools/run.ts +268 -0
  504. package/packages/mcp/src/tools/secrets.ts +198 -0
  505. package/packages/mcp/src/tools/serve.ts +221 -0
  506. package/packages/mcp/src/tools/triage.ts +532 -0
  507. package/packages/mcp/src/tools/types.ts +26 -0
  508. package/packages/mcp/src/tools/vault.ts +164 -0
  509. package/packages/mcp/src/tools/verify.ts +166 -0
  510. package/packages/mcp/src/types/index.ts +311 -0
  511. package/packages/mcp/src/types/mcp-stubs.ts +83 -0
  512. package/packages/mcp/tsconfig.json +16 -0
  513. package/playwright.config.ts +20 -0
  514. package/pnpm-workspace.yaml +4 -0
  515. package/run-test-and-push.sh +20 -0
  516. package/scripts/build-proof-cli.sh +110 -0
  517. package/scripts/ci/check-windows-paths.js +92 -0
  518. package/scripts/ci/invariants.sh +124 -0
  519. package/scripts/ci/make-final-bundle.js +106 -0
  520. package/scripts/ci/mcp-run-multipack.js +305 -0
  521. package/scripts/ci/run-pack-suite.sh +103 -0
  522. package/scripts/ci/run-phase7-final.sh +190 -0
  523. package/scripts/ci/slo-assert.js +158 -0
  524. package/scripts/ci/test-fault-tolerance.sh +301 -0
  525. package/scripts/install-mcp.sh +66 -0
  526. package/scripts/mcp-smoke.mjs +27 -0
  527. package/scripts/smoke.sh +26 -0
  528. package/scripts/stress-test.js +288 -0
  529. package/scripts/sync-version.mjs +50 -0
  530. package/scripts/validate-examples.mjs +404 -0
  531. package/scripts/validation/simple-pack-check.sh +51 -0
  532. package/scripts/validation/validate-universal-pack.mjs +77 -0
  533. package/scripts/verify-persistence.js +127 -0
  534. package/test-pack.yaml +43 -0
  535. package/test-results/.last-run.json +4 -0
  536. package/test-runner.mjs +87 -0
  537. package/tests/artifacts.spec.js +147 -0
  538. package/tests/contracts.spec.js +239 -0
  539. package/tests/e2e/assertions.test.mjs +370 -0
  540. package/tests/e2e/crawler.test.mjs +451 -0
  541. package/tests/e2e/playwright-plus-plus.test.mjs +604 -0
  542. package/tests/e2e/proof-bundle.test.mjs +258 -0
  543. package/tests/e2e/real-world/saucedemo.test.mjs +714 -0
  544. package/tests/e2e/real-world/the-internet-herokuapp.test.mjs +760 -0
  545. package/tests/e2e/ui-actions.test.mjs +546 -0
  546. package/tests/gherkin.e2e.spec.ts +310 -0
  547. package/tests/no-console-errors.spec.js +136 -0
  548. package/tests/pdf.spec.ts +252 -0
  549. package/tests/run-pack.spec.ts +58 -0
  550. package/tsconfig.base.json +15 -0
  551. package/tsconfig.build.json +8 -0
  552. package/tsconfig.json +37 -0
  553. package/tsconfig.test.json +18 -0
  554. package/typedoc.json +37 -0
  555. package/ui/README.md +50 -0
  556. package/verify-proof.mjs +60 -0
  557. package/dist/cli-minimal.d.ts +0 -6
  558. package/dist/cli-minimal.js +0 -36
  559. package/dist/commands/ai.d.ts +0 -43
  560. package/dist/commands/ai.js +0 -616
  561. package/dist/commands/ask.d.ts +0 -94
  562. package/dist/commands/ask.js +0 -582
  563. package/dist/commands/coverage.d.ts +0 -8
  564. package/dist/commands/coverage.js +0 -252
  565. package/dist/commands/crawl.d.ts +0 -24
  566. package/dist/commands/crawl.js +0 -121
  567. package/dist/commands/doctor.d.ts +0 -54
  568. package/dist/commands/doctor.js +0 -513
  569. package/dist/commands/examples.d.ts +0 -33
  570. package/dist/commands/examples.js +0 -193
  571. package/dist/commands/explain.d.ts +0 -27
  572. package/dist/commands/explain.js +0 -630
  573. package/dist/commands/flakiness.d.ts +0 -73
  574. package/dist/commands/flakiness.js +0 -435
  575. package/dist/commands/generate.d.ts +0 -66
  576. package/dist/commands/generate.js +0 -438
  577. package/dist/commands/history.d.ts +0 -76
  578. package/dist/commands/history.js +0 -755
  579. package/dist/commands/init.d.ts +0 -106
  580. package/dist/commands/init.js +0 -616
  581. package/dist/commands/monitor.d.ts +0 -27
  582. package/dist/commands/monitor.js +0 -225
  583. package/dist/commands/ollama.d.ts +0 -40
  584. package/dist/commands/ollama.js +0 -301
  585. package/dist/commands/pack.d.ts +0 -70
  586. package/dist/commands/pack.js +0 -413
  587. package/dist/commands/regression.d.ts +0 -8
  588. package/dist/commands/regression.js +0 -340
  589. package/dist/commands/repair.d.ts +0 -26
  590. package/dist/commands/repair.js +0 -307
  591. package/dist/commands/report.d.ts +0 -62
  592. package/dist/commands/report.js +0 -378
  593. package/dist/commands/retry.d.ts +0 -43
  594. package/dist/commands/retry.js +0 -275
  595. package/dist/commands/run.d.ts +0 -41
  596. package/dist/commands/run.js +0 -169
  597. package/dist/commands/scan.d.ts +0 -5
  598. package/dist/commands/scan.js +0 -155
  599. package/dist/commands/secrets.d.ts +0 -58
  600. package/dist/commands/secrets.js +0 -289
  601. package/dist/commands/serve.d.ts +0 -13
  602. package/dist/commands/serve.js +0 -156
  603. package/dist/commands/slo.d.ts +0 -8
  604. package/dist/commands/slo.js +0 -327
  605. package/dist/commands/verify.d.ts +0 -32
  606. package/dist/commands/verify.js +0 -278
  607. package/dist/core/adapters/gitleaks-secrets.d.ts +0 -114
  608. package/dist/core/adapters/gitleaks-secrets.js +0 -410
  609. package/dist/core/adapters/k6-perf.d.ts +0 -85
  610. package/dist/core/adapters/k6-perf.js +0 -398
  611. package/dist/core/adapters/osv-deps.d.ts +0 -123
  612. package/dist/core/adapters/osv-deps.js +0 -372
  613. package/dist/core/adapters/playwright-native-adapter.d.ts +0 -121
  614. package/dist/core/adapters/playwright-native-adapter.js +0 -339
  615. package/dist/core/adapters/playwright-native-api.d.ts +0 -183
  616. package/dist/core/adapters/playwright-native-api.js +0 -465
  617. package/dist/core/adapters/playwright-ui.d.ts +0 -197
  618. package/dist/core/adapters/playwright-ui.js +0 -840
  619. package/dist/core/adapters/semgrep-sast.d.ts +0 -99
  620. package/dist/core/adapters/semgrep-sast.js +0 -322
  621. package/dist/core/adapters/zap-dast.d.ts +0 -133
  622. package/dist/core/adapters/zap-dast.js +0 -424
  623. package/dist/core/ai/anthropic-provider.d.ts +0 -50
  624. package/dist/core/ai/anthropic-provider.js +0 -223
  625. package/dist/core/ai/deepseek-provider.d.ts +0 -81
  626. package/dist/core/ai/deepseek-provider.js +0 -266
  627. package/dist/core/ai/index.d.ts +0 -60
  628. package/dist/core/ai/index.js +0 -18
  629. package/dist/core/ai/llm-client.d.ts +0 -45
  630. package/dist/core/ai/llm-client.js +0 -7
  631. package/dist/core/ai/mock-provider.d.ts +0 -49
  632. package/dist/core/ai/mock-provider.js +0 -121
  633. package/dist/core/ai/ollama-provider.d.ts +0 -78
  634. package/dist/core/ai/ollama-provider.js +0 -204
  635. package/dist/core/ai/openai-provider.d.ts +0 -48
  636. package/dist/core/ai/openai-provider.js +0 -200
  637. package/dist/core/ai/provider-factory.d.ts +0 -160
  638. package/dist/core/ai/provider-factory.js +0 -269
  639. package/dist/core/artifacts/index.d.ts +0 -6
  640. package/dist/core/artifacts/index.js +0 -6
  641. package/dist/core/artifacts/ui-artifacts.d.ts +0 -133
  642. package/dist/core/artifacts/ui-artifacts.js +0 -304
  643. package/dist/core/assertions/engine.d.ts +0 -51
  644. package/dist/core/assertions/engine.js +0 -530
  645. package/dist/core/assertions/index.d.ts +0 -11
  646. package/dist/core/assertions/index.js +0 -11
  647. package/dist/core/assertions/types.d.ts +0 -121
  648. package/dist/core/assertions/types.js +0 -37
  649. package/dist/core/auth/api-key-provider.d.ts +0 -16
  650. package/dist/core/auth/api-key-provider.js +0 -63
  651. package/dist/core/auth/aws-iam-provider.d.ts +0 -35
  652. package/dist/core/auth/aws-iam-provider.js +0 -177
  653. package/dist/core/auth/azure-ad-provider.d.ts +0 -15
  654. package/dist/core/auth/azure-ad-provider.js +0 -99
  655. package/dist/core/auth/basic-auth-provider.d.ts +0 -26
  656. package/dist/core/auth/basic-auth-provider.js +0 -111
  657. package/dist/core/auth/gcp-adc-provider.d.ts +0 -27
  658. package/dist/core/auth/gcp-adc-provider.js +0 -126
  659. package/dist/core/auth/index.d.ts +0 -238
  660. package/dist/core/auth/index.js +0 -82
  661. package/dist/core/auth/jwt-provider.d.ts +0 -19
  662. package/dist/core/auth/jwt-provider.js +0 -160
  663. package/dist/core/auth/manager.d.ts +0 -84
  664. package/dist/core/auth/manager.js +0 -230
  665. package/dist/core/auth/oauth2-provider.d.ts +0 -17
  666. package/dist/core/auth/oauth2-provider.js +0 -114
  667. package/dist/core/auth/totp-provider.d.ts +0 -31
  668. package/dist/core/auth/totp-provider.js +0 -134
  669. package/dist/core/auth/ui-login-provider.d.ts +0 -26
  670. package/dist/core/auth/ui-login-provider.js +0 -198
  671. package/dist/core/cache/index.d.ts +0 -7
  672. package/dist/core/cache/index.js +0 -6
  673. package/dist/core/cache/lru-cache.d.ts +0 -203
  674. package/dist/core/cache/lru-cache.js +0 -397
  675. package/dist/core/core/coverage/analyzer.d.ts +0 -101
  676. package/dist/core/core/coverage/analyzer.js +0 -415
  677. package/dist/core/core/coverage/collector.d.ts +0 -74
  678. package/dist/core/core/coverage/collector.js +0 -459
  679. package/dist/core/core/coverage/config.d.ts +0 -37
  680. package/dist/core/core/coverage/config.js +0 -156
  681. package/dist/core/core/coverage/index.d.ts +0 -11
  682. package/dist/core/core/coverage/index.js +0 -15
  683. package/dist/core/core/coverage/types.d.ts +0 -267
  684. package/dist/core/core/coverage/types.js +0 -6
  685. package/dist/core/core/coverage/vault.d.ts +0 -95
  686. package/dist/core/core/coverage/vault.js +0 -405
  687. package/dist/core/coverage/analyzer.d.ts +0 -101
  688. package/dist/core/coverage/analyzer.js +0 -415
  689. package/dist/core/coverage/collector.d.ts +0 -74
  690. package/dist/core/coverage/collector.js +0 -459
  691. package/dist/core/coverage/config.d.ts +0 -37
  692. package/dist/core/coverage/config.js +0 -156
  693. package/dist/core/coverage/index.d.ts +0 -11
  694. package/dist/core/coverage/index.js +0 -15
  695. package/dist/core/coverage/types.d.ts +0 -267
  696. package/dist/core/coverage/types.js +0 -6
  697. package/dist/core/coverage/vault.d.ts +0 -95
  698. package/dist/core/coverage/vault.js +0 -405
  699. package/dist/core/crawler/index.d.ts +0 -57
  700. package/dist/core/crawler/index.js +0 -281
  701. package/dist/core/crawler/journey-generator.d.ts +0 -49
  702. package/dist/core/crawler/journey-generator.js +0 -412
  703. package/dist/core/crawler/page-analyzer.d.ts +0 -88
  704. package/dist/core/crawler/page-analyzer.js +0 -709
  705. package/dist/core/crawler/selector-generator.d.ts +0 -34
  706. package/dist/core/crawler/selector-generator.js +0 -240
  707. package/dist/core/crawler/types.d.ts +0 -353
  708. package/dist/core/crawler/types.js +0 -6
  709. package/dist/core/dashboard/assets.d.ts +0 -6
  710. package/dist/core/dashboard/assets.js +0 -690
  711. package/dist/core/dashboard/index.d.ts +0 -6
  712. package/dist/core/dashboard/index.js +0 -5
  713. package/dist/core/dashboard/server.d.ts +0 -72
  714. package/dist/core/dashboard/server.js +0 -354
  715. package/dist/core/dashboard/types.d.ts +0 -70
  716. package/dist/core/dashboard/types.js +0 -5
  717. package/dist/core/discoverer/index.d.ts +0 -115
  718. package/dist/core/discoverer/index.js +0 -250
  719. package/dist/core/flakiness/index.d.ts +0 -228
  720. package/dist/core/flakiness/index.js +0 -384
  721. package/dist/core/generation/code-formatter.d.ts +0 -111
  722. package/dist/core/generation/code-formatter.js +0 -307
  723. package/dist/core/generation/code-generator.d.ts +0 -144
  724. package/dist/core/generation/code-generator.js +0 -293
  725. package/dist/core/generation/crawler-pack-generator.d.ts +0 -44
  726. package/dist/core/generation/crawler-pack-generator.js +0 -231
  727. package/dist/core/generation/generator.d.ts +0 -40
  728. package/dist/core/generation/generator.js +0 -76
  729. package/dist/core/generation/index.d.ts +0 -32
  730. package/dist/core/generation/index.js +0 -30
  731. package/dist/core/generation/pack-generator.d.ts +0 -107
  732. package/dist/core/generation/pack-generator.js +0 -416
  733. package/dist/core/generation/prompt-builder.d.ts +0 -132
  734. package/dist/core/generation/prompt-builder.js +0 -672
  735. package/dist/core/generation/source-analyzer.d.ts +0 -213
  736. package/dist/core/generation/source-analyzer.js +0 -657
  737. package/dist/core/generation/test-optimizer.d.ts +0 -117
  738. package/dist/core/generation/test-optimizer.js +0 -328
  739. package/dist/core/generation/types.d.ts +0 -214
  740. package/dist/core/generation/types.js +0 -4
  741. package/dist/core/hooks/compose.d.ts +0 -61
  742. package/dist/core/hooks/compose.js +0 -225
  743. package/dist/core/hooks/runner.d.ts +0 -68
  744. package/dist/core/hooks/runner.js +0 -303
  745. package/dist/core/index.d.ts +0 -104
  746. package/dist/core/index.js +0 -91
  747. package/dist/core/pack/migrator.d.ts +0 -51
  748. package/dist/core/pack/migrator.js +0 -304
  749. package/dist/core/pack/validator.d.ts +0 -42
  750. package/dist/core/pack/validator.js +0 -322
  751. package/dist/core/pack-v2/index.d.ts +0 -9
  752. package/dist/core/pack-v2/index.js +0 -8
  753. package/dist/core/pack-v2/loader.d.ts +0 -63
  754. package/dist/core/pack-v2/loader.js +0 -292
  755. package/dist/core/pack-v2/migrator.d.ts +0 -61
  756. package/dist/core/pack-v2/migrator.js +0 -480
  757. package/dist/core/pack-v2/validator.d.ts +0 -61
  758. package/dist/core/pack-v2/validator.js +0 -577
  759. package/dist/core/parallel/index.d.ts +0 -6
  760. package/dist/core/parallel/index.js +0 -6
  761. package/dist/core/parallel/parallel-runner.d.ts +0 -107
  762. package/dist/core/parallel/parallel-runner.js +0 -192
  763. package/dist/core/proof/bundle.d.ts +0 -137
  764. package/dist/core/proof/bundle.js +0 -160
  765. package/dist/core/proof/canonicalize.d.ts +0 -47
  766. package/dist/core/proof/canonicalize.js +0 -105
  767. package/dist/core/proof/index.d.ts +0 -13
  768. package/dist/core/proof/index.js +0 -18
  769. package/dist/core/proof/schema.d.ts +0 -217
  770. package/dist/core/proof/schema.js +0 -263
  771. package/dist/core/proof/signer.d.ts +0 -111
  772. package/dist/core/proof/signer.js +0 -226
  773. package/dist/core/proof/verifier.d.ts +0 -97
  774. package/dist/core/proof/verifier.js +0 -308
  775. package/dist/core/regression/detector.d.ts +0 -107
  776. package/dist/core/regression/detector.js +0 -497
  777. package/dist/core/regression/index.d.ts +0 -9
  778. package/dist/core/regression/index.js +0 -11
  779. package/dist/core/regression/trend-analyzer.d.ts +0 -102
  780. package/dist/core/regression/trend-analyzer.js +0 -345
  781. package/dist/core/regression/types.d.ts +0 -222
  782. package/dist/core/regression/types.js +0 -7
  783. package/dist/core/regression/vault.d.ts +0 -87
  784. package/dist/core/regression/vault.js +0 -289
  785. package/dist/core/repair/engine/fixer.d.ts +0 -24
  786. package/dist/core/repair/engine/fixer.js +0 -226
  787. package/dist/core/repair/engine/suggestion-engine.d.ts +0 -18
  788. package/dist/core/repair/engine/suggestion-engine.js +0 -187
  789. package/dist/core/repair/index.d.ts +0 -10
  790. package/dist/core/repair/index.js +0 -13
  791. package/dist/core/repair/repairer.d.ts +0 -90
  792. package/dist/core/repair/repairer.js +0 -284
  793. package/dist/core/repair/types.d.ts +0 -91
  794. package/dist/core/repair/types.js +0 -6
  795. package/dist/core/repair/utils/error-analyzer.d.ts +0 -28
  796. package/dist/core/repair/utils/error-analyzer.js +0 -264
  797. package/dist/core/reporting/html-reporter.d.ts +0 -119
  798. package/dist/core/reporting/html-reporter.js +0 -737
  799. package/dist/core/reporting/index.d.ts +0 -6
  800. package/dist/core/reporting/index.js +0 -6
  801. package/dist/core/retry/flakiness-integration.d.ts +0 -60
  802. package/dist/core/retry/flakiness-integration.js +0 -228
  803. package/dist/core/retry/index.d.ts +0 -14
  804. package/dist/core/retry/index.js +0 -16
  805. package/dist/core/retry/retry-engine.d.ts +0 -80
  806. package/dist/core/retry/retry-engine.js +0 -296
  807. package/dist/core/retry/types.d.ts +0 -178
  808. package/dist/core/retry/types.js +0 -52
  809. package/dist/core/retry/vault.d.ts +0 -77
  810. package/dist/core/retry/vault.js +0 -304
  811. package/dist/core/runner/e2e-helpers.d.ts +0 -102
  812. package/dist/core/runner/e2e-helpers.js +0 -153
  813. package/dist/core/runner/phase3-runner.d.ts +0 -200
  814. package/dist/core/runner/phase3-runner.js +0 -1041
  815. package/dist/core/secrets/crypto.d.ts +0 -75
  816. package/dist/core/secrets/crypto.js +0 -223
  817. package/dist/core/secrets/manager.d.ts +0 -76
  818. package/dist/core/secrets/manager.js +0 -219
  819. package/dist/core/security/redaction-patterns-extended.d.ts +0 -27
  820. package/dist/core/security/redaction-patterns-extended.js +0 -247
  821. package/dist/core/security/redactor.d.ts +0 -71
  822. package/dist/core/security/redactor.js +0 -279
  823. package/dist/core/self-healing/assertion-healer.d.ts +0 -97
  824. package/dist/core/self-healing/assertion-healer.js +0 -371
  825. package/dist/core/self-healing/engine.d.ts +0 -122
  826. package/dist/core/self-healing/engine.js +0 -538
  827. package/dist/core/self-healing/index.d.ts +0 -10
  828. package/dist/core/self-healing/index.js +0 -11
  829. package/dist/core/self-healing/selector-healer.d.ts +0 -103
  830. package/dist/core/self-healing/selector-healer.js +0 -372
  831. package/dist/core/self-healing/types.d.ts +0 -152
  832. package/dist/core/self-healing/types.js +0 -6
  833. package/dist/core/serve/diagnostics-collector.d.ts +0 -32
  834. package/dist/core/serve/diagnostics-collector.js +0 -149
  835. package/dist/core/serve/health-checker.d.ts +0 -44
  836. package/dist/core/serve/health-checker.js +0 -219
  837. package/dist/core/serve/index.d.ts +0 -8
  838. package/dist/core/serve/index.js +0 -8
  839. package/dist/core/serve/metrics-collector.d.ts +0 -24
  840. package/dist/core/serve/metrics-collector.js +0 -322
  841. package/dist/core/serve/process-manager.d.ts +0 -36
  842. package/dist/core/serve/process-manager.js +0 -213
  843. package/dist/core/serve/server.d.ts +0 -36
  844. package/dist/core/serve/server.js +0 -191
  845. package/dist/core/slo/config.d.ts +0 -107
  846. package/dist/core/slo/config.js +0 -360
  847. package/dist/core/slo/index.d.ts +0 -11
  848. package/dist/core/slo/index.js +0 -15
  849. package/dist/core/slo/sli-calculator.d.ts +0 -92
  850. package/dist/core/slo/sli-calculator.js +0 -364
  851. package/dist/core/slo/slo-tracker.d.ts +0 -148
  852. package/dist/core/slo/slo-tracker.js +0 -379
  853. package/dist/core/slo/types.d.ts +0 -281
  854. package/dist/core/slo/types.js +0 -7
  855. package/dist/core/slo/vault.d.ts +0 -102
  856. package/dist/core/slo/vault.js +0 -427
  857. package/dist/core/tui/index.d.ts +0 -7
  858. package/dist/core/tui/index.js +0 -6
  859. package/dist/core/tui/monitor.d.ts +0 -92
  860. package/dist/core/tui/monitor.js +0 -271
  861. package/dist/core/tui/renderer.d.ts +0 -33
  862. package/dist/core/tui/renderer.js +0 -218
  863. package/dist/core/tui/types.d.ts +0 -63
  864. package/dist/core/tui/types.js +0 -5
  865. package/dist/core/types/pack-v1.d.ts +0 -251
  866. package/dist/core/types/pack-v1.js +0 -5
  867. package/dist/core/types/pack-v2.d.ts +0 -425
  868. package/dist/core/types/pack-v2.js +0 -8
  869. package/dist/core/types/trust-score.d.ts +0 -69
  870. package/dist/core/types/trust-score.js +0 -191
  871. package/dist/core/vault/cas.d.ts +0 -90
  872. package/dist/core/vault/cas.js +0 -261
  873. package/dist/core/vault/index.d.ts +0 -326
  874. package/dist/core/vault/index.js +0 -1042
  875. package/dist/core/visual/index.d.ts +0 -6
  876. package/dist/core/visual/index.js +0 -6
  877. package/dist/core/visual/visual-regression.d.ts +0 -113
  878. package/dist/core/visual/visual-regression.js +0 -236
  879. package/dist/core/watch/index.d.ts +0 -7
  880. package/dist/core/watch/index.js +0 -6
  881. package/dist/core/watch/watch-mode.d.ts +0 -213
  882. package/dist/core/watch/watch-mode.js +0 -389
  883. package/dist/generators/index.d.ts +0 -5
  884. package/dist/generators/index.js +0 -5
  885. package/dist/generators/json-reporter.d.ts +0 -10
  886. package/dist/generators/json-reporter.js +0 -12
  887. package/dist/generators/test-generator.d.ts +0 -18
  888. package/dist/generators/test-generator.js +0 -78
  889. package/dist/index.d.ts +0 -8
  890. package/dist/index.js +0 -246
  891. package/dist/scanners/dom-scanner.d.ts +0 -52
  892. package/dist/scanners/dom-scanner.js +0 -296
  893. package/dist/scanners/index.d.ts +0 -4
  894. package/dist/scanners/index.js +0 -4
  895. package/dist/schemas/pack.schema.json +0 -236
  896. package/dist/types/scan.d.ts +0 -68
  897. package/dist/types/scan.js +0 -4
  898. package/dist/utils/config.d.ts +0 -5
  899. package/dist/utils/config.js +0 -136
  900. /package/{bin → cli/bin}/qa360.js +0 -0
  901. /package/{examples → cli/examples}/accessibility.yml +0 -0
  902. /package/{examples → cli/examples}/api-basic.yml +0 -0
  903. /package/{examples → cli/examples}/complete.yml +0 -0
  904. /package/{examples → cli/examples}/crawler.yml +0 -0
  905. /package/{examples → cli/examples}/fullstack.yml +0 -0
  906. /package/{examples → cli/examples}/security.yml +0 -0
  907. /package/{examples → cli/examples}/ui-advanced.yml +0 -0
  908. /package/{examples → cli/examples}/ui-basic.yml +0 -0
  909. /package/{dist/core → core}/schemas/pack.schema.json +0 -0
@@ -0,0 +1,1536 @@
1
+ /**
2
+ * QA360 Phase 3 Runner
3
+ * Orchestrates hooks, adapters, and proof generation
4
+ * Supports both Pack v1 and Pack v2 configurations
5
+ */
6
+
7
+ import { existsSync, writeFileSync, mkdirSync } from 'fs';
8
+ import { join, resolve } from 'path';
9
+ import { createHash } from 'crypto';
10
+ import chalk from 'chalk';
11
+ import { PackConfigV1 } from '../types/pack-v1.js';
12
+ import { PackConfigV2, AuthConfigV2, GateConfigV2 } from '../types/pack-v2.js';
13
+ import { HooksRunner } from '../hooks/runner.js';
14
+ import { PlaywrightNativeApiAdapter } from '../adapters/playwright-native-api.js';
15
+ import { PlaywrightUiAdapter } from '../adapters/playwright-ui.js';
16
+ import { K6PerfAdapter } from '../adapters/k6-perf.js';
17
+ import { SemgrepSastAdapter } from '../adapters/semgrep-sast.js';
18
+ // v2.2.0 Unit Test Adapters
19
+ import { VitestAdapter } from '../adapters/vitest-adapter.js';
20
+ import { JestAdapter } from '../adapters/jest-adapter.js';
21
+ import { PytestAdapter } from '../adapters/pytest-adapter.js';
22
+ // v2.2.0 Data Fixtures
23
+ import { FixtureLoader } from '../fixtures/loader.js';
24
+ import { FixtureResolver } from '../fixtures/resolver.js';
25
+ import { PageObjectLoader } from '../pom/loader.js';
26
+ import { SecurityRedactor } from '../security/redactor.js';
27
+ import { initializeKeys, sign, KeyPair } from '../proof/signer.js';
28
+ import { canonicalize } from '../proof/canonicalize.js';
29
+ import { EvidenceVault, RunRecord, GateRecord, FindingRecord } from '../vault/index.js';
30
+ import { AuthManager, AuthConfig, AuthCredentials } from '../auth/index.js';
31
+ import {
32
+ FlakinessDetector,
33
+ TestResult as FlakyTestResult,
34
+ FlakinessResult,
35
+ FlakinessCategory,
36
+ formatFlakinessScore,
37
+ generateTestId,
38
+ DEFAULT_FLAKINESS_OPTIONS
39
+ } from '../flakiness/index.js';
40
+
41
+ export type PackConfig = PackConfigV1 | PackConfigV2;
42
+
43
+ export interface Phase3RunnerOptions {
44
+ workingDir: string;
45
+ pack: PackConfig;
46
+ packDir?: string; // Directory containing the pack file (for resolving fixtures)
47
+ outputDir?: string;
48
+ /** Enable flakiness detection (runs tests N times consecutively) */
49
+ flakyDetect?: boolean;
50
+ /** Number of consecutive runs for flakiness detection (default: 3) */
51
+ flakyRuns?: number;
52
+ }
53
+
54
+ export interface GateResult {
55
+ gate: string;
56
+ success: boolean;
57
+ duration: number;
58
+ adapter: string;
59
+ results: any;
60
+ junit?: string;
61
+ error?: string;
62
+ }
63
+
64
+ export interface Phase3RunResult {
65
+ success: boolean;
66
+ pack: PackConfig;
67
+ duration: number;
68
+ gates: GateResult[];
69
+ hooks: {
70
+ beforeAll: any[];
71
+ beforeEach: any[];
72
+ afterEach: any[];
73
+ afterAll: any[];
74
+ };
75
+ summary: {
76
+ total: number;
77
+ passed: number;
78
+ failed: number;
79
+ trustScore: number;
80
+ };
81
+ /** Flakiness analysis results (if enabled) */
82
+ flakiness?: FlakinessResult[];
83
+ proofPath?: string;
84
+ /** Vault run ID for this execution */
85
+ runId?: string;
86
+ error?: string;
87
+ }
88
+
89
+ export class Phase3Runner {
90
+ private workingDir: string;
91
+ private pack: PackConfig;
92
+ private packDir: string; // Directory containing the pack file
93
+ private outputDir: string;
94
+ private redactor: SecurityRedactor;
95
+ private hooksRunner: HooksRunner;
96
+ private keyPair?: KeyPair;
97
+ private vault?: EvidenceVault;
98
+ private authManager: AuthManager;
99
+ private authCredentialsCache = new Map<string, AuthCredentials>();
100
+ private flakyDetect: boolean;
101
+ private flakyRuns: number;
102
+ private flakinessDetector: FlakinessDetector;
103
+ // v2.2.0 Data Fixtures & POM
104
+ private fixtureLoader?: FixtureLoader;
105
+ private fixtureResolver?: FixtureResolver;
106
+ private pageObjectLoader?: PageObjectLoader;
107
+
108
+ constructor(options: Phase3RunnerOptions) {
109
+ this.workingDir = options.workingDir;
110
+ this.pack = options.pack;
111
+ this.packDir = options.packDir || options.workingDir;
112
+ this.outputDir = options.outputDir || join(this.workingDir, '.qa360', 'runs');
113
+ this.redactor = SecurityRedactor.forLogs();
114
+ this.authManager = new AuthManager();
115
+ this.flakyDetect = options.flakyDetect || false;
116
+ this.flakyRuns = options.flakyRuns || DEFAULT_FLAKINESS_OPTIONS.consecutiveRuns;
117
+ this.flakinessDetector = new FlakinessDetector({
118
+ consecutiveRuns: this.flakyRuns,
119
+ minRuns: 2,
120
+ enablePatternDetection: true
121
+ });
122
+
123
+ // Initialize hooks runner (convert v2 hooks to v1 format if needed)
124
+ const hooks = this.isPackV2(options.pack)
125
+ ? this.convertV2HooksToV1(options.pack.hooks)
126
+ : options.pack.hooks;
127
+
128
+ this.hooksRunner = new HooksRunner({
129
+ workingDir: this.workingDir,
130
+ hooks: hooks || {},
131
+ execution: this.isPackV2(options.pack) ? this.convertV2ExecutionToV1(options.pack.execution) : options.pack.execution,
132
+ redactor: this.redactor
133
+ });
134
+
135
+ // Register auth profiles from pack v2
136
+ if (this.isPackV2(options.pack) && options.pack.auth?.profiles) {
137
+ this.registerAuthProfiles(options.pack.auth.profiles);
138
+ }
139
+
140
+ // Load data fixtures from pack v2
141
+ if (this.isPackV2(options.pack) && options.pack.fixtures && options.pack.fixtures.length > 0) {
142
+ this.initializeFixtures(options.pack.fixtures);
143
+ }
144
+
145
+ // Initialize Page Object Model from pack v2
146
+ if (this.isPackV2(options.pack) && options.pack.pageObjects) {
147
+ this.initializePageObjects(options.pack.pageObjects);
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Initialize Page Object Model from the pack configuration
153
+ */
154
+ private initializePageObjects(config: { directory?: string; pattern?: string; baseUrl?: string }): void {
155
+ try {
156
+ const pomDir = config.directory ? resolve(this.packDir, config.directory) : this.packDir;
157
+ this.pageObjectLoader = new PageObjectLoader({ cwd: pomDir });
158
+ console.log(chalk.gray(` 📄 POM loader initialized for: ${pomDir}`));
159
+ } catch (error) {
160
+ console.log(chalk.yellow(`⚠️ Failed to initialize POM loader: ${error instanceof Error ? error.message : 'Unknown error'}`));
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Load Page Objects before execution
166
+ */
167
+ private async loadPageObjects(): Promise<void> {
168
+ if (!this.pageObjectLoader || !this.isPackV2(this.pack) || !this.pack.pageObjects) {
169
+ return;
170
+ }
171
+
172
+ try {
173
+ const pattern = this.pack.pageObjects.pattern || '**/*.page.{ts,js}';
174
+ const result = await this.pageObjectLoader.loadFromDirectory(this.packDir, { pattern });
175
+ console.log(chalk.green(` ✅ Page Objects loaded: ${result.pagesCount} pages`));
176
+ } catch (error) {
177
+ console.log(chalk.yellow(`⚠️ Failed to load page objects: ${error instanceof Error ? error.message : 'Unknown error'}`));
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Initialize data fixtures from the pack configuration
183
+ */
184
+ private initializeFixtures(fixturePaths: string[]): void {
185
+ try {
186
+ this.fixtureLoader = new FixtureLoader(this.packDir);
187
+ // Load fixtures synchronously - they're already validated
188
+ // We'll load them before execution starts
189
+ this.fixtureResolver = new FixtureResolver(this.fixtureLoader, this.packDir);
190
+ } catch (error) {
191
+ console.log(chalk.yellow(`⚠️ Failed to initialize fixtures: ${error instanceof Error ? error.message : 'Unknown error'}`));
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Load fixtures before execution
197
+ */
198
+ private async loadFixtures(): Promise<void> {
199
+ if (!this.fixtureLoader || !this.isPackV2(this.pack) || !this.pack.fixtures) {
200
+ return;
201
+ }
202
+
203
+ try {
204
+ await this.fixtureLoader.load(this.pack.fixtures);
205
+ console.log(chalk.green(` ✅ Fixtures loaded: ${this.fixtureLoader.getFixtureNames().join(', ')}`));
206
+ } catch (error) {
207
+ console.log(chalk.yellow(`⚠️ Failed to load fixtures: ${error instanceof Error ? error.message : 'Unknown error'}`));
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Resolve fixture references in a configuration object
213
+ */
214
+ private resolveFixturesInConfig(config: any): any {
215
+ if (!this.fixtureResolver || !config) {
216
+ return config;
217
+ }
218
+
219
+ try {
220
+ const result = this.fixtureResolver.resolve(config, {
221
+ baseDir: this.workingDir,
222
+ keepUnresolved: false
223
+ });
224
+
225
+ if (result.hasFixtures) {
226
+ console.log(chalk.gray(` 🔧 Fixtures resolved in config`));
227
+ }
228
+
229
+ return result.value;
230
+ } catch (error) {
231
+ console.log(chalk.yellow(`⚠️ Failed to resolve fixtures: ${error instanceof Error ? error.message : 'Unknown error'}`));
232
+ return config;
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Type guard to check if pack is v2
238
+ */
239
+ private isPackV2(pack: PackConfig): pack is PackConfigV2 {
240
+ return (pack as PackConfigV2).version === 2;
241
+ }
242
+
243
+ /**
244
+ * Register authentication profiles from pack config
245
+ */
246
+ private registerAuthProfiles(profiles: Record<string, any>): void {
247
+ for (const [name, profile] of Object.entries(profiles)) {
248
+ // Convert v2 auth profile to auth module format
249
+ const authConfig: AuthConfig = {
250
+ type: profile.type || 'none',
251
+ ...profile.config
252
+ };
253
+ this.authManager.registerProfile(name, authConfig);
254
+ }
255
+ }
256
+
257
+ /**
258
+ * Get auth profile name for a specific gate
259
+ */
260
+ private getAuthProfileForGate(gateName: string): string | undefined {
261
+ if (!this.isPackV2(this.pack) || !this.pack.auth) {
262
+ return undefined;
263
+ }
264
+
265
+ const authConfig = this.pack.auth as AuthConfigV2;
266
+
267
+ // Check if gate has specific auth override
268
+ const gateConfig = (this.pack.gates as Record<string, GateConfigV2>)[gateName];
269
+ if (gateConfig?.auth) {
270
+ return gateConfig.auth;
271
+ }
272
+
273
+ // Use default based on gate type
274
+ if (gateName === 'api_smoke' || gateName === 'api') {
275
+ return authConfig.api;
276
+ }
277
+ if (gateName === 'ui' || gateName === 'a11y') {
278
+ return authConfig.ui;
279
+ }
280
+
281
+ return undefined;
282
+ }
283
+
284
+ /**
285
+ * Authenticate and get credentials for a gate
286
+ */
287
+ private async getCredentialsForGate(gateName: string): Promise<AuthCredentials | undefined> {
288
+ const profileName = this.getAuthProfileForGate(gateName);
289
+
290
+ if (!profileName) {
291
+ return undefined;
292
+ }
293
+
294
+ // Check cache first
295
+ if (this.authCredentialsCache.has(profileName)) {
296
+ return this.authCredentialsCache.get(profileName);
297
+ }
298
+
299
+ // Authenticate
300
+ const result = await this.authManager.authenticate(profileName);
301
+
302
+ if (result.success && result.credentials) {
303
+ this.authCredentialsCache.set(profileName, result.credentials);
304
+ return result.credentials;
305
+ }
306
+
307
+ console.log(chalk.yellow(` ⚠️ Auth failed for profile '${profileName}': ${result.error}`));
308
+ return undefined;
309
+ }
310
+
311
+ /**
312
+ * Convert v2 hooks to v1 format
313
+ */
314
+ private convertV2HooksToV1(hooks?: any): any {
315
+ if (!hooks) return undefined;
316
+
317
+ // v2 hooks use { type, command, ... } format
318
+ // v1 hooks use { run, timeout } format
319
+ const converted: any = {
320
+ beforeAll: [],
321
+ afterAll: [],
322
+ beforeEach: [],
323
+ afterEach: []
324
+ };
325
+
326
+ for (const [phase, phaseHooks] of Object.entries(hooks)) {
327
+ if (Array.isArray(phaseHooks)) {
328
+ converted[phase] = (phaseHooks as any[]).map((h: any) => {
329
+ if (h.type === 'run' || h.type === 'script') {
330
+ return { run: h.command, timeout: h.timeout, cwd: h.cwd, env: h.env };
331
+ }
332
+ if (h.type === 'wait_on') {
333
+ return { run: `npx wait-on ${h.wait_for?.resource || h.command}`, timeout: h.timeout };
334
+ }
335
+ if (h.type === 'docker') {
336
+ return { run: `docker compose ${h.compose?.command || 'up -d'}`, timeout: h.timeout };
337
+ }
338
+ return h;
339
+ });
340
+ }
341
+ }
342
+
343
+ return converted;
344
+ }
345
+
346
+ /**
347
+ * Convert v2 execution config to v1 format
348
+ */
349
+ private convertV2ExecutionToV1(execution?: any): any {
350
+ if (!execution) return undefined;
351
+
352
+ return {
353
+ timeout: execution.default_timeout || execution.timeout,
354
+ max_retries: execution.default_retries || execution.retries,
355
+ on_failure: execution.on_failure || 'continue'
356
+ };
357
+ }
358
+
359
+ /**
360
+ * Get gates array from pack (handles v1 and v2)
361
+ */
362
+ private getGatesArray(): string[] {
363
+ if (this.isPackV2(this.pack)) {
364
+ return Object.keys(this.pack.gates).filter(gateName => {
365
+ const gateConfig = (this.pack.gates as Record<string, GateConfigV2>)[gateName];
366
+ return gateConfig?.enabled !== false;
367
+ });
368
+ }
369
+ return (this.pack as PackConfigV1).gates || [];
370
+ }
371
+
372
+ /**
373
+ * Execute complete Phase 3 workflow
374
+ */
375
+ async run(): Promise<Phase3RunResult> {
376
+ const startTime = Date.now();
377
+
378
+ const gatesArray = this.getGatesArray();
379
+
380
+ console.log(chalk.bold.blue(`\n🚀 QA360 Phase 3 Runner - ${this.pack.name}`));
381
+ console.log(chalk.gray(`Gates: ${gatesArray.join(', ')}`));
382
+
383
+ try {
384
+ // Ensure output directory exists
385
+ this.ensureOutputDir();
386
+
387
+ // Load data fixtures if configured
388
+ await this.loadFixtures();
389
+
390
+ // Load page objects if configured
391
+ await this.loadPageObjects();
392
+
393
+ // Initialize cryptographic keys
394
+ console.log(chalk.blue('\n🔑 Initializing Ed25519 keys...'));
395
+ this.keyPair = await initializeKeys();
396
+ console.log(chalk.green(' ✅ Keys ready'));
397
+
398
+ // Initialize Evidence Vault
399
+ console.log(chalk.blue('\n🗄️ Initializing Evidence Vault...'));
400
+ const vaultDir = join(this.workingDir, '.qa360');
401
+ this.vault = await EvidenceVault.open(vaultDir);
402
+ console.log(chalk.green(' ✅ Vault ready'));
403
+
404
+ // Execute beforeAll hooks
405
+ console.log(chalk.blue('\n🔗 Phase 1: Setup Hooks'));
406
+ const beforeAllResults = await this.hooksRunner.executeHooks('beforeAll');
407
+
408
+ // Check if setup failed and should stop
409
+ const setupFailed = beforeAllResults.some(r => !r.success);
410
+ if (setupFailed && this.pack.execution?.on_failure === 'stop') {
411
+ throw new Error('Setup hooks failed, stopping execution');
412
+ }
413
+
414
+ // Execute gates
415
+ console.log(chalk.blue('\n🎯 Phase 2: Quality Gates'));
416
+ const gateResults: GateResult[] = [];
417
+
418
+ for (const gate of gatesArray) {
419
+ const gateResult = await this.executeGate(gate);
420
+ gateResults.push(gateResult);
421
+
422
+ // Check if gate failed and should stop
423
+ if (!gateResult.success && this.pack.execution?.on_failure === 'stop') {
424
+ console.log(chalk.yellow(`🛑 Gate ${gate} failed, stopping execution`));
425
+ break;
426
+ }
427
+ }
428
+
429
+ // Flakiness Detection Phase (Vision 2.0)
430
+ let flakinessResults: FlakinessResult[] | undefined;
431
+ if (this.flakyDetect) {
432
+ console.log(chalk.blue(`\n🎲 Phase 2.5: Flakiness Detection (${this.flakyRuns} consecutive runs)`));
433
+ flakinessResults = await this.detectFlakiness(gateResults);
434
+
435
+ // Display flakiness summary
436
+ const unstableCount = flakinessResults.filter(f =>
437
+ f.category === FlakinessCategory.UNSTABLE || f.category === FlakinessCategory.SHAKY
438
+ ).length;
439
+
440
+ if (unstableCount > 0) {
441
+ console.log(chalk.yellow(` ⚠️ ${unstableCount} test(s) show flaky behavior`));
442
+ } else {
443
+ console.log(chalk.green(' ✅ All tests stable - no flakiness detected'));
444
+ }
445
+ }
446
+
447
+ // Execute afterAll hooks
448
+ console.log(chalk.blue('\n🔗 Phase 3: Cleanup Hooks'));
449
+ const afterAllResults = await this.hooksRunner.executeHooks('afterAll');
450
+
451
+ // Calculate results
452
+ const duration = Date.now() - startTime;
453
+ const summary = this.calculateSummary(gateResults);
454
+ const success = summary.failed === 0;
455
+
456
+ // Generate proof
457
+ console.log(chalk.blue('\n📋 Phase 4: Proof Generation'));
458
+ const proofPath = await this.generateProof({
459
+ success,
460
+ pack: this.pack,
461
+ duration,
462
+ gates: gateResults,
463
+ hooks: {
464
+ beforeAll: beforeAllResults,
465
+ beforeEach: [],
466
+ afterEach: [],
467
+ afterAll: afterAllResults
468
+ },
469
+ summary
470
+ });
471
+
472
+ // Save to Evidence Vault
473
+ console.log(chalk.blue('\n💾 Phase 5: Evidence Storage'));
474
+ const proofRunId = proofPath.split('/').pop()?.replace('-proof.json', '') || 'unknown';
475
+ const vaultRunId = await this.saveToVault(proofRunId, {
476
+ success,
477
+ pack: this.pack,
478
+ duration,
479
+ gates: gateResults,
480
+ hooks: {
481
+ beforeAll: beforeAllResults,
482
+ beforeEach: [],
483
+ afterEach: [],
484
+ afterAll: afterAllResults
485
+ },
486
+ summary,
487
+ proofPath
488
+ }, proofPath, flakinessResults, startTime);
489
+
490
+ // Final summary
491
+ console.log(chalk.blue('\n📊 Execution Summary'));
492
+ console.log(` Duration: ${duration}ms`);
493
+ console.log(` Gates: ${summary.passed}/${summary.total} passed`);
494
+ console.log(` Trust Score: ${summary.trustScore}%`);
495
+ console.log(` Proof: ${proofPath}`);
496
+
497
+ // Display flakiness details if enabled
498
+ if (flakinessResults && flakinessResults.length > 0) {
499
+ console.log(chalk.blue('\n🎲 Flakiness Scores:'));
500
+ for (const f of flakinessResults) {
501
+ const scoreFormatted = formatFlakinessScore(f.score);
502
+ const categoryMeta = {
503
+ [FlakinessCategory.LEGENDARY]: { emoji: '🟢', color: chalk.green },
504
+ [FlakinessCategory.SOLID]: { emoji: '🟢', color: chalk.green },
505
+ [FlakinessCategory.GOOD]: { emoji: '🟡', color: chalk.yellow },
506
+ [FlakinessCategory.SHAKY]: { emoji: '🟠', color: chalk.hex('#F97316') },
507
+ [FlakinessCategory.UNSTABLE]: { emoji: '🔴', color: chalk.red }
508
+ }[f.category];
509
+
510
+ console.log(` ${categoryMeta.color(`${f.testName}: ${scoreFormatted}`)} (${f.successfulRuns}/${f.totalRuns} passes)`);
511
+ if (f.suggestedFix) {
512
+ console.log(chalk.gray(` 💡 ${f.suggestedFix}`));
513
+ }
514
+ }
515
+ }
516
+
517
+ if (success) {
518
+ console.log(chalk.green('\n✅ All quality gates passed!'));
519
+ } else {
520
+ console.log(chalk.red(`\n❌ ${summary.failed} quality gate(s) failed`));
521
+ }
522
+
523
+ return {
524
+ success,
525
+ pack: this.pack,
526
+ duration,
527
+ gates: gateResults,
528
+ hooks: {
529
+ beforeAll: beforeAllResults,
530
+ beforeEach: [],
531
+ afterEach: [],
532
+ afterAll: afterAllResults
533
+ },
534
+ summary,
535
+ flakiness: flakinessResults,
536
+ proofPath,
537
+ runId: vaultRunId
538
+ };
539
+
540
+ } catch (error) {
541
+ const duration = Date.now() - startTime;
542
+
543
+ // Ensure cleanup even on error
544
+ try {
545
+ await this.hooksRunner.executeHooks('afterAll');
546
+ } catch {
547
+ // Ignore cleanup errors
548
+ }
549
+
550
+ return {
551
+ success: false,
552
+ pack: this.pack,
553
+ duration,
554
+ gates: [],
555
+ hooks: { beforeAll: [], beforeEach: [], afterEach: [], afterAll: [] },
556
+ summary: { total: 0, passed: 0, failed: 0, trustScore: 0 },
557
+ error: this.redactor.redact(error instanceof Error ? error.message : 'Unknown error')
558
+ };
559
+ }
560
+ }
561
+
562
+ /**
563
+ * Execute a single quality gate
564
+ */
565
+ private async executeGate(gate: string): Promise<GateResult> {
566
+ const startTime = Date.now();
567
+
568
+ console.log(chalk.cyan(`\n 🎯 Gate: ${gate}`));
569
+
570
+ try {
571
+ let result: any;
572
+ let adapter: string;
573
+
574
+ // Check if this is a v2 pack with dynamic gate configuration
575
+ if (this.isPackV2(this.pack)) {
576
+ const gateConfig = (this.pack.gates as Record<string, any>)[gate];
577
+ if (gateConfig) {
578
+ return await this.executeDynamicGate(gate, gateConfig);
579
+ }
580
+ }
581
+
582
+ // Legacy v1 gates or predefined gates
583
+ switch (gate) {
584
+ case 'api_smoke':
585
+ adapter = 'playwright-native-api';
586
+ result = await this.runApiSmokeGate();
587
+ break;
588
+
589
+ case 'ui':
590
+ adapter = 'playwright-ui';
591
+ result = await this.runUiGate();
592
+ break;
593
+
594
+ case 'a11y':
595
+ adapter = 'playwright-ui';
596
+ result = await this.runA11yGate();
597
+ break;
598
+
599
+ case 'perf':
600
+ adapter = 'k6';
601
+ result = await this.runPerfGate();
602
+ break;
603
+
604
+ case 'sast':
605
+ adapter = 'semgrep';
606
+ result = await this.runSastGate();
607
+ break;
608
+
609
+ case 'dast':
610
+ adapter = 'zap';
611
+ result = await this.runDastGate();
612
+ break;
613
+
614
+ default:
615
+ throw new Error(`Unknown gate: ${gate}`);
616
+ }
617
+
618
+ const duration = Date.now() - startTime;
619
+
620
+ if (result.success) {
621
+ console.log(chalk.green(` ✅ ${gate} passed (${duration}ms)`));
622
+ } else {
623
+ console.log(chalk.red(` ❌ ${gate} failed (${duration}ms)`));
624
+ if (result.error) {
625
+ console.log(chalk.red(` 🔍 ${result.error}`));
626
+ }
627
+ }
628
+
629
+ return {
630
+ gate,
631
+ success: result.success,
632
+ duration,
633
+ adapter,
634
+ results: result,
635
+ junit: result.junit,
636
+ error: result.error
637
+ };
638
+
639
+ } catch (error) {
640
+ const duration = Date.now() - startTime;
641
+ const errorMessage = this.redactor.redact(error instanceof Error ? error.message : 'Unknown error');
642
+
643
+ console.log(chalk.red(` 💥 ${gate} crashed (${duration}ms): ${errorMessage}`));
644
+
645
+ return {
646
+ gate,
647
+ success: false,
648
+ duration,
649
+ adapter: 'unknown',
650
+ results: null,
651
+ error: errorMessage
652
+ };
653
+ }
654
+ }
655
+
656
+ /**
657
+ * Execute a dynamic v2 gate
658
+ */
659
+ private async executeDynamicGate(gateName: string, gateConfig: any): Promise<GateResult> {
660
+ const startTime = Date.now();
661
+ const adapterType = gateConfig.adapter || gateConfig.type;
662
+
663
+ if (!adapterType) {
664
+ throw new Error(`Gate '${gateName}' must specify an adapter`);
665
+ }
666
+
667
+ // Resolve fixtures in gate configuration
668
+ const resolvedGateConfig = this.resolveFixturesInConfig(gateConfig);
669
+
670
+ // Get auth credentials for this gate
671
+ const credentials = await this.getCredentialsForGate(gateName);
672
+
673
+ // Map adapter type to implementation
674
+ let result: GateResult;
675
+ switch (adapterType) {
676
+ case 'playwright-api':
677
+ result = await this.executePlaywrightApiGate(gateName, resolvedGateConfig, credentials);
678
+ break;
679
+
680
+ case 'playwright-ui':
681
+ result = await this.executePlaywrightUiGate(gateName, resolvedGateConfig, credentials);
682
+ break;
683
+
684
+ case 'k6':
685
+ case 'k6-perf':
686
+ result = await this.executeK6PerfGate(gateName, resolvedGateConfig);
687
+ break;
688
+
689
+ case 'semgrep':
690
+ case 'sast':
691
+ result = await this.executeSemgrepSastGate(gateName, resolvedGateConfig);
692
+ break;
693
+
694
+ // v2.2.0 Unit Test Adapters
695
+ case 'vitest':
696
+ result = await this.executeVitestGate(gateName, resolvedGateConfig);
697
+ break;
698
+
699
+ case 'jest':
700
+ result = await this.executeJestGate(gateName, resolvedGateConfig);
701
+ break;
702
+
703
+ case 'pytest':
704
+ result = await this.executePytestGate(gateName, resolvedGateConfig);
705
+ break;
706
+
707
+ default:
708
+ throw new Error(`Unsupported adapter: '${adapterType}' for gate '${gateName}'`);
709
+ }
710
+
711
+ // Set duration
712
+ result.duration = Date.now() - startTime;
713
+
714
+ // Log result
715
+ if (result.success) {
716
+ console.log(chalk.green(` ✅ ${gateName} passed (${result.duration}ms)`));
717
+ } else {
718
+ console.log(chalk.red(` ❌ ${gateName} failed (${result.duration}ms)`));
719
+ if (result.error) {
720
+ console.log(chalk.red(` 🔍 ${result.error}`));
721
+ }
722
+ }
723
+
724
+ return result;
725
+ }
726
+
727
+ /**
728
+ * Execute Playwright API gate with v2 config
729
+ * Uses PlaywrightNativeApiAdapter for zero-overhead HTTP testing
730
+ */
731
+ private async executePlaywrightApiGate(gateName: string, gateConfig: any, credentials?: any): Promise<GateResult> {
732
+ const { PlaywrightNativeApiAdapter } = await import('../adapters/playwright-native-api.js');
733
+ const adapter = new PlaywrightNativeApiAdapter();
734
+
735
+ // Transform v2 config to adapter format
736
+ const gateConfigData = gateConfig.config || {};
737
+ const gateOptions = gateConfig.options || {};
738
+
739
+ const config = {
740
+ target: {
741
+ baseUrl: gateConfigData.baseUrl,
742
+ smoke: gateConfigData.smoke
743
+ },
744
+ budgets: gateConfigData.budgets,
745
+ timeout: gateOptions.timeout || gateConfigData.timeout,
746
+ retries: gateOptions.retries || gateConfigData.retries,
747
+ auth: credentials
748
+ };
749
+
750
+ const result = await adapter.runSmokeTests(config);
751
+ return {
752
+ gate: gateName,
753
+ success: result.success,
754
+ duration: 0, // Will be set by caller
755
+ adapter: 'playwright-native-api',
756
+ results: result,
757
+ junit: result.junit
758
+ };
759
+ }
760
+
761
+ /**
762
+ * Execute Playwright UI gate with v2 config
763
+ */
764
+ private async executePlaywrightUiGate(gateName: string, gateConfig: any, credentials?: any): Promise<GateResult> {
765
+ const { PlaywrightUiAdapter } = await import('../adapters/playwright-ui.js');
766
+ const adapter = new PlaywrightUiAdapter();
767
+
768
+ // Transform v2 config to adapter format
769
+ // v2: { baseUrl, pages: [{ url: '/', expectedElements: [...] }] }
770
+ // adapter expects: { target: { baseUrl, pages: ['https://.../'] } }
771
+ const gateConfigData = gateConfig.config || {};
772
+ const gateOptions = gateConfig.options || {};
773
+
774
+ // Transform page objects to full URLs
775
+ // v2 pages format: [{ url: '/', expectedElements: [...] }] or ['/', '/about']
776
+ const rawPages = gateConfigData.pages;
777
+ let pages: string[] | undefined;
778
+ if (rawPages && Array.isArray(rawPages) && rawPages.length > 0) {
779
+ const baseUrl = gateConfigData.baseUrl || '';
780
+ pages = rawPages.map((p: any) => {
781
+ if (typeof p === 'string') {
782
+ // Already a URL string - make it absolute if relative
783
+ return p.startsWith('http') ? p : `${baseUrl.replace(/\/$/, '')}${p}`;
784
+ } else if (p && typeof p === 'object' && p.url) {
785
+ // Page object { url: '/', ... } - convert to full URL
786
+ const url = p.url;
787
+ return url.startsWith('http') ? url : `${baseUrl.replace(/\/$/, '')}${url}`;
788
+ }
789
+ return p;
790
+ });
791
+ } else if (gateConfigData.baseUrl) {
792
+ pages = [gateConfigData.baseUrl];
793
+ }
794
+
795
+ const config = {
796
+ target: {
797
+ baseUrl: gateConfigData.baseUrl,
798
+ pages: pages
799
+ },
800
+ budgets: gateConfigData.budgets,
801
+ timeout: gateOptions.timeout || gateConfigData.timeout,
802
+ auth: credentials,
803
+ // Playwright++ features
804
+ artifacts: gateConfigData.artifacts,
805
+ htmlReport: gateConfigData.htmlReport,
806
+ bail: gateConfigData.bail
807
+ };
808
+
809
+ const result = await adapter.runSmokeTests(config);
810
+ return {
811
+ gate: gateName,
812
+ success: result.success,
813
+ duration: 0, // Will be set by caller
814
+ adapter: 'playwright-ui',
815
+ results: result,
816
+ junit: result.junit
817
+ };
818
+ }
819
+
820
+ /**
821
+ * Execute K6 Performance gate with v2 config
822
+ */
823
+ private async executeK6PerfGate(gateName: string, gateConfig: any): Promise<GateResult> {
824
+ const { K6PerfAdapter } = await import('../adapters/k6-perf.js');
825
+ const adapter = new K6PerfAdapter(this.workingDir);
826
+
827
+ const config = {
828
+ baseUrl: gateConfig.config?.baseUrl,
829
+ ...gateConfig.config
830
+ };
831
+
832
+ const result = await adapter.runPerfTest(config);
833
+ return {
834
+ gate: gateName,
835
+ success: result.success,
836
+ duration: 0,
837
+ adapter: 'k6',
838
+ results: result,
839
+ junit: result.junit
840
+ };
841
+ }
842
+
843
+ /**
844
+ * Execute Semgrep SAST gate with v2 config
845
+ */
846
+ private async executeSemgrepSastGate(gateName: string, gateConfig: any): Promise<GateResult> {
847
+ const { SemgrepSastAdapter } = await import('../adapters/semgrep-sast.js');
848
+ const adapter = new SemgrepSastAdapter();
849
+
850
+ const config = {
851
+ workingDir: this.workingDir,
852
+ ...gateConfig.config
853
+ };
854
+
855
+ const result = await adapter.runSastScan(config);
856
+ return {
857
+ gate: gateName,
858
+ success: result.success,
859
+ duration: 0,
860
+ adapter: 'semgrep',
861
+ results: result,
862
+ junit: result.junit
863
+ };
864
+ }
865
+
866
+ /**
867
+ * Execute Vitest unit tests gate (v2.2.0)
868
+ */
869
+ private async executeVitestGate(gateName: string, gateConfig: any): Promise<GateResult> {
870
+ const adapter = new VitestAdapter(this.workingDir);
871
+
872
+ const config: any = {
873
+ cwd: this.workingDir,
874
+ ...gateConfig.config
875
+ };
876
+
877
+ const result = await adapter.execute(config);
878
+
879
+ return {
880
+ gate: gateName,
881
+ success: result.success,
882
+ duration: result.duration,
883
+ adapter: 'vitest',
884
+ results: {
885
+ total: result.total,
886
+ passed: result.passed,
887
+ failed: result.failed,
888
+ skipped: result.skipped,
889
+ tests: result.tests.map(t => ({
890
+ name: t.name,
891
+ status: t.status,
892
+ duration: t.duration,
893
+ error: t.error
894
+ }))
895
+ },
896
+ junit: this.buildJunitFromVitest(result)
897
+ };
898
+ }
899
+
900
+ /**
901
+ * Execute Jest unit tests gate (v2.2.0)
902
+ */
903
+ private async executeJestGate(gateName: string, gateConfig: any): Promise<GateResult> {
904
+ const adapter = new JestAdapter(this.workingDir);
905
+
906
+ const config: any = {
907
+ cwd: this.workingDir,
908
+ ...gateConfig.config
909
+ };
910
+
911
+ const result = await adapter.execute(config);
912
+
913
+ return {
914
+ gate: gateName,
915
+ success: result.success,
916
+ duration: result.duration,
917
+ adapter: 'jest',
918
+ results: {
919
+ total: result.total,
920
+ passed: result.passed,
921
+ failed: result.failed,
922
+ skipped: result.skipped + result.pending,
923
+ tests: result.tests.map(t => ({
924
+ name: t.name,
925
+ status: t.status,
926
+ duration: t.duration,
927
+ error: t.error
928
+ }))
929
+ },
930
+ junit: this.buildJunitFromJest(result)
931
+ };
932
+ }
933
+
934
+ /**
935
+ * Execute Pytest unit tests gate (v2.2.0)
936
+ */
937
+ private async executePytestGate(gateName: string, gateConfig: any): Promise<GateResult> {
938
+ const adapter = new PytestAdapter(this.workingDir);
939
+
940
+ const config: any = {
941
+ cwd: this.workingDir,
942
+ ...gateConfig.config
943
+ };
944
+
945
+ const result = await adapter.execute(config);
946
+
947
+ return {
948
+ gate: gateName,
949
+ success: result.success,
950
+ duration: result.duration,
951
+ adapter: 'pytest',
952
+ results: {
953
+ total: result.total,
954
+ passed: result.passed,
955
+ failed: result.failed,
956
+ skipped: result.skipped,
957
+ tests: result.tests.map(t => ({
958
+ name: t.name,
959
+ status: t.status,
960
+ duration: t.duration,
961
+ error: t.error
962
+ }))
963
+ },
964
+ junit: this.buildJunitFromPytest(result)
965
+ };
966
+ }
967
+
968
+ /**
969
+ * Build JUnit format from Vitest results
970
+ */
971
+ private buildJunitFromVitest(result: any): any {
972
+ // Convert adapter result to JUnit-like format
973
+ return {
974
+ testsuites: [{
975
+ name: 'vitest',
976
+ tests: result.total,
977
+ failures: result.failed,
978
+ skipped: result.skipped,
979
+ time: result.duration / 1000,
980
+ testcases: result.tests.map((t: any) => ({
981
+ name: t.name,
982
+ classname: t.file,
983
+ time: t.duration / 1000,
984
+ failure: t.error ? { message: t.error } : undefined,
985
+ skipped: t.status === 'skipped' ? { message: 'Skipped' } : undefined
986
+ }))
987
+ }]
988
+ };
989
+ }
990
+
991
+ /**
992
+ * Build JUnit format from Jest results
993
+ */
994
+ private buildJunitFromJest(result: any): any {
995
+ return {
996
+ testsuites: [{
997
+ name: 'jest',
998
+ tests: result.total,
999
+ failures: result.failed,
1000
+ skipped: result.pending,
1001
+ time: result.duration / 1000,
1002
+ testcases: result.tests.map((t: any) => ({
1003
+ name: t.name,
1004
+ time: t.duration / 1000,
1005
+ failure: t.error ? { message: t.error } : undefined,
1006
+ skipped: t.status === 'pending' || t.status === 'skipped' ? { message: 'Skipped' } : undefined
1007
+ }))
1008
+ }]
1009
+ };
1010
+ }
1011
+
1012
+ /**
1013
+ * Build JUnit format from Pytest results
1014
+ */
1015
+ private buildJunitFromPytest(result: any): any {
1016
+ return {
1017
+ testsuites: [{
1018
+ name: 'pytest',
1019
+ tests: result.total,
1020
+ failures: result.failed,
1021
+ skipped: result.skipped,
1022
+ time: result.duration / 1000,
1023
+ testcases: result.tests.map((t: any) => ({
1024
+ name: t.name,
1025
+ classname: t.file,
1026
+ time: t.duration / 1000,
1027
+ failure: t.error ? { message: t.error } : undefined,
1028
+ skipped: t.status === 'skipped' ? { message: 'Skipped' } : undefined
1029
+ }))
1030
+ }]
1031
+ };
1032
+ }
1033
+
1034
+ /**
1035
+ * Run API smoke gate
1036
+ * Uses PlaywrightNativeApiAdapter for zero-overhead HTTP testing
1037
+ */
1038
+ private async runApiSmokeGate() {
1039
+ const target = this.getTargetApi();
1040
+ if (!target) {
1041
+ throw new Error('API smoke gate requires targets.api configuration or gate config with baseUrl');
1042
+ }
1043
+
1044
+ const credentials = await this.getCredentialsForGate('api_smoke');
1045
+
1046
+ const adapter = new PlaywrightNativeApiAdapter();
1047
+ return await adapter.runSmokeTests({
1048
+ target,
1049
+ budgets: this.getBudgets(),
1050
+ timeout: this.getExecutionTimeout() || 10000,
1051
+ retries: this.getExecutionRetries() || 1,
1052
+ auth: credentials
1053
+ });
1054
+ }
1055
+
1056
+ /**
1057
+ * Run UI gate (includes basic accessibility)
1058
+ */
1059
+ private async runUiGate() {
1060
+ const target = this.getTargetWeb();
1061
+ if (!target) {
1062
+ throw new Error('UI gate requires targets.web configuration or gate config with baseUrl');
1063
+ }
1064
+
1065
+ const credentials = await this.getCredentialsForGate('ui');
1066
+
1067
+ const adapter = new PlaywrightUiAdapter();
1068
+ return await adapter.runSmokeTests({
1069
+ target,
1070
+ budgets: this.getBudgets(),
1071
+ timeout: this.getExecutionTimeout() || 30000,
1072
+ auth: credentials
1073
+ });
1074
+ }
1075
+
1076
+ /**
1077
+ * Run A11y gate (focused accessibility testing)
1078
+ */
1079
+ private async runA11yGate() {
1080
+ // Same as UI gate but focused on accessibility
1081
+ return await this.runUiGate();
1082
+ }
1083
+
1084
+ /**
1085
+ * Run performance gate
1086
+ */
1087
+ private async runPerfGate() {
1088
+ const baseUrl = (this.pack as PackConfigV1).targets?.web?.baseUrl || (this.pack as PackConfigV1).targets?.api?.baseUrl;
1089
+ if (!baseUrl) {
1090
+ throw new Error('Performance gate requires web or api target');
1091
+ }
1092
+
1093
+ const adapter = new K6PerfAdapter(this.workingDir);
1094
+ return await adapter.runPerfTest({
1095
+ baseUrl,
1096
+ budgets: (this.pack as PackConfigV1).budgets,
1097
+ duration: '30s',
1098
+ vus: 5,
1099
+ timeout: 120000
1100
+ });
1101
+ }
1102
+
1103
+ /**
1104
+ * Run SAST gate
1105
+ */
1106
+ private async runSastGate() {
1107
+ const adapter = new SemgrepSastAdapter();
1108
+ return await adapter.runSastScan({
1109
+ workingDir: this.workingDir,
1110
+ security: (this.pack as PackConfigV1).security,
1111
+ rules: ['auto'],
1112
+ paths: ['src/', 'lib/', '.'],
1113
+ timeout: 120000
1114
+ });
1115
+ }
1116
+
1117
+ /**
1118
+ * Run DAST gate (mock implementation)
1119
+ */
1120
+ private async runDastGate() {
1121
+ // Mock DAST implementation for Phase 3
1122
+ console.log(chalk.yellow(' ⚠️ DAST gate: Mock implementation (ZAP integration in Phase 4)'));
1123
+
1124
+ return {
1125
+ success: true,
1126
+ findings: [],
1127
+ summary: { total: 0, high: 0, medium: 0, low: 0 },
1128
+ junit: `<?xml version="1.0" encoding="UTF-8"?>
1129
+ <testsuite name="DAST Mock" tests="1" failures="0" time="0">
1130
+ <testcase name="Mock DAST Scan" time="0"></testcase>
1131
+ </testsuite>`
1132
+ };
1133
+ }
1134
+
1135
+ /**
1136
+ * Calculate execution summary
1137
+ */
1138
+ private calculateSummary(gateResults: GateResult[]): Phase3RunResult['summary'] {
1139
+ const total = gateResults.length;
1140
+ const passed = gateResults.filter(g => g.success).length;
1141
+ const failed = total - passed;
1142
+
1143
+ // Calculate trust score (weighted by gate importance)
1144
+ const gateWeights: Record<string, number> = {
1145
+ 'api_smoke': 20,
1146
+ 'ui': 15,
1147
+ 'perf': 15,
1148
+ 'sast': 20,
1149
+ 'dast': 20,
1150
+ 'a11y': 10
1151
+ };
1152
+
1153
+ let totalWeight = 0;
1154
+ let passedWeight = 0;
1155
+
1156
+ for (const gate of gateResults) {
1157
+ const weight = gateWeights[gate.gate] || 10;
1158
+ totalWeight += weight;
1159
+ if (gate.success) {
1160
+ passedWeight += weight;
1161
+ }
1162
+ }
1163
+
1164
+ const trustScore = totalWeight > 0 ? Math.round((passedWeight / totalWeight) * 100) : 0;
1165
+
1166
+ return { total, passed, failed, trustScore };
1167
+ }
1168
+
1169
+ /**
1170
+ * Generate cryptographically signed proof document
1171
+ */
1172
+ private async generateProof(result: Phase3RunResult): Promise<string> {
1173
+ const timestamp = new Date().toISOString();
1174
+ const runId = `run-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
1175
+
1176
+ // Build proof payload (without signature)
1177
+ const proofPayload = {
1178
+ version: '3.0.0',
1179
+ runId,
1180
+ timestamp,
1181
+ pack: {
1182
+ name: result.pack.name,
1183
+ version: result.pack.version,
1184
+ gates: result.pack.gates
1185
+ },
1186
+ execution: {
1187
+ duration: result.duration,
1188
+ success: result.success,
1189
+ trustScore: result.summary.trustScore
1190
+ },
1191
+ gates: result.gates.map(g => {
1192
+ const gateObj: any = {
1193
+ gate: g.gate,
1194
+ adapter: g.adapter,
1195
+ success: g.success,
1196
+ duration: g.duration
1197
+ };
1198
+ // Only include error if it exists (avoid undefined fields)
1199
+ if (g.error !== undefined) {
1200
+ gateObj.error = g.error;
1201
+ }
1202
+ return gateObj;
1203
+ }),
1204
+ hooks: {
1205
+ beforeAll: result.hooks.beforeAll.length,
1206
+ afterAll: result.hooks.afterAll.length
1207
+ }
1208
+ };
1209
+
1210
+ // Canonicalize the payload for signing (must match verification: canonical + newline)
1211
+ const canonicalPayload = canonicalize(proofPayload) + '\n';
1212
+
1213
+ // Sign with Ed25519
1214
+ let signatureValue: string;
1215
+ let algorithm: string;
1216
+ let publicKeyB64: string;
1217
+
1218
+ if (this.keyPair) {
1219
+ signatureValue = sign(canonicalPayload, this.keyPair.secretKey);
1220
+ algorithm = 'ed25519';
1221
+ publicKeyB64 = Buffer.from(this.keyPair.publicKey).toString('base64');
1222
+ console.log(chalk.green(' 🔏 Proof signed with Ed25519'));
1223
+ } else {
1224
+ signatureValue = `unsigned-${runId}`;
1225
+ algorithm = 'none';
1226
+ publicKeyB64 = '';
1227
+ console.log(chalk.yellow(' ⚠️ Proof unsigned (no keys available)'));
1228
+ }
1229
+
1230
+ // Build complete proof with signature
1231
+ const proof = {
1232
+ ...proofPayload,
1233
+ signature: {
1234
+ algorithm,
1235
+ publicKey: publicKeyB64,
1236
+ value: signatureValue,
1237
+ timestamp
1238
+ }
1239
+ };
1240
+
1241
+ // Save proof
1242
+ const proofPath = join(this.outputDir, `${runId}-proof.json`);
1243
+ writeFileSync(proofPath, JSON.stringify(proof, null, 2));
1244
+
1245
+ return proofPath;
1246
+ }
1247
+
1248
+ /**
1249
+ * Save run results to Evidence Vault
1250
+ * @returns The vault run ID
1251
+ */
1252
+ private async saveToVault(
1253
+ runId: string,
1254
+ result: Phase3RunResult,
1255
+ proofPath: string,
1256
+ flakinessResults?: FlakinessResult[],
1257
+ startTime?: number
1258
+ ): Promise<string | undefined> {
1259
+ if (!this.vault) {
1260
+ console.log(chalk.yellow(' ⚠️ Vault not initialized, skipping storage'));
1261
+ return undefined;
1262
+ }
1263
+
1264
+ try {
1265
+ // Begin run in vault (returns actual runId used)
1266
+ // Use startTime if provided to correctly calculate duration
1267
+ const { runId: vaultRunId } = await this.vault.beginRun({
1268
+ pack_path: `${(this.pack as any).name || 'pack'}.yaml`,
1269
+ pack_hash: this.hashPack(result.pack),
1270
+ started_at: startTime // Use actual run start time for accurate duration
1271
+ });
1272
+
1273
+ // Finish run with final status
1274
+ await this.vault.finishRun(vaultRunId, {
1275
+ status: result.success ? 'passed' : 'failed',
1276
+ trust_score: result.summary.trustScore,
1277
+ signature: this.keyPair ? 'ed25519-signed' : undefined
1278
+ });
1279
+
1280
+ // Record gate executions
1281
+ for (const gate of result.gates) {
1282
+ await this.vault.recordGate(vaultRunId, {
1283
+ name: gate.gate,
1284
+ status: gate.success ? 'passed' : 'failed',
1285
+ duration_ms: gate.duration,
1286
+ metrics_json: JSON.stringify(gate.results?.summary || {})
1287
+ });
1288
+
1289
+ // Record finding if gate has error
1290
+ if (gate.error) {
1291
+ await this.vault.recordFinding(vaultRunId, {
1292
+ gate: gate.gate,
1293
+ severity: 'high',
1294
+ rule: 'gate-failure',
1295
+ message: gate.error
1296
+ });
1297
+ }
1298
+ }
1299
+
1300
+ // Record flakiness results if available
1301
+ if (flakinessResults && flakinessResults.length > 0) {
1302
+ for (const flaky of flakinessResults) {
1303
+ // Record unstable/shaky tests as findings
1304
+ if (flaky.category === FlakinessCategory.UNSTABLE || flaky.category === FlakinessCategory.SHAKY) {
1305
+ await this.vault.recordFinding(vaultRunId, {
1306
+ gate: flaky.gate,
1307
+ severity: flaky.category === FlakinessCategory.UNSTABLE ? 'high' : 'medium',
1308
+ rule: 'flaky-test-detected',
1309
+ message: `Test "${flaky.testName}" is ${flaky.category}: ${flaky.score}% reliability (${flaky.successfulRuns}/${flaky.totalRuns} passes)`
1310
+ });
1311
+ }
1312
+ }
1313
+ console.log(chalk.green(` 💾 ${flakinessResults.length} flakiness analysis(es) saved`));
1314
+ }
1315
+
1316
+ console.log(chalk.green(' 💾 Run saved to Evidence Vault'));
1317
+ return vaultRunId;
1318
+
1319
+ } catch (error) {
1320
+ console.log(chalk.yellow(` ⚠️ Failed to save to vault: ${error}`));
1321
+ return undefined;
1322
+ }
1323
+ }
1324
+
1325
+ /**
1326
+ * Detect flakiness by running tests multiple times consecutively
1327
+ * @param gateResults Original gate results from first run
1328
+ * @returns Flakiness analysis results
1329
+ */
1330
+ private async detectFlakiness(gateResults: GateResult[]): Promise<FlakinessResult[]> {
1331
+ const flakinessMap = new Map<string, FlakyTestResult[]>();
1332
+ const timestamp = Date.now();
1333
+
1334
+ // First, collect results from the initial run
1335
+ for (const gateResult of gateResults) {
1336
+ for (const [testId, testResult] of this.extractTestResults(gateResult, timestamp)) {
1337
+ if (!flakinessMap.has(testId)) {
1338
+ flakinessMap.set(testId, []);
1339
+ }
1340
+ flakinessMap.get(testId)!.push(testResult);
1341
+ }
1342
+ }
1343
+
1344
+ // Run additional consecutive runs
1345
+ for (let run = 1; run < this.flakyRuns; run++) {
1346
+ console.log(chalk.gray(` 🔄 Consecutive run ${run + 1}/${this.flakyRuns}...`));
1347
+
1348
+ for (const gateResult of gateResults) {
1349
+ // Re-run all gates to detect flakiness
1350
+ try {
1351
+ const retryResult = await this.executeGate(gateResult.gate);
1352
+
1353
+ // Extract and store test results
1354
+ for (const [testId, testResult] of this.extractTestResults(retryResult, timestamp)) {
1355
+ if (!flakinessMap.has(testId)) {
1356
+ flakinessMap.set(testId, []);
1357
+ }
1358
+ flakinessMap.get(testId)!.push(testResult);
1359
+ }
1360
+ } catch (error) {
1361
+ console.log(chalk.yellow(` ⚠️ Failed to re-run ${gateResult.gate}: ${error}`));
1362
+ }
1363
+ }
1364
+ }
1365
+
1366
+ // Analyze all collected results
1367
+ return this.flakinessDetector.analyzeAll(flakinessMap);
1368
+ }
1369
+
1370
+ /**
1371
+ * Extract test results from a gate result
1372
+ * Returns a map of testId to TestResult for flakiness tracking
1373
+ */
1374
+ private extractTestResults(gateResult: GateResult, timestamp: number): Map<string, FlakyTestResult> {
1375
+ const results = new Map<string, FlakyTestResult>();
1376
+
1377
+ // Extract test results from adapter output
1378
+ const adapterResults = gateResult.results;
1379
+
1380
+ if (adapterResults?.results && Array.isArray(adapterResults.results)) {
1381
+ // Check for Playwright Native API adapter format
1382
+ const firstResult = adapterResults.results[0];
1383
+
1384
+ if (firstResult && 'endpoint' in firstResult && 'method' in firstResult) {
1385
+ // PlaywrightNativeApiAdapter format: { endpoint, method, status, success, error, ... }
1386
+ for (const test of adapterResults.results as Array<{endpoint: string; method: string; status: number; success: boolean; error?: string; responseTime?: number}>) {
1387
+ // Extract just the path from full URL for cleaner test names
1388
+ const url = new URL(test.endpoint);
1389
+ const path = url.pathname + url.search;
1390
+ const testName = `${test.method} ${path} -> ${test.status}`;
1391
+ const testId = generateTestId(testName, gateResult.gate);
1392
+ results.set(testId, {
1393
+ testId,
1394
+ testName,
1395
+ filePath: gateResult.gate,
1396
+ gate: gateResult.gate,
1397
+ success: test.success,
1398
+ durationMs: test.responseTime || 0,
1399
+ errorMessage: test.error,
1400
+ timestamp: timestamp + Math.random(), // Small offset for ordering
1401
+ environment: process.env.NODE_ENV || 'local'
1402
+ });
1403
+ }
1404
+ } else {
1405
+ // Generic adapter format with results array
1406
+ for (const test of adapterResults.results as Array<{name?: string; passed?: boolean; status?: string; duration?: number; error?: {message?: string; type?: string}}>) {
1407
+ const testName = test.name || gateResult.gate;
1408
+ const testId = generateTestId(testName, gateResult.gate);
1409
+ results.set(testId, {
1410
+ testId,
1411
+ testName,
1412
+ filePath: gateResult.gate,
1413
+ gate: gateResult.gate,
1414
+ success: test.passed || test.status === 'passed',
1415
+ durationMs: test.duration || 0,
1416
+ errorType: test.error?.type,
1417
+ errorMessage: test.error?.message,
1418
+ timestamp: timestamp + Math.random(),
1419
+ environment: process.env.NODE_ENV || 'local'
1420
+ });
1421
+ }
1422
+ }
1423
+ } else if (adapterResults?.summary) {
1424
+ // Generic adapter with summary only (no detailed results)
1425
+ const testId = generateTestId(gateResult.gate, gateResult.gate);
1426
+ results.set(testId, {
1427
+ testId,
1428
+ testName: gateResult.gate,
1429
+ filePath: gateResult.gate,
1430
+ gate: gateResult.gate,
1431
+ success: gateResult.success,
1432
+ durationMs: gateResult.duration,
1433
+ errorMessage: gateResult.error,
1434
+ timestamp,
1435
+ environment: process.env.NODE_ENV || 'local'
1436
+ });
1437
+ } else {
1438
+ // Fallback: create single test result from gate
1439
+ const testId = generateTestId(gateResult.gate, gateResult.gate);
1440
+ results.set(testId, {
1441
+ testId,
1442
+ testName: gateResult.gate,
1443
+ filePath: gateResult.gate,
1444
+ gate: gateResult.gate,
1445
+ success: gateResult.success,
1446
+ durationMs: gateResult.duration,
1447
+ errorMessage: gateResult.error,
1448
+ timestamp,
1449
+ environment: process.env.NODE_ENV || 'local'
1450
+ });
1451
+ }
1452
+
1453
+ return results;
1454
+ }
1455
+
1456
+ /**
1457
+ * Generate hash of pack configuration
1458
+ */
1459
+ private hashPack(pack: PackConfig): string {
1460
+ return createHash('sha256')
1461
+ .update(JSON.stringify(pack))
1462
+ .digest('hex')
1463
+ .substring(0, 16);
1464
+ }
1465
+
1466
+ /**
1467
+ * Get API target (v1 or v2 format)
1468
+ */
1469
+ private getTargetApi(): any {
1470
+ if (this.isPackV2(this.pack)) {
1471
+ // In v2, target is in the gate config
1472
+ const gateConfig = this.pack.gates['api_smoke'] || this.pack.gates['api'];
1473
+ if (gateConfig && (gateConfig as any).config?.baseUrl) {
1474
+ return (gateConfig as any).config;
1475
+ }
1476
+ return undefined;
1477
+ }
1478
+ return this.pack.targets?.api;
1479
+ }
1480
+
1481
+ /**
1482
+ * Get Web target (v1 or v2 format)
1483
+ */
1484
+ private getTargetWeb(): any {
1485
+ if (this.isPackV2(this.pack)) {
1486
+ // In v2, target is in the gate config
1487
+ const gateConfig = this.pack.gates['ui'] || this.pack.gates['a11y'];
1488
+ if (gateConfig && (gateConfig as any).config?.baseUrl) {
1489
+ return (gateConfig as any).config;
1490
+ }
1491
+ return undefined;
1492
+ }
1493
+ return this.pack.targets?.web;
1494
+ }
1495
+
1496
+ /**
1497
+ * Get budgets (v1 or v2 format)
1498
+ */
1499
+ private getBudgets(): any {
1500
+ if (this.isPackV2(this.pack)) {
1501
+ // In v2, budgets can be in gate config or global
1502
+ // For now, return undefined - budgets are gate-specific in v2
1503
+ return undefined;
1504
+ }
1505
+ return this.pack.budgets;
1506
+ }
1507
+
1508
+ /**
1509
+ * Get execution timeout (v1 or v2 format)
1510
+ */
1511
+ private getExecutionTimeout(): number | undefined {
1512
+ if (this.isPackV2(this.pack)) {
1513
+ return this.pack.execution?.default_timeout;
1514
+ }
1515
+ return this.pack.execution?.timeout;
1516
+ }
1517
+
1518
+ /**
1519
+ * Get execution retries (v1 or v2 format)
1520
+ */
1521
+ private getExecutionRetries(): number | undefined {
1522
+ if (this.isPackV2(this.pack)) {
1523
+ return this.pack.execution?.default_retries;
1524
+ }
1525
+ return this.pack.execution?.max_retries;
1526
+ }
1527
+
1528
+ /**
1529
+ * Ensure output directory exists
1530
+ */
1531
+ private ensureOutputDir(): void {
1532
+ if (!existsSync(this.outputDir)) {
1533
+ mkdirSync(this.outputDir, { recursive: true });
1534
+ }
1535
+ }
1536
+ }