@avi770/testteam 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (325) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/LICENSE +21 -0
  3. package/README.md +167 -0
  4. package/agents/01-analyst.ts +100 -0
  5. package/agents/02-seed-architect.ts +59 -0
  6. package/agents/03-test-generator.ts +191 -0
  7. package/agents/04-unit-runner.ts +160 -0
  8. package/agents/05-browser-crawler.ts +790 -0
  9. package/agents/06-api-exerciser.ts +311 -0
  10. package/agents/07-security-scout.ts +188 -0
  11. package/agents/08-a11y-guardian.ts +212 -0
  12. package/agents/09-healer.ts +228 -0
  13. package/agents/10-reporter.ts +266 -0
  14. package/agents/11-fixer.ts +253 -0
  15. package/agents/12-ux-inspector.ts +444 -0
  16. package/agents/13-performance-profiler.ts +271 -0
  17. package/agents/14-data-integrity-auditor.ts +417 -0
  18. package/agents/15-regression-sentinel.ts +307 -0
  19. package/agents/16-chaos-agent.ts +228 -0
  20. package/agents/17-documentation-validator.ts +266 -0
  21. package/agents/18-integration-watchdog.ts +178 -0
  22. package/agents/19-tenant-isolation-auditor.ts +199 -0
  23. package/agents/20-workflow-completion-tester.ts +203 -0
  24. package/agents/21-state-session-tester.ts +262 -0
  25. package/agents/22-email-notification-verifier.ts +244 -0
  26. package/agents/23-migration-tester.ts +80 -0
  27. package/agents/__tests__/01-analyst.test.ts +188 -0
  28. package/agents/__tests__/02-seed-architect.test.ts +152 -0
  29. package/agents/__tests__/03-test-generator-full.test.ts +321 -0
  30. package/agents/__tests__/03-test-generator.test.ts +318 -0
  31. package/agents/__tests__/04-unit-runner.test.ts +320 -0
  32. package/agents/__tests__/05-browser-crawler-beta.test.ts +492 -0
  33. package/agents/__tests__/05-browser-crawler-release.test.ts +412 -0
  34. package/agents/__tests__/05-browser-crawler-uat.test.ts +578 -0
  35. package/agents/__tests__/05-browser-crawler.test.ts +518 -0
  36. package/agents/__tests__/06-api-exerciser.test.ts +619 -0
  37. package/agents/__tests__/07-security-scout.test.ts +382 -0
  38. package/agents/__tests__/08-a11y-guardian.test.ts +530 -0
  39. package/agents/__tests__/09-healer.test.ts +384 -0
  40. package/agents/__tests__/10-reporter.test.ts +366 -0
  41. package/agents/__tests__/11-fixer.test.ts +406 -0
  42. package/agents/__tests__/12-ux-inspector-extended.test.ts +465 -0
  43. package/agents/__tests__/12-ux-inspector.test.ts +443 -0
  44. package/agents/__tests__/13-performance-profiler.test.ts +411 -0
  45. package/agents/__tests__/14-data-integrity-auditor-extended.test.ts +573 -0
  46. package/agents/__tests__/14-data-integrity-auditor.test.ts +407 -0
  47. package/agents/__tests__/15-regression-sentinel.test.ts +657 -0
  48. package/agents/__tests__/16-chaos-agent.test.ts +427 -0
  49. package/agents/__tests__/17-documentation-validator.test.ts +402 -0
  50. package/agents/__tests__/18-integration-watchdog.test.ts +263 -0
  51. package/agents/__tests__/19-tenant-isolation-auditor.test.ts +400 -0
  52. package/agents/__tests__/20-workflow-completion-tester.test.ts +586 -0
  53. package/agents/__tests__/21-state-session-tester.test.ts +374 -0
  54. package/agents/__tests__/22-email-notification-verifier.test.ts +441 -0
  55. package/agents/__tests__/23-migration-tester.test.ts +145 -0
  56. package/agents/__tests__/base-agent.test.ts +188 -0
  57. package/agents/__tests__/registry.test.ts +218 -0
  58. package/agents/base-agent.ts +77 -0
  59. package/agents/registry.ts +136 -0
  60. package/baselines/api-schemas/.gitkeep +0 -0
  61. package/baselines/performance/.gitkeep +0 -0
  62. package/baselines/screenshots/.gitkeep +0 -0
  63. package/bin/testteam.js +10 -0
  64. package/core/__tests__/ci-output.test.ts +430 -0
  65. package/core/__tests__/cli.test.ts +387 -0
  66. package/core/__tests__/config.test.ts +78 -0
  67. package/core/__tests__/cost-tracker.test.ts +158 -0
  68. package/core/__tests__/evidence.test.ts +265 -0
  69. package/core/__tests__/fix-loop.test.ts +210 -0
  70. package/core/__tests__/health-check.test.ts +44 -0
  71. package/core/__tests__/init.test.ts +609 -0
  72. package/core/__tests__/integration.test.ts +204 -0
  73. package/core/__tests__/license-gen.test.ts +227 -0
  74. package/core/__tests__/license.test.ts +326 -0
  75. package/core/__tests__/multi-browser.test.ts +278 -0
  76. package/core/__tests__/orchestrator.test.ts +519 -0
  77. package/core/__tests__/phase-gate.test.ts +43 -0
  78. package/core/__tests__/report-html.test.ts +398 -0
  79. package/core/__tests__/report-upload.test.ts +325 -0
  80. package/core/__tests__/run-counter.test.ts +234 -0
  81. package/core/ci-output.ts +240 -0
  82. package/core/cli.ts +232 -0
  83. package/core/config.ts +178 -0
  84. package/core/cost-tracker.ts +59 -0
  85. package/core/evidence.ts +132 -0
  86. package/core/fix-loop.ts +85 -0
  87. package/core/health-check.ts +54 -0
  88. package/core/init.ts +546 -0
  89. package/core/license-gen.ts +212 -0
  90. package/core/license.ts +211 -0
  91. package/core/messages.ts +67 -0
  92. package/core/multi-browser.ts +136 -0
  93. package/core/orchestrator.ts +354 -0
  94. package/core/phase-gate.ts +55 -0
  95. package/core/report-html.ts +657 -0
  96. package/core/report-upload.ts +188 -0
  97. package/core/run-counter.ts +175 -0
  98. package/core/types.ts +57 -0
  99. package/dist/agents/01-analyst.d.ts +11 -0
  100. package/dist/agents/01-analyst.d.ts.map +1 -0
  101. package/dist/agents/01-analyst.js +75 -0
  102. package/dist/agents/01-analyst.js.map +1 -0
  103. package/dist/agents/02-seed-architect.d.ts +11 -0
  104. package/dist/agents/02-seed-architect.d.ts.map +1 -0
  105. package/dist/agents/02-seed-architect.js +51 -0
  106. package/dist/agents/02-seed-architect.js.map +1 -0
  107. package/dist/agents/03-test-generator.d.ts +9 -0
  108. package/dist/agents/03-test-generator.d.ts.map +1 -0
  109. package/dist/agents/03-test-generator.js +167 -0
  110. package/dist/agents/03-test-generator.js.map +1 -0
  111. package/dist/agents/04-unit-runner.d.ts +9 -0
  112. package/dist/agents/04-unit-runner.d.ts.map +1 -0
  113. package/dist/agents/04-unit-runner.js +113 -0
  114. package/dist/agents/04-unit-runner.js.map +1 -0
  115. package/dist/agents/05-browser-crawler.d.ts +30 -0
  116. package/dist/agents/05-browser-crawler.d.ts.map +1 -0
  117. package/dist/agents/05-browser-crawler.js +685 -0
  118. package/dist/agents/05-browser-crawler.js.map +1 -0
  119. package/dist/agents/06-api-exerciser.d.ts +23 -0
  120. package/dist/agents/06-api-exerciser.d.ts.map +1 -0
  121. package/dist/agents/06-api-exerciser.js +253 -0
  122. package/dist/agents/06-api-exerciser.js.map +1 -0
  123. package/dist/agents/07-security-scout.d.ts +11 -0
  124. package/dist/agents/07-security-scout.d.ts.map +1 -0
  125. package/dist/agents/07-security-scout.js +142 -0
  126. package/dist/agents/07-security-scout.js.map +1 -0
  127. package/dist/agents/08-a11y-guardian.d.ts +13 -0
  128. package/dist/agents/08-a11y-guardian.d.ts.map +1 -0
  129. package/dist/agents/08-a11y-guardian.js +176 -0
  130. package/dist/agents/08-a11y-guardian.js.map +1 -0
  131. package/dist/agents/09-healer.d.ts +33 -0
  132. package/dist/agents/09-healer.d.ts.map +1 -0
  133. package/dist/agents/09-healer.js +167 -0
  134. package/dist/agents/09-healer.js.map +1 -0
  135. package/dist/agents/10-reporter.d.ts +26 -0
  136. package/dist/agents/10-reporter.d.ts.map +1 -0
  137. package/dist/agents/10-reporter.js +215 -0
  138. package/dist/agents/10-reporter.js.map +1 -0
  139. package/dist/agents/11-fixer.d.ts +26 -0
  140. package/dist/agents/11-fixer.d.ts.map +1 -0
  141. package/dist/agents/11-fixer.js +195 -0
  142. package/dist/agents/11-fixer.js.map +1 -0
  143. package/dist/agents/12-ux-inspector.d.ts +15 -0
  144. package/dist/agents/12-ux-inspector.d.ts.map +1 -0
  145. package/dist/agents/12-ux-inspector.js +364 -0
  146. package/dist/agents/12-ux-inspector.js.map +1 -0
  147. package/dist/agents/13-performance-profiler.d.ts +13 -0
  148. package/dist/agents/13-performance-profiler.d.ts.map +1 -0
  149. package/dist/agents/13-performance-profiler.js +216 -0
  150. package/dist/agents/13-performance-profiler.js.map +1 -0
  151. package/dist/agents/14-data-integrity-auditor.d.ts +12 -0
  152. package/dist/agents/14-data-integrity-auditor.d.ts.map +1 -0
  153. package/dist/agents/14-data-integrity-auditor.js +356 -0
  154. package/dist/agents/14-data-integrity-auditor.js.map +1 -0
  155. package/dist/agents/15-regression-sentinel.d.ts +25 -0
  156. package/dist/agents/15-regression-sentinel.d.ts.map +1 -0
  157. package/dist/agents/15-regression-sentinel.js +251 -0
  158. package/dist/agents/15-regression-sentinel.js.map +1 -0
  159. package/dist/agents/16-chaos-agent.d.ts +9 -0
  160. package/dist/agents/16-chaos-agent.d.ts.map +1 -0
  161. package/dist/agents/16-chaos-agent.js +207 -0
  162. package/dist/agents/16-chaos-agent.js.map +1 -0
  163. package/dist/agents/17-documentation-validator.d.ts +31 -0
  164. package/dist/agents/17-documentation-validator.d.ts.map +1 -0
  165. package/dist/agents/17-documentation-validator.js +246 -0
  166. package/dist/agents/17-documentation-validator.js.map +1 -0
  167. package/dist/agents/18-integration-watchdog.d.ts +10 -0
  168. package/dist/agents/18-integration-watchdog.d.ts.map +1 -0
  169. package/dist/agents/18-integration-watchdog.js +138 -0
  170. package/dist/agents/18-integration-watchdog.js.map +1 -0
  171. package/dist/agents/19-tenant-isolation-auditor.d.ts +9 -0
  172. package/dist/agents/19-tenant-isolation-auditor.d.ts.map +1 -0
  173. package/dist/agents/19-tenant-isolation-auditor.js +166 -0
  174. package/dist/agents/19-tenant-isolation-auditor.js.map +1 -0
  175. package/dist/agents/20-workflow-completion-tester.d.ts +12 -0
  176. package/dist/agents/20-workflow-completion-tester.d.ts.map +1 -0
  177. package/dist/agents/20-workflow-completion-tester.js +159 -0
  178. package/dist/agents/20-workflow-completion-tester.js.map +1 -0
  179. package/dist/agents/21-state-session-tester.d.ts +10 -0
  180. package/dist/agents/21-state-session-tester.d.ts.map +1 -0
  181. package/dist/agents/21-state-session-tester.js +233 -0
  182. package/dist/agents/21-state-session-tester.js.map +1 -0
  183. package/dist/agents/22-email-notification-verifier.d.ts +11 -0
  184. package/dist/agents/22-email-notification-verifier.d.ts.map +1 -0
  185. package/dist/agents/22-email-notification-verifier.js +199 -0
  186. package/dist/agents/22-email-notification-verifier.js.map +1 -0
  187. package/dist/agents/23-migration-tester.d.ts +10 -0
  188. package/dist/agents/23-migration-tester.d.ts.map +1 -0
  189. package/dist/agents/23-migration-tester.js +74 -0
  190. package/dist/agents/23-migration-tester.js.map +1 -0
  191. package/dist/agents/base-agent.d.ts +19 -0
  192. package/dist/agents/base-agent.d.ts.map +1 -0
  193. package/dist/agents/base-agent.js +67 -0
  194. package/dist/agents/base-agent.js.map +1 -0
  195. package/dist/agents/registry.d.ts +29 -0
  196. package/dist/agents/registry.d.ts.map +1 -0
  197. package/dist/agents/registry.js +117 -0
  198. package/dist/agents/registry.js.map +1 -0
  199. package/dist/core/ci-output.d.ts +35 -0
  200. package/dist/core/ci-output.d.ts.map +1 -0
  201. package/dist/core/ci-output.js +193 -0
  202. package/dist/core/ci-output.js.map +1 -0
  203. package/dist/core/cli.d.ts +11 -0
  204. package/dist/core/cli.d.ts.map +1 -0
  205. package/dist/core/cli.js +197 -0
  206. package/dist/core/cli.js.map +1 -0
  207. package/dist/core/config.d.ts +111 -0
  208. package/dist/core/config.d.ts.map +1 -0
  209. package/dist/core/config.js +42 -0
  210. package/dist/core/config.js.map +1 -0
  211. package/dist/core/cost-tracker.d.ts +22 -0
  212. package/dist/core/cost-tracker.d.ts.map +1 -0
  213. package/dist/core/cost-tracker.js +41 -0
  214. package/dist/core/cost-tracker.js.map +1 -0
  215. package/dist/core/evidence.d.ts +28 -0
  216. package/dist/core/evidence.d.ts.map +1 -0
  217. package/dist/core/evidence.js +95 -0
  218. package/dist/core/evidence.js.map +1 -0
  219. package/dist/core/fix-loop.d.ts +29 -0
  220. package/dist/core/fix-loop.d.ts.map +1 -0
  221. package/dist/core/fix-loop.js +70 -0
  222. package/dist/core/fix-loop.js.map +1 -0
  223. package/dist/core/health-check.d.ts +21 -0
  224. package/dist/core/health-check.d.ts.map +1 -0
  225. package/dist/core/health-check.js +26 -0
  226. package/dist/core/health-check.js.map +1 -0
  227. package/dist/core/init.d.ts +2 -0
  228. package/dist/core/init.d.ts.map +1 -0
  229. package/dist/core/init.js +435 -0
  230. package/dist/core/init.js.map +1 -0
  231. package/dist/core/license-gen.d.ts +12 -0
  232. package/dist/core/license-gen.d.ts.map +1 -0
  233. package/dist/core/license-gen.js +169 -0
  234. package/dist/core/license-gen.js.map +1 -0
  235. package/dist/core/license.d.ts +33 -0
  236. package/dist/core/license.d.ts.map +1 -0
  237. package/dist/core/license.js +170 -0
  238. package/dist/core/license.js.map +1 -0
  239. package/dist/core/messages.d.ts +10 -0
  240. package/dist/core/messages.d.ts.map +1 -0
  241. package/dist/core/messages.js +47 -0
  242. package/dist/core/messages.js.map +1 -0
  243. package/dist/core/multi-browser.d.ts +36 -0
  244. package/dist/core/multi-browser.d.ts.map +1 -0
  245. package/dist/core/multi-browser.js +88 -0
  246. package/dist/core/multi-browser.js.map +1 -0
  247. package/dist/core/orchestrator.d.ts +48 -0
  248. package/dist/core/orchestrator.d.ts.map +1 -0
  249. package/dist/core/orchestrator.js +291 -0
  250. package/dist/core/orchestrator.js.map +1 -0
  251. package/dist/core/phase-gate.d.ts +4 -0
  252. package/dist/core/phase-gate.d.ts.map +1 -0
  253. package/dist/core/phase-gate.js +39 -0
  254. package/dist/core/phase-gate.js.map +1 -0
  255. package/dist/core/report-html.d.ts +9 -0
  256. package/dist/core/report-html.d.ts.map +1 -0
  257. package/dist/core/report-html.js +617 -0
  258. package/dist/core/report-html.js.map +1 -0
  259. package/dist/core/report-upload.d.ts +16 -0
  260. package/dist/core/report-upload.d.ts.map +1 -0
  261. package/dist/core/report-upload.js +124 -0
  262. package/dist/core/report-upload.js.map +1 -0
  263. package/dist/core/run-counter.d.ts +40 -0
  264. package/dist/core/run-counter.d.ts.map +1 -0
  265. package/dist/core/run-counter.js +120 -0
  266. package/dist/core/run-counter.js.map +1 -0
  267. package/dist/core/types.d.ts +53 -0
  268. package/dist/core/types.d.ts.map +1 -0
  269. package/dist/core/types.js +2 -0
  270. package/dist/core/types.js.map +1 -0
  271. package/dist/helpers/api-client.d.ts +30 -0
  272. package/dist/helpers/api-client.d.ts.map +1 -0
  273. package/dist/helpers/api-client.js +77 -0
  274. package/dist/helpers/api-client.js.map +1 -0
  275. package/dist/helpers/element-discovery.d.ts +18 -0
  276. package/dist/helpers/element-discovery.d.ts.map +1 -0
  277. package/dist/helpers/element-discovery.js +82 -0
  278. package/dist/helpers/element-discovery.js.map +1 -0
  279. package/dist/helpers/env-resolver.d.ts +29 -0
  280. package/dist/helpers/env-resolver.d.ts.map +1 -0
  281. package/dist/helpers/env-resolver.js +51 -0
  282. package/dist/helpers/env-resolver.js.map +1 -0
  283. package/dist/helpers/form-filler.d.ts +13 -0
  284. package/dist/helpers/form-filler.d.ts.map +1 -0
  285. package/dist/helpers/form-filler.js +98 -0
  286. package/dist/helpers/form-filler.js.map +1 -0
  287. package/dist/helpers/modal-handler.d.ts +16 -0
  288. package/dist/helpers/modal-handler.d.ts.map +1 -0
  289. package/dist/helpers/modal-handler.js +95 -0
  290. package/dist/helpers/modal-handler.js.map +1 -0
  291. package/dist/helpers/navigation.d.ts +37 -0
  292. package/dist/helpers/navigation.d.ts.map +1 -0
  293. package/dist/helpers/navigation.js +83 -0
  294. package/dist/helpers/navigation.js.map +1 -0
  295. package/dist/helpers/quality-gate.d.ts +17 -0
  296. package/dist/helpers/quality-gate.d.ts.map +1 -0
  297. package/dist/helpers/quality-gate.js +144 -0
  298. package/dist/helpers/quality-gate.js.map +1 -0
  299. package/dist/helpers/screenshot.d.ts +24 -0
  300. package/dist/helpers/screenshot.d.ts.map +1 -0
  301. package/dist/helpers/screenshot.js +76 -0
  302. package/dist/helpers/screenshot.js.map +1 -0
  303. package/dist/helpers/seed-validator.d.ts +15 -0
  304. package/dist/helpers/seed-validator.d.ts.map +1 -0
  305. package/dist/helpers/seed-validator.js +53 -0
  306. package/dist/helpers/seed-validator.js.map +1 -0
  307. package/helpers/__tests__/api-client.test.ts +199 -0
  308. package/helpers/__tests__/element-discovery.test.ts +202 -0
  309. package/helpers/__tests__/form-filler-extended.test.ts +212 -0
  310. package/helpers/__tests__/form-filler.test.ts +99 -0
  311. package/helpers/__tests__/modal-handler.test.ts +152 -0
  312. package/helpers/__tests__/navigation.test.ts +214 -0
  313. package/helpers/__tests__/quality-gate.test.ts +117 -0
  314. package/helpers/__tests__/screenshot.test.ts +139 -0
  315. package/helpers/__tests__/seed-validator.test.ts +114 -0
  316. package/helpers/api-client.ts +111 -0
  317. package/helpers/element-discovery.ts +105 -0
  318. package/helpers/env-resolver.ts +69 -0
  319. package/helpers/form-filler.ts +126 -0
  320. package/helpers/modal-handler.ts +108 -0
  321. package/helpers/navigation.ts +100 -0
  322. package/helpers/quality-gate.ts +180 -0
  323. package/helpers/screenshot.ts +111 -0
  324. package/helpers/seed-validator.ts +70 -0
  325. package/package.json +88 -0
