playwright-intelligence-engine 0.0.0-stage → 2.0.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 (353) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +229 -3
  3. package/bin/pw-intel.js +37 -0
  4. package/dist/authoring/author.d.ts +83 -0
  5. package/dist/authoring/author.d.ts.map +1 -0
  6. package/dist/authoring/generation-contract.d.ts +90 -0
  7. package/dist/authoring/generation-contract.d.ts.map +1 -0
  8. package/dist/authoring/hybrid-author.d.ts +6 -0
  9. package/dist/authoring/hybrid-author.d.ts.map +1 -0
  10. package/dist/authoring/index.d.ts +7 -0
  11. package/dist/authoring/index.d.ts.map +1 -0
  12. package/dist/authoring/llm-author.d.ts +28 -0
  13. package/dist/authoring/llm-author.d.ts.map +1 -0
  14. package/dist/authoring/naming/index.d.ts +4 -0
  15. package/dist/authoring/naming/index.d.ts.map +1 -0
  16. package/dist/authoring/naming/name-plan.d.ts +45 -0
  17. package/dist/authoring/naming/name-plan.d.ts.map +1 -0
  18. package/dist/authoring/naming/name-planner.d.ts +25 -0
  19. package/dist/authoring/naming/name-planner.d.ts.map +1 -0
  20. package/dist/authoring/naming/naming-linter.d.ts +20 -0
  21. package/dist/authoring/naming/naming-linter.d.ts.map +1 -0
  22. package/dist/authoring/template-author.d.ts +6 -0
  23. package/dist/authoring/template-author.d.ts.map +1 -0
  24. package/dist/cli/index.d.ts +2 -0
  25. package/dist/cli/index.d.ts.map +1 -0
  26. package/dist/collectors/collector-interface.d.ts +7 -0
  27. package/dist/collectors/collector-interface.d.ts.map +1 -0
  28. package/dist/collectors/environment-collector.d.ts +12 -0
  29. package/dist/collectors/environment-collector.d.ts.map +1 -0
  30. package/dist/collectors/error-collector.d.ts +18 -0
  31. package/dist/collectors/error-collector.d.ts.map +1 -0
  32. package/dist/collectors/git-diff-collector.d.ts +20 -0
  33. package/dist/collectors/git-diff-collector.d.ts.map +1 -0
  34. package/dist/collectors/live-browser-collector.d.ts +45 -0
  35. package/dist/collectors/live-browser-collector.d.ts.map +1 -0
  36. package/dist/collectors/network-collector.d.ts +28 -0
  37. package/dist/collectors/network-collector.d.ts.map +1 -0
  38. package/dist/collectors/static-ast-collector.d.ts +32 -0
  39. package/dist/collectors/static-ast-collector.d.ts.map +1 -0
  40. package/dist/collectors/trace-collector.d.ts +12 -0
  41. package/dist/collectors/trace-collector.d.ts.map +1 -0
  42. package/dist/config/project-config.d.ts +33 -0
  43. package/dist/config/project-config.d.ts.map +1 -0
  44. package/dist/core/errors/engine-errors.d.ts +34 -0
  45. package/dist/core/errors/engine-errors.d.ts.map +1 -0
  46. package/dist/core/types/diagnostic-context.d.ts +151 -0
  47. package/dist/core/types/diagnostic-context.d.ts.map +1 -0
  48. package/dist/core/types/repair-evidence.d.ts +56 -0
  49. package/dist/core/types/repair-evidence.d.ts.map +1 -0
  50. package/dist/core/version.d.ts +13 -0
  51. package/dist/core/version.d.ts.map +1 -0
  52. package/dist/diagnostics/diagnostic-contract.d.ts +52 -0
  53. package/dist/diagnostics/diagnostic-contract.d.ts.map +1 -0
  54. package/dist/diagnostics/diagnostic-report.d.ts +11 -0
  55. package/dist/diagnostics/diagnostic-report.d.ts.map +1 -0
  56. package/dist/diagnostics/evidence-correlator.d.ts +22 -0
  57. package/dist/diagnostics/evidence-correlator.d.ts.map +1 -0
  58. package/dist/diagnostics/external-failure-evidence.d.ts +18 -0
  59. package/dist/diagnostics/external-failure-evidence.d.ts.map +1 -0
  60. package/dist/diagnostics/failure-category.d.ts +4 -0
  61. package/dist/diagnostics/failure-category.d.ts.map +1 -0
  62. package/dist/diagnostics/failure-classifier.d.ts +58 -0
  63. package/dist/diagnostics/failure-classifier.d.ts.map +1 -0
  64. package/dist/evaluation/evaluation-scorecard.d.ts +72 -0
  65. package/dist/evaluation/evaluation-scorecard.d.ts.map +1 -0
  66. package/dist/evaluation/repository-inspector.d.ts +21 -0
  67. package/dist/evaluation/repository-inspector.d.ts.map +1 -0
  68. package/dist/evaluation/repository-runner.d.ts +24 -0
  69. package/dist/evaluation/repository-runner.d.ts.map +1 -0
  70. package/dist/evidence/evidence-model.d.ts +88 -0
  71. package/dist/evidence/evidence-model.d.ts.map +1 -0
  72. package/dist/evidence/evidence-normalizer.d.ts +19 -0
  73. package/dist/evidence/evidence-normalizer.d.ts.map +1 -0
  74. package/dist/evidence/evidence-redactor.d.ts +37 -0
  75. package/dist/evidence/evidence-redactor.d.ts.map +1 -0
  76. package/dist/framework/config-adapter.d.ts +28 -0
  77. package/dist/framework/config-adapter.d.ts.map +1 -0
  78. package/dist/gate/index.d.ts +21 -0
  79. package/dist/gate/index.d.ts.map +1 -0
  80. package/dist/gate/oracle.d.ts +26 -0
  81. package/dist/gate/oracle.d.ts.map +1 -0
  82. package/dist/gate/quality.d.ts +27 -0
  83. package/dist/gate/quality.d.ts.map +1 -0
  84. package/dist/gate/safety.d.ts +28 -0
  85. package/dist/gate/safety.d.ts.map +1 -0
  86. package/dist/generator/architecture-generator.d.ts +55 -0
  87. package/dist/generator/architecture-generator.d.ts.map +1 -0
  88. package/dist/generator/assertion-quality-gate.d.ts +18 -0
  89. package/dist/generator/assertion-quality-gate.d.ts.map +1 -0
  90. package/dist/generator/candidate-writer.d.ts +34 -0
  91. package/dist/generator/candidate-writer.d.ts.map +1 -0
  92. package/dist/generator/test-generator.d.ts +26 -0
  93. package/dist/generator/test-generator.d.ts.map +1 -0
  94. package/dist/governance/audit-log.d.ts +23 -0
  95. package/dist/governance/audit-log.d.ts.map +1 -0
  96. package/dist/governance/governance-policy.d.ts +24 -0
  97. package/dist/governance/governance-policy.d.ts.map +1 -0
  98. package/dist/governance/team-dashboard.d.ts +51 -0
  99. package/dist/governance/team-dashboard.d.ts.map +1 -0
  100. package/dist/governance/workflow-audit.d.ts +13 -0
  101. package/dist/governance/workflow-audit.d.ts.map +1 -0
  102. package/dist/index.d.ts +90 -0
  103. package/dist/index.d.ts.map +1 -0
  104. package/dist/index.js +20318 -0
  105. package/dist/integrations/ci-summary.d.ts +14 -0
  106. package/dist/integrations/ci-summary.d.ts.map +1 -0
  107. package/dist/integrations/playwright-report-adapter.d.ts +22 -0
  108. package/dist/integrations/playwright-report-adapter.d.ts.map +1 -0
  109. package/dist/integrations/quality-gate.d.ts +35 -0
  110. package/dist/integrations/quality-gate.d.ts.map +1 -0
  111. package/dist/integrations/sarif-report.d.ts +48 -0
  112. package/dist/integrations/sarif-report.d.ts.map +1 -0
  113. package/dist/intelligence/autonomous-repair-workflow.d.ts +31 -0
  114. package/dist/intelligence/autonomous-repair-workflow.d.ts.map +1 -0
  115. package/dist/intelligence/business-intent.d.ts +58 -0
  116. package/dist/intelligence/business-intent.d.ts.map +1 -0
  117. package/dist/intelligence/evidence-bundle.d.ts +364 -0
  118. package/dist/intelligence/evidence-bundle.d.ts.map +1 -0
  119. package/dist/intelligence/failure-clustering.d.ts +38 -0
  120. package/dist/intelligence/failure-clustering.d.ts.map +1 -0
  121. package/dist/intelligence/generation-agent.d.ts +120 -0
  122. package/dist/intelligence/generation-agent.d.ts.map +1 -0
  123. package/dist/intelligence/generation-verifier.d.ts +151 -0
  124. package/dist/intelligence/generation-verifier.d.ts.map +1 -0
  125. package/dist/intelligence/llm-proposal.d.ts +377 -0
  126. package/dist/intelligence/llm-proposal.d.ts.map +1 -0
  127. package/dist/intelligence/locator-disambiguation-engine.d.ts +38 -0
  128. package/dist/intelligence/locator-disambiguation-engine.d.ts.map +1 -0
  129. package/dist/intelligence/model-contract.d.ts +86 -0
  130. package/dist/intelligence/model-contract.d.ts.map +1 -0
  131. package/dist/intelligence/model-provider.d.ts +75 -0
  132. package/dist/intelligence/model-provider.d.ts.map +1 -0
  133. package/dist/intelligence/project-intelligence.d.ts +22 -0
  134. package/dist/intelligence/project-intelligence.d.ts.map +1 -0
  135. package/dist/intelligence/repair-agent.d.ts +46 -0
  136. package/dist/intelligence/repair-agent.d.ts.map +1 -0
  137. package/dist/intelligence/repository-profile.d.ts +131 -0
  138. package/dist/intelligence/repository-profile.d.ts.map +1 -0
  139. package/dist/intelligence/suite-intelligence.d.ts +59 -0
  140. package/dist/intelligence/suite-intelligence.d.ts.map +1 -0
  141. package/dist/intelligence/test-intelligence.d.ts +44 -0
  142. package/dist/intelligence/test-intelligence.d.ts.map +1 -0
  143. package/dist/intelligence/trace-proposal-workflow.d.ts +30 -0
  144. package/dist/intelligence/trace-proposal-workflow.d.ts.map +1 -0
  145. package/dist/intelligence/verified-llm-repair-workflow.d.ts +21 -0
  146. package/dist/intelligence/verified-llm-repair-workflow.d.ts.map +1 -0
  147. package/dist/intelligence/workflow-planner.d.ts +278 -0
  148. package/dist/intelligence/workflow-planner.d.ts.map +1 -0
  149. package/dist/mcp/server.d.ts +32 -0
  150. package/dist/mcp/server.d.ts.map +1 -0
  151. package/dist/memory/stability-ledger.d.ts +46 -0
  152. package/dist/memory/stability-ledger.d.ts.map +1 -0
  153. package/dist/perception/app-model.d.ts +55 -0
  154. package/dist/perception/app-model.d.ts.map +1 -0
  155. package/dist/perception/crawler.d.ts +18 -0
  156. package/dist/perception/crawler.d.ts.map +1 -0
  157. package/dist/perception/index.d.ts +3 -0
  158. package/dist/perception/index.d.ts.map +1 -0
  159. package/dist/repair/approval-service.d.ts +139 -0
  160. package/dist/repair/approval-service.d.ts.map +1 -0
  161. package/dist/repair/assertion-hasher.d.ts +62 -0
  162. package/dist/repair/assertion-hasher.d.ts.map +1 -0
  163. package/dist/repair/ast-patcher.d.ts +36 -0
  164. package/dist/repair/ast-patcher.d.ts.map +1 -0
  165. package/dist/repair/browser-operation-executor.d.ts +27 -0
  166. package/dist/repair/browser-operation-executor.d.ts.map +1 -0
  167. package/dist/repair/diff-allowlist.d.ts +74 -0
  168. package/dist/repair/diff-allowlist.d.ts.map +1 -0
  169. package/dist/repair/intent-validator.d.ts +157 -0
  170. package/dist/repair/intent-validator.d.ts.map +1 -0
  171. package/dist/repair/repair-operations.d.ts +40 -0
  172. package/dist/repair/repair-operations.d.ts.map +1 -0
  173. package/dist/repair/repair-planner.d.ts +98 -0
  174. package/dist/repair/repair-planner.d.ts.map +1 -0
  175. package/dist/repair/repair-workflow.d.ts +42 -0
  176. package/dist/repair/repair-workflow.d.ts.map +1 -0
  177. package/dist/repair/safe-operation-compiler.d.ts +15 -0
  178. package/dist/repair/safe-operation-compiler.d.ts.map +1 -0
  179. package/dist/repair/source-mapper.d.ts +19 -0
  180. package/dist/repair/source-mapper.d.ts.map +1 -0
  181. package/dist/repair/statement-patcher.d.ts +75 -0
  182. package/dist/repair/statement-patcher.d.ts.map +1 -0
  183. package/dist/repair/verification-gates.d.ts +25 -0
  184. package/dist/repair/verification-gates.d.ts.map +1 -0
  185. package/dist/repair/workflow-plan.d.ts +35 -0
  186. package/dist/repair/workflow-plan.d.ts.map +1 -0
  187. package/dist/repair/workspace-promoter.d.ts +43 -0
  188. package/dist/repair/workspace-promoter.d.ts.map +1 -0
  189. package/dist/reproduce/diagnostic-rerunner.d.ts +40 -0
  190. package/dist/reproduce/diagnostic-rerunner.d.ts.map +1 -0
  191. package/dist/reproduce/direct-browser-investigator.d.ts +41 -0
  192. package/dist/reproduce/direct-browser-investigator.d.ts.map +1 -0
  193. package/dist/reproduce/interactive-workflow-runner.d.ts +43 -0
  194. package/dist/reproduce/interactive-workflow-runner.d.ts.map +1 -0
  195. package/dist/sandbox/dependency-strategy.d.ts +45 -0
  196. package/dist/sandbox/dependency-strategy.d.ts.map +1 -0
  197. package/dist/sandbox/execution-runner.d.ts +81 -0
  198. package/dist/sandbox/execution-runner.d.ts.map +1 -0
  199. package/dist/sandbox/worktree-manager.d.ts +51 -0
  200. package/dist/sandbox/worktree-manager.d.ts.map +1 -0
  201. package/dist/selector/abstention-policy.d.ts +34 -0
  202. package/dist/selector/abstention-policy.d.ts.map +1 -0
  203. package/dist/selector/candidate-extractor.d.ts +71 -0
  204. package/dist/selector/candidate-extractor.d.ts.map +1 -0
  205. package/dist/selector/live-locator-verifier.d.ts +28 -0
  206. package/dist/selector/live-locator-verifier.d.ts.map +1 -0
  207. package/dist/selector/repair-candidate-ranker.d.ts +10 -0
  208. package/dist/selector/repair-candidate-ranker.d.ts.map +1 -0
  209. package/dist/selector/selector-scorer.d.ts +43 -0
  210. package/dist/selector/selector-scorer.d.ts.map +1 -0
  211. package/dist/trace/adapters/adapter-registry.d.ts +18 -0
  212. package/dist/trace/adapters/adapter-registry.d.ts.map +1 -0
  213. package/dist/trace/adapters/trace-adapter.d.ts +21 -0
  214. package/dist/trace/adapters/trace-adapter.d.ts.map +1 -0
  215. package/dist/trace/adapters/unknown-adapter.d.ts +10 -0
  216. package/dist/trace/adapters/unknown-adapter.d.ts.map +1 -0
  217. package/dist/trace/adapters/v10-adapter.d.ts +10 -0
  218. package/dist/trace/adapters/v10-adapter.d.ts.map +1 -0
  219. package/dist/trace/adapters/v4-adapter.d.ts +11 -0
  220. package/dist/trace/adapters/v4-adapter.d.ts.map +1 -0
  221. package/dist/trace/adapters/v8-adapter.d.ts +10 -0
  222. package/dist/trace/adapters/v8-adapter.d.ts.map +1 -0
  223. package/dist/trace/adapters/v9-adapter.d.ts +10 -0
  224. package/dist/trace/adapters/v9-adapter.d.ts.map +1 -0
  225. package/dist/trace/archive-reader.d.ts +44 -0
  226. package/dist/trace/archive-reader.d.ts.map +1 -0
  227. package/dist/trace/snapshot-resolver.d.ts +55 -0
  228. package/dist/trace/snapshot-resolver.d.ts.map +1 -0
  229. package/dist/trace/trace-parser.d.ts +50 -0
  230. package/dist/trace/trace-parser.d.ts.map +1 -0
  231. package/dist/trace/version-detector.d.ts +20 -0
  232. package/dist/trace/version-detector.d.ts.map +1 -0
  233. package/package.json +76 -6
  234. package/skills/README.md +20 -0
  235. package/skills/pie-failure-investigation/SKILL.md +16 -0
  236. package/skills/pie-quality-engineer/SKILL.md +66 -0
  237. package/skills/pie-release-readiness/SKILL.md +22 -0
  238. package/skills/pie-repair-review/SKILL.md +21 -0
  239. package/skills/pie-trace-to-repair/SKILL.md +35 -0
  240. package/src/authoring/author.ts +81 -0
  241. package/src/authoring/generation-contract.ts +283 -0
  242. package/src/authoring/hybrid-author.ts +115 -0
  243. package/src/authoring/index.ts +7 -0
  244. package/src/authoring/llm-author.ts +282 -0
  245. package/src/authoring/naming/index.ts +4 -0
  246. package/src/authoring/naming/name-plan.ts +28 -0
  247. package/src/authoring/naming/name-planner.ts +339 -0
  248. package/src/authoring/naming/naming-linter.ts +292 -0
  249. package/src/authoring/template-author.ts +39 -0
  250. package/src/cli/index.ts +1172 -0
  251. package/src/collectors/collector-interface.ts +8 -0
  252. package/src/collectors/environment-collector.ts +103 -0
  253. package/src/collectors/error-collector.ts +177 -0
  254. package/src/collectors/git-diff-collector.ts +190 -0
  255. package/src/collectors/live-browser-collector.ts +412 -0
  256. package/src/collectors/network-collector.ts +90 -0
  257. package/src/collectors/static-ast-collector.ts +226 -0
  258. package/src/collectors/trace-collector.ts +66 -0
  259. package/src/config/project-config.ts +114 -0
  260. package/src/core/errors/engine-errors.ts +90 -0
  261. package/src/core/types/diagnostic-context.ts +94 -0
  262. package/src/core/types/repair-evidence.ts +45 -0
  263. package/src/core/version.ts +25 -0
  264. package/src/diagnostics/diagnostic-contract.ts +79 -0
  265. package/src/diagnostics/diagnostic-report.ts +204 -0
  266. package/src/diagnostics/evidence-correlator.ts +117 -0
  267. package/src/diagnostics/external-failure-evidence.ts +72 -0
  268. package/src/diagnostics/failure-category.ts +40 -0
  269. package/src/diagnostics/failure-classifier.ts +701 -0
  270. package/src/evaluation/evaluation-scorecard.ts +198 -0
  271. package/src/evaluation/repository-inspector.ts +143 -0
  272. package/src/evaluation/repository-runner.ts +197 -0
  273. package/src/evidence/evidence-model.ts +76 -0
  274. package/src/evidence/evidence-normalizer.ts +322 -0
  275. package/src/evidence/evidence-redactor.ts +218 -0
  276. package/src/framework/config-adapter.ts +131 -0
  277. package/src/gate/index.ts +43 -0
  278. package/src/gate/oracle.ts +69 -0
  279. package/src/gate/quality.ts +101 -0
  280. package/src/gate/safety.ts +94 -0
  281. package/src/generator/architecture-generator.ts +582 -0
  282. package/src/generator/assertion-quality-gate.ts +88 -0
  283. package/src/generator/candidate-writer.ts +288 -0
  284. package/src/generator/test-generator.ts +52 -0
  285. package/src/governance/audit-log.ts +115 -0
  286. package/src/governance/governance-policy.ts +72 -0
  287. package/src/governance/team-dashboard.ts +189 -0
  288. package/src/governance/workflow-audit.ts +72 -0
  289. package/src/index.ts +107 -0
  290. package/src/integrations/ci-summary.ts +37 -0
  291. package/src/integrations/playwright-report-adapter.ts +173 -0
  292. package/src/integrations/quality-gate.ts +95 -0
  293. package/src/integrations/sarif-report.ts +110 -0
  294. package/src/intelligence/autonomous-repair-workflow.ts +139 -0
  295. package/src/intelligence/business-intent.ts +163 -0
  296. package/src/intelligence/evidence-bundle.ts +300 -0
  297. package/src/intelligence/failure-clustering.ts +134 -0
  298. package/src/intelligence/generation-agent.ts +561 -0
  299. package/src/intelligence/generation-verifier.ts +1115 -0
  300. package/src/intelligence/llm-proposal.ts +519 -0
  301. package/src/intelligence/locator-disambiguation-engine.ts +331 -0
  302. package/src/intelligence/model-contract.ts +205 -0
  303. package/src/intelligence/model-provider.ts +348 -0
  304. package/src/intelligence/project-intelligence.ts +140 -0
  305. package/src/intelligence/repair-agent.ts +297 -0
  306. package/src/intelligence/repository-profile.ts +796 -0
  307. package/src/intelligence/suite-intelligence.ts +305 -0
  308. package/src/intelligence/test-intelligence.ts +136 -0
  309. package/src/intelligence/trace-proposal-workflow.ts +318 -0
  310. package/src/intelligence/verified-llm-repair-workflow.ts +48 -0
  311. package/src/intelligence/workflow-planner.ts +966 -0
  312. package/src/mcp/server.ts +970 -0
  313. package/src/memory/stability-ledger.ts +243 -0
  314. package/src/perception/app-model.ts +32 -0
  315. package/src/perception/crawler.ts +263 -0
  316. package/src/perception/index.ts +3 -0
  317. package/src/repair/approval-service.ts +541 -0
  318. package/src/repair/assertion-hasher.ts +386 -0
  319. package/src/repair/ast-patcher.ts +223 -0
  320. package/src/repair/browser-operation-executor.ts +263 -0
  321. package/src/repair/diff-allowlist.ts +612 -0
  322. package/src/repair/intent-validator.ts +761 -0
  323. package/src/repair/repair-operations.ts +65 -0
  324. package/src/repair/repair-planner.ts +467 -0
  325. package/src/repair/repair-workflow.ts +241 -0
  326. package/src/repair/safe-operation-compiler.ts +68 -0
  327. package/src/repair/source-mapper.ts +156 -0
  328. package/src/repair/statement-patcher.ts +544 -0
  329. package/src/repair/verification-gates.ts +46 -0
  330. package/src/repair/workflow-plan.ts +59 -0
  331. package/src/repair/workspace-promoter.ts +313 -0
  332. package/src/reproduce/diagnostic-rerunner.ts +287 -0
  333. package/src/reproduce/direct-browser-investigator.ts +208 -0
  334. package/src/reproduce/interactive-workflow-runner.ts +828 -0
  335. package/src/sandbox/dependency-strategy.ts +215 -0
  336. package/src/sandbox/execution-runner.ts +408 -0
  337. package/src/sandbox/worktree-manager.ts +287 -0
  338. package/src/selector/abstention-policy.ts +175 -0
  339. package/src/selector/candidate-extractor.ts +343 -0
  340. package/src/selector/live-locator-verifier.ts +139 -0
  341. package/src/selector/repair-candidate-ranker.ts +92 -0
  342. package/src/selector/selector-scorer.ts +344 -0
  343. package/src/trace/adapters/adapter-registry.ts +49 -0
  344. package/src/trace/adapters/trace-adapter.ts +28 -0
  345. package/src/trace/adapters/unknown-adapter.ts +50 -0
  346. package/src/trace/adapters/v10-adapter.ts +39 -0
  347. package/src/trace/adapters/v4-adapter.ts +31 -0
  348. package/src/trace/adapters/v8-adapter.ts +42 -0
  349. package/src/trace/adapters/v9-adapter.ts +39 -0
  350. package/src/trace/archive-reader.ts +246 -0
  351. package/src/trace/snapshot-resolver.ts +308 -0
  352. package/src/trace/trace-parser.ts +467 -0
  353. package/src/trace/version-detector.ts +81 -0