@@ -0,0 +1,228 @@
1
+ import * as fs from 'fs/promises';
2
+ import * as path from 'path';
3
+ import { BaseAgent } from './base-agent';
4
+ import { validateCodeQuality } from '../helpers/quality-gate';
5
+ import type { Finding } from '../core/types';
6
+
7
+ /** Maximum number of fix attempts per finding before escalation. */
8
+ const MAX_ATTEMPTS = 3;
9
+
10
+ /** Describes the root-cause category of a test-bug finding. */
11
+ type FixStrategy = 'selector' | 'timing' | 'data' | 'unknown';
12
+
13
+ /** Per-finding attempt tracking (kept in-memory for a single run). */
14
+ interface AttemptRecord {
15
+ findingId: string;
16
+ attempts: number;
17
+ }
18
+
19
+ /**
20
+ * Classifies a test-bug finding into one of three fix strategies based on
21
+ * keywords in the description.
22
+ *
23
+ * - selector: element-locator problems (element not found, stale selector, …)
24
+ * - timing: race-condition / timeout problems
25
+ * - data: seed / fixture / data mismatch problems
26
+ * - unknown: fallback when no keyword matches
27
+ */
28
+ export function classifyFixStrategy(finding: Finding): FixStrategy {
29
+ const lower = finding.description.toLowerCase();
30
+
31
+ if (
32
+ lower.includes('selector') ||
33
+ lower.includes('element not found') ||
34
+ lower.includes('locator') ||
35
+ lower.includes('stale') ||
36
+ lower.includes('no such element')
37
+ ) {
38
+ return 'selector';
39
+ }
40
+
41
+ if (
42
+ lower.includes('timeout') ||
43
+ lower.includes('timing') ||
44
+ lower.includes('race') ||
45
+ lower.includes('wait') ||
46
+ lower.includes('timed out')
47
+ ) {
48
+ return 'timing';
49
+ }
50
+
51
+ if (
52
+ lower.includes('seed') ||
53
+ lower.includes('fixture') ||
54
+ lower.includes('data') ||
55
+ lower.includes('expected value') ||
56
+ lower.includes('mismatch')
57
+ ) {
58
+ return 'data';
59
+ }
60
+
61
+ return 'unknown';
62
+ }
63
+
64
+ /**
65
+ * Generates a suggested fix string given a FixStrategy and the original
66
+ * finding. The generated suggestion intentionally uses plain English so it
67
+ * can be reviewed by a developer or fed to a downstream AI patch agent.
68
+ */
69
+ export function generateFixSuggestion(finding: Finding, strategy: FixStrategy): string {
70
+ switch (strategy) {
71
+ case 'selector':
72
+ return (
73
+ `[Healer] Selector fix for "${finding.module}": ` +
74
+ `Update the element selector/locator — the target element may have changed its ` +
75
+ `data-testid, class, or role attribute. ` +
76
+ `Suggested action: inspect the current DOM and replace the stale selector with ` +
77
+ `a stable data-testid attribute (e.g. data-testid="<component>-<action>").`
78
+ );
79
+
80
+ case 'timing':
81
+ return (
82
+ `[Healer] Timing fix for "${finding.module}": ` +
83
+ `Add an explicit wait before the failing assertion. ` +
84
+ `Suggested action: replace hard sleeps with \`waitFor\` / \`waitForSelector\` ` +
85
+ `or increase the relevant timeout in the test configuration.`
86
+ );
87
+
88
+ case 'data':
89
+ return (
90
+ `[Healer] Data fix for "${finding.module}": ` +
91
+ `Update the test seed or fixture so the expected values match the current schema. ` +
92
+ `Suggested action: re-run \`pnpm seed:test\` to refresh test data, or update ` +
93
+ `the fixture file to reflect the current data model.`
94
+ );
95
+
96
+ default:
97
+ return (
98
+ `[Healer] Unknown strategy for "${finding.module}": ` +
99
+ `Manual investigation required — no known fix pattern matched the description.`
100
+ );
101
+ }
102
+ }
103
+
104
+ export class HealerAgent extends BaseAgent {
105
+ readonly agentId = 9;
106
+ readonly agentName = 'Healer';
107
+
108
+ /** Attempt tracking for the current run (finding id → attempt count). */
109
+ private readonly attemptMap = new Map<string, AttemptRecord>();
110
+
111
+ /** Set to true when there are no findings to process — makes execute() a no-op. */
112
+ private noFindings = false;
113
+
114
+ protected async preFlight(): Promise<void> {
115
+ const testBugsDir = path.join(this.runDir, 'findings', 'test-bugs');
116
+ let entries: string[];
117
+ try {
118
+ entries = await fs.readdir(testBugsDir);
119
+ } catch (err: unknown) {
120
+ if (isNodeError(err) && err.code === 'ENOENT') {
121
+ this.noFindings = true;
122
+ return;
123
+ }
124
+ throw err;
125
+ }
126
+
127
+ const jsonFiles = entries.filter((e) => e.endsWith('.json'));
128
+ if (jsonFiles.length === 0) {
129
+ this.noFindings = true;
130
+ }
131
+ }
132
+
133
+ protected async execute(): Promise<Finding[]> {
134
+ if (this.noFindings) {
135
+ return [];
136
+ }
137
+
138
+ const testBugsDir = path.join(this.runDir, 'findings', 'test-bugs');
139
+
140
+ let entries: string[];
141
+ try {
142
+ entries = await fs.readdir(testBugsDir);
143
+ } catch {
144
+ return [];
145
+ }
146
+
147
+ const jsonFiles = entries.filter((e) => e.endsWith('.json'));
148
+ const unresolvedFindings: Finding[] = [];
149
+
150
+ for (const filename of jsonFiles) {
151
+ const filePath = path.join(testBugsDir, filename);
152
+ let finding: Finding;
153
+ try {
154
+ const raw = await fs.readFile(filePath, 'utf-8');
155
+ finding = JSON.parse(raw) as Finding;
156
+ } catch {
157
+ continue;
158
+ }
159
+
160
+ const record = this.getOrCreateAttemptRecord(finding.id);
161
+
162
+ if (record.attempts >= MAX_ATTEMPTS) {
163
+ unresolvedFindings.push({
164
+ ...finding,
165
+ suggestedFix:
166
+ `[Healer] Escalated after ${MAX_ATTEMPTS} attempts — requires manual investigation.`,
167
+ });
168
+ continue;
169
+ }
170
+
171
+ record.attempts += 1;
172
+
173
+ const strategy = classifyFixStrategy(finding);
174
+ const suggestion = generateFixSuggestion(finding, strategy);
175
+
176
+ // Run quality gate on any embedded test code in suggestedFix (if present)
177
+ const qualityViolations = validateCodeQuality(suggestion, `healer-fix-${finding.id}.ts`);
178
+ const hasErrors = qualityViolations.some((v) => v.severity === 'error');
179
+
180
+ if (hasErrors) {
181
+ // The fix suggestion itself has quality errors — escalate
182
+ unresolvedFindings.push({
183
+ ...finding,
184
+ suggestedFix:
185
+ `[Healer] Generated fix failed quality gate (attempt ${record.attempts}/${MAX_ATTEMPTS}): ` +
186
+ qualityViolations.map((v) => v.message).join('; '),
187
+ });
188
+ continue;
189
+ }
190
+
191
+ const resolvedFinding: Finding = {
192
+ ...finding,
193
+ suggestedFix: suggestion,
194
+ };
195
+
196
+ this.addEvidence({
197
+ type: 'log',
198
+ path: filePath,
199
+ description: `Healer processed test-bug "${finding.id}" with strategy "${strategy}" (attempt ${record.attempts})`,
200
+ });
201
+
202
+ // Write updated finding back to disk
203
+ await fs.writeFile(filePath, JSON.stringify(resolvedFinding, null, 2), 'utf-8');
204
+ }
205
+
206
+ return unresolvedFindings;
207
+ }
208
+
209
+ private getOrCreateAttemptRecord(findingId: string): AttemptRecord {
210
+ const existing = this.attemptMap.get(findingId);
211
+ if (existing) return existing;
212
+ const record: AttemptRecord = { findingId, attempts: 0 };
213
+ this.attemptMap.set(findingId, record);
214
+ return record;
215
+ }
216
+ }
217
+
218
+ // ---------------------------------------------------------------------------
219
+ // Internal helpers
220
+ // ---------------------------------------------------------------------------
221
+
222
+ interface NodeError extends Error {
223
+ code: string;
224
+ }
225
+
226
+ function isNodeError(err: unknown): err is NodeError {
227
+ return err instanceof Error && 'code' in err;
228
+ }
@@ -0,0 +1,266 @@
1
+ import * as fs from 'fs/promises';
2
+ import * as path from 'path';
3
+ import { BaseAgent } from './base-agent';
4
+ import { readAgentResult } from '../core/evidence';
5
+ import type { AgentResult, Finding, FindingSeverity, Phase } from '../core/types';
6
+
7
+ /** Agent IDs whose results are pulled into the consolidated report. */
8
+ const KNOWN_AGENT_IDS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23] as const;
9
+
10
+ /** Release-gate recommendation. */
11
+ export type ReleaseRecommendation = 'GO' | 'CONDITIONAL GO' | 'NO-GO';
12
+
13
+ /** Phase-level aggregation used in the report. */
14
+ interface PhaseSummary {
15
+ phase: Phase;
16
+ passCount: number;
17
+ failCount: number;
18
+ findings: Finding[];
19
+ }
20
+
21
+ /**
22
+ * Determines the release recommendation based on the severity of all findings
23
+ * across every agent result.
24
+ *
25
+ * - NO-GO: any critical finding
26
+ * - CONDITIONAL GO: 0 critical findings, at least one high finding
27
+ * - GO: 0 critical and 0 high findings
28
+ */
29
+ export function determineRecommendation(results: AgentResult[]): ReleaseRecommendation {
30
+ const allFindings = results.flatMap((r) => r.findings);
31
+ const hasCritical = allFindings.some((f) => f.severity === 'critical');
32
+ const hasHigh = allFindings.some((f) => f.severity === 'high');
33
+
34
+ if (hasCritical) return 'NO-GO';
35
+ if (hasHigh) return 'CONDITIONAL GO';
36
+ return 'GO';
37
+ }
38
+
39
+ /**
40
+ * Aggregates agent results by phase.
41
+ */
42
+ function aggregateByPhase(results: AgentResult[]): Map<Phase, PhaseSummary> {
43
+ const map = new Map<Phase, PhaseSummary>();
44
+
45
+ for (const result of results) {
46
+ const phase = result.phase;
47
+ const existing = map.get(phase) ?? {
48
+ phase,
49
+ passCount: 0,
50
+ failCount: 0,
51
+ findings: [],
52
+ };
53
+
54
+ if (result.status === 'passed') {
55
+ existing.passCount += 1;
56
+ } else {
57
+ existing.failCount += 1;
58
+ }
59
+ existing.findings.push(...result.findings);
60
+ map.set(phase, existing);
61
+ }
62
+
63
+ return map;
64
+ }
65
+
66
+ function severityEmoji(severity: FindingSeverity): string {
67
+ switch (severity) {
68
+ case 'critical': return 'CRITICAL';
69
+ case 'high': return 'HIGH';
70
+ case 'medium': return 'MEDIUM';
71
+ case 'low': return 'LOW';
72
+ case 'info': return 'INFO';
73
+ }
74
+ }
75
+
76
+ function formatFindingLine(f: Finding): string {
77
+ const loc = f.file ? ` (${f.file}${f.line ? `:${f.line}` : ''})` : '';
78
+ return `- [${severityEmoji(f.severity)}] Agent ${f.agentId} | ${f.module}${loc}: ${f.description}`;
79
+ }
80
+
81
+ /**
82
+ * Extracts a summary string from the agent result with the given ID, or returns
83
+ * a placeholder when that result is not present in the collection.
84
+ */
85
+ function agentSummaryLine(results: AgentResult[], agentId: number, label: string): string {
86
+ const result = results.find((r) => r.agentId === agentId);
87
+ if (!result) return `${label}: not run`;
88
+ return `${label}: ${result.summary}`;
89
+ }
90
+
91
+ /**
92
+ * Builds the full markdown report string from the collected agent results.
93
+ */
94
+ export function buildMarkdownReport(
95
+ results: AgentResult[],
96
+ runId: string,
97
+ recommendation: ReleaseRecommendation,
98
+ ): string {
99
+ const phaseMap = aggregateByPhase(results);
100
+ const phases: Phase[] = ['alpha', 'beta', 'uat', 'release'];
101
+
102
+ const lines: string[] = [];
103
+
104
+ lines.push(`# TestTeam — Release Report`);
105
+ lines.push(`**Run ID:** ${runId}`);
106
+ lines.push(`**Generated:** ${new Date().toISOString()}`);
107
+ lines.push(`**Recommendation:** ${recommendation}`);
108
+ lines.push('');
109
+
110
+ // Phase summaries
111
+ lines.push('## Phase Summaries');
112
+ for (const phase of phases) {
113
+ const summary = phaseMap.get(phase);
114
+ if (!summary) {
115
+ lines.push(`### ${phase.toUpperCase()}`);
116
+ lines.push('No agents ran in this phase.');
117
+ lines.push('');
118
+ continue;
119
+ }
120
+ lines.push(`### ${phase.toUpperCase()}`);
121
+ lines.push(`- Pass: ${summary.passCount}`);
122
+ lines.push(`- Fail: ${summary.failCount}`);
123
+ lines.push(`- Total findings: ${summary.findings.length}`);
124
+ lines.push('');
125
+ }
126
+
127
+ // Specialist summaries
128
+ lines.push('## Specialist Summaries');
129
+ lines.push(agentSummaryLine(results, 4, 'Coverage (Agent 4)'));
130
+ lines.push(agentSummaryLine(results, 7, 'Security (Agent 7)'));
131
+ lines.push(agentSummaryLine(results, 8, 'Accessibility (Agent 8)'));
132
+ lines.push(agentSummaryLine(results, 13, 'Performance (Agent 13)'));
133
+ lines.push(agentSummaryLine(results, 14, 'Data Integrity (Agent 14)'));
134
+ lines.push('');
135
+
136
+ // All findings by severity
137
+ const allFindings = results.flatMap((r) => r.findings);
138
+ const severities: FindingSeverity[] = ['critical', 'high', 'medium', 'low'];
139
+
140
+ lines.push('## Findings by Severity');
141
+ for (const sev of severities) {
142
+ const group = allFindings.filter((f) => f.severity === sev);
143
+ if (group.length === 0) continue;
144
+ lines.push(`### ${sev.toUpperCase()} (${group.length})`);
145
+ for (const f of group) {
146
+ lines.push(formatFindingLine(f));
147
+ }
148
+ lines.push('');
149
+ }
150
+
151
+ if (allFindings.length === 0) {
152
+ lines.push('No findings — all agents passed.');
153
+ lines.push('');
154
+ }
155
+
156
+ lines.push(`---`);
157
+ lines.push(`_Generated by TestTeam Reporter (Agent 10)_`);
158
+
159
+ return lines.join('\n');
160
+ }
161
+
162
+ export class ReporterAgent extends BaseAgent {
163
+ readonly agentId = 10;
164
+ readonly agentName = 'Reporter';
165
+
166
+ /** When true, execute() returns a single informational finding instead of a full report. */
167
+ private noPriorResults = false;
168
+
169
+ protected async preFlight(): Promise<void> {
170
+ const agentResultsDir = path.join(this.runDir, 'agent-results');
171
+ let entries: string[];
172
+ try {
173
+ entries = await fs.readdir(agentResultsDir);
174
+ } catch (err: unknown) {
175
+ if (isNodeError(err) && err.code === 'ENOENT') {
176
+ // Directory missing — gracefully degrade instead of throwing
177
+ this.noPriorResults = true;
178
+ return;
179
+ }
180
+ throw err;
181
+ }
182
+
183
+ const jsonFiles = entries.filter((e) => e.endsWith('.json'));
184
+ if (jsonFiles.length === 0) {
185
+ // Empty directory — gracefully degrade instead of throwing
186
+ this.noPriorResults = true;
187
+ }
188
+ }
189
+
190
+ protected async execute(): Promise<Finding[]> {
191
+ if (this.noPriorResults) {
192
+ return [{
193
+ id: `${this.agentId}-no-results`,
194
+ type: 'infra-issue',
195
+ severity: 'low',
196
+ agentId: this.agentId,
197
+ module: 'reporter',
198
+ description: 'No agent results to report — agent-results directory is empty or missing',
199
+ }];
200
+ }
201
+
202
+ // Read all available agent results
203
+ const results: AgentResult[] = [];
204
+ for (const agentId of KNOWN_AGENT_IDS) {
205
+ const result = await readAgentResult(this.runDir, agentId);
206
+ if (result) {
207
+ results.push(result);
208
+ }
209
+ }
210
+
211
+ // Also read any extra result files not in the known-IDs list
212
+ const agentResultsDir = path.join(this.runDir, 'agent-results');
213
+ try {
214
+ const entries = await fs.readdir(agentResultsDir);
215
+ for (const entry of entries) {
216
+ if (!entry.endsWith('.json')) continue;
217
+ const idStr = entry.replace('.json', '');
218
+ const id = parseInt(idStr, 10);
219
+ if (isNaN(id) || KNOWN_AGENT_IDS.includes(id as typeof KNOWN_AGENT_IDS[number])) continue;
220
+ const raw = await fs.readFile(path.join(agentResultsDir, entry), 'utf-8');
221
+ results.push(JSON.parse(raw) as AgentResult);
222
+ }
223
+ } catch {
224
+ // Ignore read errors for extra files
225
+ }
226
+
227
+ const recommendation = determineRecommendation(results);
228
+
229
+ // Use run directory name as run ID
230
+ const runId = path.basename(this.runDir);
231
+ const date = new Date().toISOString().slice(0, 10);
232
+ const reportFilename = `${date}-${runId}.md`;
233
+
234
+ const reportsDir = path.join(
235
+ path.dirname(path.dirname(this.runDir)), // go up from .run/<runId> to project root
236
+ 'testteam',
237
+ 'reports',
238
+ );
239
+ await fs.mkdir(reportsDir, { recursive: true });
240
+
241
+ const reportPath = path.join(reportsDir, reportFilename);
242
+ const markdownContent = buildMarkdownReport(results, runId, recommendation);
243
+ await fs.writeFile(reportPath, markdownContent, 'utf-8');
244
+
245
+ this.addEvidence({
246
+ type: 'report',
247
+ path: reportPath,
248
+ description: `Consolidated release report — recommendation: ${recommendation}`,
249
+ });
250
+
251
+ // Reporter does not generate new findings — it only aggregates
252
+ return [];
253
+ }
254
+ }
255
+
256
+ // ---------------------------------------------------------------------------
257
+ // Internal helpers
258
+ // ---------------------------------------------------------------------------
259
+
260
+ interface NodeError extends Error {
261
+ code: string;
262
+ }
263
+
264
+ function isNodeError(err: unknown): err is NodeError {
265
+ return err instanceof Error && 'code' in err;
266
+ }