@@ -0,0 +1,305 @@
1
+ import fs from 'node:fs/promises';
2
+ import fsSync from 'node:fs';
3
+ import path from 'node:path';
4
+
5
+ export type TestRunStatus = 'PASS' | 'FAIL' | 'SKIP' | 'TIMEOUT' | 'BLOCKED';
6
+
7
+ export interface TestRunObservation {
8
+ runId: string;
9
+ testId: string;
10
+ repository?: string | undefined;
11
+ title?: string | undefined;
12
+ sourceFile?: string | undefined;
13
+ status: TestRunStatus;
14
+ durationMs?: number | undefined;
15
+ timestamp?: number | undefined;
16
+ retryIndex?: number | undefined;
17
+ failureClusterId?: string | undefined;
18
+ }
19
+
20
+ export interface TestStabilitySummary {
21
+ testId: string;
22
+ repository?: string | undefined;
23
+ title?: string | undefined;
24
+ sourceFile?: string | undefined;
25
+ runs: number;
26
+ passes: number;
27
+ failures: number;
28
+ skips: number;
29
+ blocked: number;
30
+ passRate: number;
31
+ flakeRate: number;
32
+ flaky: boolean;
33
+ averageDurationMs?: number | undefined;
34
+ p95DurationMs?: number | undefined;
35
+ lastStatus: TestRunStatus;
36
+ failureClusters: string[];
37
+ }
38
+
39
+ export interface TestImpactCandidate {
40
+ testId: string;
41
+ title?: string | undefined;
42
+ sourceFile: string;
43
+ dependencies?: string[] | undefined;
44
+ tags?: string[] | undefined;
45
+ }
46
+
47
+ export interface ImpactedTest {
48
+ testId: string;
49
+ title?: string | undefined;
50
+ sourceFile: string;
51
+ confidence: 'direct' | 'dependency' | 'directory' | 'tag' | 'unknown';
52
+ reasons: string[];
53
+ }
54
+
55
+ export interface RepositoryImpactAnalysis {
56
+ root: string;
57
+ changedFiles: string[];
58
+ candidates: TestImpactCandidate[];
59
+ impactedTests: ImpactedTest[];
60
+ limitations: string[];
61
+ }
62
+
63
+ function round(value: number): number {
64
+ return Number(value.toFixed(3));
65
+ }
66
+
67
+ function percentile(values: number[], percentileValue: number): number | undefined {
68
+ if (values.length === 0) return undefined;
69
+ const sorted = [...values].sort((left, right) => left - right);
70
+ const index = Math.min(sorted.length - 1, Math.ceil((percentileValue / 100) * sorted.length) - 1);
71
+ return sorted[Math.max(0, index)];
72
+ }
73
+
74
+ export function analyzeTestStability(records: TestRunObservation[]): TestStabilitySummary[] {
75
+ const groups = new Map<string, TestRunObservation[]>();
76
+ for (const record of records) {
77
+ const existing = groups.get(record.testId);
78
+ if (existing) existing.push(record);
79
+ else groups.set(record.testId, [record]);
80
+ }
81
+
82
+ return [...groups.values()].map((runs) => {
83
+ const passes = runs.filter((run) => run.status === 'PASS').length;
84
+ const failures = runs.filter((run) => run.status === 'FAIL' || run.status === 'TIMEOUT').length;
85
+ const skips = runs.filter((run) => run.status === 'SKIP').length;
86
+ const blocked = runs.filter((run) => run.status === 'BLOCKED').length;
87
+ const durations = runs.map((run) => run.durationMs).filter((value): value is number => typeof value === 'number');
88
+ const distinctStatuses = new Set(runs.map((run) => run.status));
89
+ const flaky = passes > 0 && failures > 0;
90
+ const lastRun = [...runs].sort((left, right) => (right.timestamp ?? 0) - (left.timestamp ?? 0))[0]!;
91
+ return {
92
+ testId: lastRun.testId,
93
+ repository: lastRun.repository,
94
+ title: lastRun.title,
95
+ sourceFile: lastRun.sourceFile,
96
+ runs: runs.length,
97
+ passes,
98
+ failures,
99
+ skips,
100
+ blocked,
101
+ passRate: round(passes / runs.length),
102
+ flakeRate: round(distinctStatuses.size > 1 ? Math.min(passes, failures) / runs.length : 0),
103
+ flaky,
104
+ averageDurationMs: durations.length > 0 ? round(durations.reduce((sum, value) => sum + value, 0) / durations.length) : undefined,
105
+ p95DurationMs: percentile(durations, 95),
106
+ lastStatus: lastRun.status,
107
+ failureClusters: [...new Set(runs.map((run) => run.failureClusterId).filter((value): value is string => Boolean(value)))].sort(),
108
+ };
109
+ }).sort((left, right) => Number(right.flaky) - Number(left.flaky) || right.failures - left.failures || left.testId.localeCompare(right.testId));
110
+ }
111
+
112
+ function normalizedPath(value: string): string {
113
+ return value.replace(/\\/g, '/').replace(/^\.\//, '').toLowerCase();
114
+ }
115
+
116
+ async function listTestFiles(root: string, depth = 0): Promise<string[]> {
117
+ if (depth > 12) return [];
118
+ const ignored = new Set(['node_modules', '.git', 'dist', 'build', 'coverage', 'test-results', 'playwright-report']);
119
+ let entries;
120
+ try {
121
+ entries = await fs.readdir(root, { withFileTypes: true });
122
+ } catch {
123
+ return [];
124
+ }
125
+ const files: string[] = [];
126
+ for (const entry of entries) {
127
+ if (ignored.has(entry.name)) continue;
128
+ const fullPath = path.join(root, entry.name);
129
+ if (entry.isDirectory()) files.push(...await listTestFiles(fullPath, depth + 1));
130
+ else if (/\.(?:spec|test)\.(?:ts|tsx|js|jsx|mjs|cjs)$/i.test(entry.name)) files.push(fullPath);
131
+ }
132
+ return files;
133
+ }
134
+
135
+ interface PathAlias {
136
+ pattern: string;
137
+ targets: string[];
138
+ }
139
+
140
+ function resolveImport(root: string, sourceFile: string, specifier: string, aliases: PathAlias[]): string | undefined {
141
+ let base: string | undefined;
142
+ if (specifier.startsWith('.')) {
143
+ base = path.resolve(path.dirname(sourceFile), specifier);
144
+ } else {
145
+ const alias = aliases.find((candidate) => {
146
+ if (candidate.pattern.includes('*')) {
147
+ const [prefix, suffix] = candidate.pattern.split('*');
148
+ return specifier.startsWith(prefix ?? '') && specifier.endsWith(suffix ?? '');
149
+ }
150
+ return candidate.pattern === specifier;
151
+ });
152
+ if (alias) {
153
+ const [prefix, suffix] = alias.pattern.split('*');
154
+ const wildcard = alias.pattern.includes('*')
155
+ ? specifier.slice((prefix ?? '').length, specifier.length - (suffix ?? '').length || undefined)
156
+ : '';
157
+ const target = alias.targets[0];
158
+ if (target) base = path.resolve(root, target.replace('*', wildcard));
159
+ }
160
+ }
161
+ if (!base) return undefined;
162
+ const candidates = [
163
+ base,
164
+ ...['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'].map((extension) => `${base}${extension}`),
165
+ ...['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'].map((extension) => path.join(base, `index${extension}`)),
166
+ ];
167
+ const resolved = candidates.find((candidate) => fsSync.existsSync(candidate) && fsSync.statSync(candidate).isFile());
168
+ return resolved ? path.relative(root, resolved).replace(/\\/g, '/') : undefined;
169
+ }
170
+
171
+ function extractImportSpecifiers(source: string): string[] {
172
+ const imports = new Set<string>();
173
+ const patterns = [
174
+ /\b(?:import|export)\s+(?:[\s\S]*?\s+from\s+)?['"]([^'"]+)['"]/g,
175
+ /\b(?:require|import)\(\s*['"]([^'"]+)['"]\s*\)/g,
176
+ ];
177
+ for (const pattern of patterns) {
178
+ for (const match of source.matchAll(pattern)) {
179
+ const value = match[1];
180
+ if (value) imports.add(value);
181
+ }
182
+ }
183
+ return [...imports];
184
+ }
185
+
186
+ async function loadPathAliases(root: string): Promise<PathAlias[]> {
187
+ const tsconfigPath = path.join(root, 'tsconfig.json');
188
+ try {
189
+ const raw = await fs.readFile(tsconfigPath, 'utf8');
190
+ const withoutComments = raw.replace(/^\s*\/\/.*$/gm, '');
191
+ const parsed = JSON.parse(withoutComments) as { compilerOptions?: { baseUrl?: string; paths?: Record<string, string[]> } };
192
+ const baseUrl = parsed.compilerOptions?.baseUrl ?? '.';
193
+ return Object.entries(parsed.compilerOptions?.paths ?? {}).map(([pattern, targets]) => ({
194
+ pattern,
195
+ targets: targets.map((target) => path.join(baseUrl, target)),
196
+ }));
197
+ } catch {
198
+ return [];
199
+ }
200
+ }
201
+
202
+ async function collectTransitiveDependencies(root: string, entry: string, aliases: PathAlias[], visited = new Set<string>()): Promise<string[]> {
203
+ const absoluteEntry = path.resolve(entry);
204
+ if (visited.has(absoluteEntry)) return [];
205
+ visited.add(absoluteEntry);
206
+
207
+ let source: string;
208
+ try {
209
+ source = await fs.readFile(absoluteEntry, 'utf8');
210
+ } catch {
211
+ return [];
212
+ }
213
+
214
+ const dependencies: string[] = [];
215
+ for (const specifier of extractImportSpecifiers(source)) {
216
+ const dependency = resolveImport(root, absoluteEntry, specifier, aliases);
217
+ if (!dependency) continue;
218
+ dependencies.push(dependency);
219
+ const nested = await collectTransitiveDependencies(root, path.resolve(root, dependency), aliases, visited);
220
+ dependencies.push(...nested);
221
+ }
222
+ return [...new Set(dependencies)];
223
+ }
224
+
225
+ /** Discovers test impact metadata from a repository's real test sources. */
226
+ export async function discoverTestImpactCandidates(root: string): Promise<TestImpactCandidate[]> {
227
+ const projectRoot = path.resolve(root);
228
+ const files = await listTestFiles(projectRoot);
229
+ const aliases = await loadPathAliases(projectRoot);
230
+ const candidates: TestImpactCandidate[] = [];
231
+ for (const file of files) {
232
+ let source: string;
233
+ try {
234
+ source = await fs.readFile(file, 'utf8');
235
+ } catch {
236
+ continue;
237
+ }
238
+ const dependencies = await collectTransitiveDependencies(projectRoot, file, aliases);
239
+ const relativeSource = path.relative(projectRoot, file).replace(/\\/g, '/');
240
+ const tags = [...new Set([...source.matchAll(/@([A-Za-z][\w-]*)/g)].map((match) => match[1]!.toLowerCase()))].sort();
241
+ candidates.push({
242
+ testId: relativeSource,
243
+ title: relativeSource,
244
+ sourceFile: relativeSource,
245
+ ...(dependencies.length > 0 ? { dependencies } : {}),
246
+ ...(tags.length > 0 ? { tags } : {}),
247
+ });
248
+ }
249
+ return candidates.sort((left, right) => left.testId.localeCompare(right.testId));
250
+ }
251
+
252
+ /** Builds a deterministic changed-file impact report for a repository. */
253
+ export async function analyzeRepositoryImpact(root: string, changedFiles: string[]): Promise<RepositoryImpactAnalysis> {
254
+ const projectRoot = path.resolve(root);
255
+ const candidates = await discoverTestImpactCandidates(projectRoot);
256
+ const normalizedChanged = changedFiles.map((file) => {
257
+ const absolute = path.isAbsolute(file) ? file : path.resolve(projectRoot, file);
258
+ return path.relative(projectRoot, absolute).replace(/\\/g, '/');
259
+ });
260
+ return {
261
+ root: projectRoot,
262
+ changedFiles: [...new Set(normalizedChanged)].sort(),
263
+ candidates,
264
+ impactedTests: selectImpactedTests(normalizedChanged, candidates),
265
+ limitations: [
266
+ 'Impact is based on resolved relative/path-alias imports, bounded transitive traversal, directory proximity, and tags.',
267
+ 'Package imports and dynamic runtime dependencies require explicit metadata or a future project graph.',
268
+ ],
269
+ };
270
+ }
271
+
272
+ function sameDirectory(left: string, right: string): boolean {
273
+ const leftParts = normalizedPath(left).split('/');
274
+ const rightParts = normalizedPath(right).split('/');
275
+ leftParts.pop();
276
+ rightParts.pop();
277
+ return leftParts.join('/') === rightParts.join('/');
278
+ }
279
+
280
+ export function selectImpactedTests(changedFiles: string[], tests: TestImpactCandidate[]): ImpactedTest[] {
281
+ const changed = changedFiles.map(normalizedPath);
282
+ return tests.flatMap((test) => {
283
+ const source = normalizedPath(test.sourceFile);
284
+ const reasons: string[] = [];
285
+ let confidence: ImpactedTest['confidence'] = 'unknown';
286
+ if (changed.includes(source)) {
287
+ confidence = 'direct';
288
+ reasons.push(`Test source changed: ${test.sourceFile}`);
289
+ } else if (test.dependencies?.some((dependency) => changed.includes(normalizedPath(dependency)))) {
290
+ confidence = 'dependency';
291
+ reasons.push('A declared test dependency changed');
292
+ } else if (changed.some((file) => sameDirectory(file, source))) {
293
+ confidence = 'directory';
294
+ reasons.push('A file in the test source directory changed');
295
+ } else if (test.tags?.some((tag) => changed.some((file) => file.includes(tag.toLowerCase())))) {
296
+ confidence = 'tag';
297
+ reasons.push('A changed path matches a declared test tag');
298
+ }
299
+ return confidence === 'unknown' ? [] : [{ testId: test.testId, title: test.title, sourceFile: test.sourceFile, confidence, reasons }];
300
+ }).sort((left, right) => {
301
+ const rank = { direct: 4, dependency: 3, directory: 2, tag: 1, unknown: 0 };
302
+ return rank[right.confidence] - rank[left.confidence] || left.testId.localeCompare(right.testId);
303
+ });
304
+ }
305
+
@@ -0,0 +1,136 @@
1
+ import { BusinessIntent, extractBusinessIntent } from './business-intent.js';
2
+
3
+ export interface RequirementInput {
4
+ id: string;
5
+ title: string;
6
+ description?: string | undefined;
7
+ tags?: string[] | undefined;
8
+ }
9
+
10
+ export interface ExistingTestInput {
11
+ id: string;
12
+ title?: string | undefined;
13
+ sourceFile?: string | undefined;
14
+ sourceText: string;
15
+ fixtureNames?: string[] | undefined;
16
+ pageObjectNames?: string[] | undefined;
17
+ }
18
+
19
+ export interface PlannedTestScenario {
20
+ id: string;
21
+ requirementId: string;
22
+ title: string;
23
+ intent: BusinessIntent;
24
+ requiredFixtures: string[];
25
+ preferredPageObjects: string[];
26
+ }
27
+
28
+ export interface CoverageMatch {
29
+ scenarioId: string;
30
+ status: 'covered' | 'partial' | 'missing';
31
+ score: number;
32
+ matchedTestIds: string[];
33
+ reasons: string[];
34
+ }
35
+
36
+ export interface DuplicateTestGroup {
37
+ testIds: string[];
38
+ similarity: number;
39
+ reason: string;
40
+ }
41
+
42
+ export interface TestIntelligencePlan {
43
+ scenarios: PlannedTestScenario[];
44
+ coverage: CoverageMatch[];
45
+ missingScenarios: string[];
46
+ highRiskScenarios: string[];
47
+ duplicateTests: DuplicateTestGroup[];
48
+ }
49
+
50
+ function tokens(intent: BusinessIntent): Set<string> {
51
+ return new Set([...intent.actions, ...intent.entities, ...intent.expectedOutcomes]
52
+ .flatMap((value) => value.toLowerCase().split(/[^a-z0-9]+/).filter((token) => token.length >= 3)));
53
+ }
54
+
55
+ function similarity(left: BusinessIntent, right: BusinessIntent): number {
56
+ const leftTokens = tokens(left);
57
+ const rightTokens = tokens(right);
58
+ if (leftTokens.size === 0 || rightTokens.size === 0) return 0;
59
+ const intersection = [...leftTokens].filter((token) => rightTokens.has(token)).length;
60
+ const union = new Set([...leftTokens, ...rightTokens]).size;
61
+ return union > 0 ? intersection / union : 0;
62
+ }
63
+
64
+ function inferFixtures(requirement: RequirementInput): string[] {
65
+ const text = `${requirement.title} ${requirement.description ?? ''}`.toLowerCase();
66
+ return ['auth', 'authenticated', 'login', 'permission', 'admin'].some((term) => text.includes(term)) ? ['authenticatedPage'] : [];
67
+ }
68
+
69
+ function inferPageObjects(intent: BusinessIntent): string[] {
70
+ return intent.entities.slice(0, 3).map((entity) => `${entity[0]!.toUpperCase()}${entity.slice(1)}Page`);
71
+ }
72
+
73
+ function scenarioFor(requirement: RequirementInput): PlannedTestScenario {
74
+ const intent = extractBusinessIntent({
75
+ id: requirement.id,
76
+ title: requirement.title,
77
+ sourceText: requirement.description ?? requirement.title,
78
+ });
79
+ return {
80
+ id: `scenario-${requirement.id}`,
81
+ requirementId: requirement.id,
82
+ title: requirement.title,
83
+ intent,
84
+ requiredFixtures: inferFixtures(requirement),
85
+ preferredPageObjects: inferPageObjects(intent),
86
+ };
87
+ }
88
+
89
+ export function buildTestIntelligencePlan(
90
+ requirements: RequirementInput[],
91
+ existingTests: ExistingTestInput[] = [],
92
+ ): TestIntelligencePlan {
93
+ const scenarios = requirements.map(scenarioFor);
94
+ const indexedTests = existingTests.map((test) => ({
95
+ test,
96
+ intent: extractBusinessIntent({
97
+ id: test.id,
98
+ sourceText: test.sourceText,
99
+ ...(test.title !== undefined ? { title: test.title } : {}),
100
+ ...(test.sourceFile !== undefined ? { sourcePath: test.sourceFile } : {}),
101
+ }),
102
+ }));
103
+ const coverage = scenarios.map((scenario) => {
104
+ const matches = indexedTests.map((candidate) => ({ ...candidate, score: similarity(scenario.intent, candidate.intent) }))
105
+ .filter((candidate) => candidate.score >= 0.25)
106
+ .sort((left, right) => right.score - left.score);
107
+ const best = matches[0]?.score ?? 0;
108
+ const status: CoverageMatch['status'] = best >= 0.7 ? 'covered' : best >= 0.4 ? 'partial' : 'missing';
109
+ const reasons = status === 'missing'
110
+ ? ['No existing test shares enough intent evidence']
111
+ : [`Best intent similarity: ${best.toFixed(3)}`, ...(matches[0]?.test.fixtureNames?.length ? [`Uses fixture(s): ${matches[0].test.fixtureNames.join(', ')}`] : [])];
112
+ return {
113
+ scenarioId: scenario.id,
114
+ status,
115
+ score: Number(best.toFixed(3)),
116
+ matchedTestIds: matches.slice(0, 3).map((match) => match.test.id),
117
+ reasons,
118
+ };
119
+ });
120
+
121
+ const duplicateTests: DuplicateTestGroup[] = [];
122
+ for (let leftIndex = 0; leftIndex < indexedTests.length; leftIndex += 1) {
123
+ for (let rightIndex = leftIndex + 1; rightIndex < indexedTests.length; rightIndex += 1) {
124
+ const score = similarity(indexedTests[leftIndex]!.intent, indexedTests[rightIndex]!.intent);
125
+ if (score >= 0.8) duplicateTests.push({ testIds: [indexedTests[leftIndex]!.test.id, indexedTests[rightIndex]!.test.id], similarity: Number(score.toFixed(3)), reason: 'Tests have highly overlapping action, entity, and assertion intent.' });
126
+ }
127
+ }
128
+
129
+ return {
130
+ scenarios,
131
+ coverage,
132
+ missingScenarios: coverage.filter((entry) => entry.status === 'missing').map((entry) => entry.scenarioId),
133
+ highRiskScenarios: scenarios.filter((scenario) => scenario.intent.risk === 'critical' || scenario.intent.risk === 'high').map((scenario) => scenario.id),
134
+ duplicateTests,
135
+ };
136
+ }