@resolveio/server-lib 22.3.219 → 22.3.221

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 (745) hide show
  1. package/.nodemon.json +5 -0
  2. package/.vscode/settings.json +21 -0
  3. package/AGENTS.md +195 -0
  4. package/README.md +22 -0
  5. package/build_package.sh +5 -0
  6. package/compileDTS.pl +64 -0
  7. package/docs/ai-assistant-nightly-eval.md +65 -0
  8. package/docs/ai-assistant-preflight-checklist.md +23 -0
  9. package/docs/ai-assistant-report-builder-bridge-playbook.md +115 -0
  10. package/eslint-plugin-custom/index.js +7 -0
  11. package/eslint-plugin-custom/rules/no-filter-zero-index.js +44 -0
  12. package/eslint.config.js +103 -0
  13. package/gulpfile.js +216 -0
  14. package/methodAndPublicationListGenerator.py +375 -0
  15. package/mongodbensurers.js +2 -0
  16. package/mongostop.js +3 -0
  17. package/package.json +1 -1
  18. package/scripts/cleanup-bypassed-callmethod-logs.js +616 -0
  19. package/settings.development.json +25 -0
  20. package/settings.development.redacted.json +25 -0
  21. package/src/.env +12 -0
  22. package/src/ai/assistant-core-heuristics.ts +379 -0
  23. package/src/ai/resolveio-platform-intelligence-memory-corpus.ts +185 -0
  24. package/src/ai/resolveio-platform-intelligence-memory.ts +325 -0
  25. package/{ai/resolveio-platform-intelligence-types.d.ts → src/ai/resolveio-platform-intelligence-types.ts} +20 -15
  26. package/src/ai/resolveio-platform-intelligence.ts +462 -0
  27. package/src/client-server-app.ts +12 -0
  28. package/src/collections/ai-run.collection.ts +117 -0
  29. package/src/collections/ai-terminal-conversation.collection.ts +91 -0
  30. package/src/collections/ai-terminal-issue-report.collection.ts +99 -0
  31. package/src/collections/ai-terminal-message.collection.ts +77 -0
  32. package/src/collections/app-setting.collection.ts +104 -0
  33. package/src/collections/app-status.collection.ts +58 -0
  34. package/src/collections/communication-metric.collection.ts +84 -0
  35. package/src/collections/counter.collection.ts +56 -0
  36. package/src/collections/cron-job-history.collection.ts +94 -0
  37. package/src/collections/cron-job.collection.ts +92 -0
  38. package/src/collections/customer-notification.collection.ts +131 -0
  39. package/src/collections/customer-portal-password.collection.ts +76 -0
  40. package/src/collections/email-history.collection.ts +134 -0
  41. package/src/collections/email-verified.collection.ts +62 -0
  42. package/src/collections/file.collection.ts +74 -0
  43. package/src/collections/flag-update.collection.ts +57 -0
  44. package/src/collections/flag.collection.ts +57 -0
  45. package/src/collections/log-method-latency.collection.ts +77 -0
  46. package/src/collections/log-subscription.collection.ts +80 -0
  47. package/src/collections/log.collection.ts +93 -0
  48. package/src/collections/logged-in-users.collection.ts +67 -0
  49. package/src/collections/monitor-cpu.collection.ts +65 -0
  50. package/src/collections/monitor-function.collection.ts +74 -0
  51. package/src/collections/monitor-memory.collection.ts +77 -0
  52. package/src/collections/monitor-mongo.collection.ts +71 -0
  53. package/src/collections/notification.collection.ts +57 -0
  54. package/src/collections/openai-usage-ledger.collection.ts +131 -0
  55. package/src/collections/report-builder-dashboard-builder.collection.ts +109 -0
  56. package/src/collections/report-builder-library.collection.ts +89 -0
  57. package/src/collections/report-builder-report.collection.ts +184 -0
  58. package/src/collections/user-group.collection.ts +89 -0
  59. package/src/collections/user-guide.collection.ts +57 -0
  60. package/src/collections/user.collection.ts +181 -0
  61. package/src/cron/cron.ts +117 -0
  62. package/src/fixtures/cron-jobs.ts +95 -0
  63. package/src/fixtures/init.ts +35 -0
  64. package/src/http/auth.ts +818 -0
  65. package/src/http/health.ts +7 -0
  66. package/src/http/home.ts +90 -0
  67. package/src/http/slow-query-publication.ts +49 -0
  68. package/src/index.ts +1 -0
  69. package/src/managers/ai-assistant-codex-manager.manager.ts +1131 -0
  70. package/src/managers/ai-run-evidence.manager.ts +264 -0
  71. package/src/managers/communication-metric.manager.ts +82 -0
  72. package/src/managers/cron.manager.ts +333 -0
  73. package/src/managers/customer-notification-content.manager.ts +236 -0
  74. package/src/managers/diagnostic-manager-bootstrap.ts +165 -0
  75. package/src/managers/error-auto-fix.manager.ts +2767 -0
  76. package/src/managers/local-log.manager.ts +113 -0
  77. package/src/managers/method.manager.ts +1857 -0
  78. package/src/managers/mongo.manager.ts +4575 -0
  79. package/src/managers/monitor.manager.ts +507 -0
  80. package/src/managers/openai-usage-ledger.manager.ts +112 -0
  81. package/src/managers/slow-query-verifier.manager.ts +3590 -0
  82. package/src/managers/slow-query.manager.ts +519 -0
  83. package/src/managers/subscription.manager.ts +3128 -0
  84. package/src/managers/websocket.manager.ts +746 -0
  85. package/src/managers/worker-dispatcher.manager.ts +1360 -0
  86. package/src/managers/worker-server.manager.ts +536 -0
  87. package/src/methods/accounts.ts +532 -0
  88. package/src/methods/ai-terminal.ts +29070 -0
  89. package/src/methods/app-settings.ts +114 -0
  90. package/src/methods/aws.ts +649 -0
  91. package/src/methods/collections.ts +641 -0
  92. package/src/methods/counters.ts +69 -0
  93. package/src/methods/cron-jobs.ts +2614 -0
  94. package/src/methods/customer-notifications.ts +458 -0
  95. package/src/methods/diagnostics.ts +616 -0
  96. package/src/methods/flag-updates.ts +7 -0
  97. package/src/methods/flags.ts +7 -0
  98. package/src/methods/logs.ts +657 -0
  99. package/src/methods/mongo-explorer.ts +1880 -0
  100. package/src/methods/monitor.ts +540 -0
  101. package/src/methods/pdf.ts +1236 -0
  102. package/src/methods/publications.ts +129 -0
  103. package/src/methods/report-builder.ts +3300 -0
  104. package/src/methods/support.ts +335 -0
  105. package/src/models/ai-run.model.ts +27 -0
  106. package/src/models/ai-terminal-conversation.model.ts +19 -0
  107. package/src/models/ai-terminal-issue-report.model.ts +21 -0
  108. package/src/models/ai-terminal-message.model.ts +24 -0
  109. package/src/models/app-setting.model.ts +17 -0
  110. package/{models/app-status.model.d.ts → src/models/app-status.model.ts} +3 -2
  111. package/{models/billing-logged-in-users.model.d.ts → src/models/billing-logged-in-users.model.ts} +5 -4
  112. package/src/models/collection-document.model.ts +24 -0
  113. package/src/models/communication-metric.model.ts +23 -0
  114. package/{models/counter.model.d.ts → src/models/counter.model.ts} +4 -3
  115. package/src/models/cron-job-history.model.ts +16 -0
  116. package/src/models/cron-job.model.ts +15 -0
  117. package/src/models/customer-notification.model.ts +28 -0
  118. package/src/models/customer-portal-password.model.ts +12 -0
  119. package/src/models/dialog.model.ts +25 -0
  120. package/{models/email-history.model.js → src/models/email-history.model.ts} +36 -4
  121. package/{models/email-verified.model.d.ts → src/models/email-verified.model.ts} +6 -5
  122. package/{models/file.model.d.ts → src/models/file.model.ts} +8 -7
  123. package/{models/flag-update.model.d.ts → src/models/flag-update.model.ts} +4 -3
  124. package/{models/flag.model.d.ts → src/models/flag.model.ts} +4 -3
  125. package/src/models/log-method-latency.model.ts +11 -0
  126. package/{models/log-subscription.model.d.ts → src/models/log-subscription.model.ts} +11 -9
  127. package/src/models/log.model.ts +19 -0
  128. package/{models/logged-in-users.model.d.ts → src/models/logged-in-users.model.ts} +6 -5
  129. package/{models/method-response.model.d.ts → src/models/method-response.model.ts} +7 -6
  130. package/src/models/method.model.ts +25 -0
  131. package/{models/monitor-cpu.model.d.ts → src/models/monitor-cpu.model.ts} +9 -7
  132. package/src/models/monitor-function.model.ts +16 -0
  133. package/src/models/monitor-memory.model.ts +17 -0
  134. package/src/models/monitor-mongo.model.ts +15 -0
  135. package/{models/notification.model.d.ts → src/models/notification.model.ts} +6 -4
  136. package/src/models/openai-usage-ledger.model.ts +56 -0
  137. package/src/models/pagination.model.ts +35 -0
  138. package/src/models/permission.model.ts +14 -0
  139. package/src/models/report-builder-dashboard-builder.model.ts +29 -0
  140. package/src/models/report-builder-library.model.ts +20 -0
  141. package/src/models/report-builder-report.model.ts +136 -0
  142. package/src/models/report-builder.model.ts +68 -0
  143. package/src/models/select-data-label.model.ts +9 -0
  144. package/src/models/server-message.model.ts +31 -0
  145. package/src/models/slow-query-report.model.ts +23 -0
  146. package/src/models/subscription.model.ts +73 -0
  147. package/src/models/support-ticket.model.ts +104 -0
  148. package/src/models/user-group.model.ts +24 -0
  149. package/{models/user-guide.model.d.ts → src/models/user-guide.model.ts} +5 -4
  150. package/src/models/user.model.ts +96 -0
  151. package/src/private/images/ResolveIO.png +0 -0
  152. package/src/publications/ai-terminal.ts +73 -0
  153. package/src/publications/app-settings.ts +25 -0
  154. package/src/publications/app-status.ts +13 -0
  155. package/src/publications/cron-jobs.ts +40 -0
  156. package/src/publications/customer-notifications.ts +101 -0
  157. package/src/publications/files.ts +33 -0
  158. package/src/publications/flags-update.ts +19 -0
  159. package/src/publications/flags.ts +19 -0
  160. package/src/publications/logs.ts +163 -0
  161. package/src/publications/notifications.ts +13 -0
  162. package/src/publications/report-builder-dashboard-builders.ts +39 -0
  163. package/src/publications/report-builder-libraries.ts +41 -0
  164. package/src/publications/report-builder-reports.ts +47 -0
  165. package/src/publications/super-admin.ts +13 -0
  166. package/src/publications/user-groups.ts +12 -0
  167. package/src/publications/user-guides.ts +12 -0
  168. package/src/resolveio-server-app.ts +617 -0
  169. package/src/server-app.ts +3354 -0
  170. package/src/services/codex-client.ts +1231 -0
  171. package/src/services/openai-client.ts +265 -0
  172. package/src/types/error-report.ts +26 -0
  173. package/src/types/js-tiktoken.d.ts +11 -0
  174. package/src/types/slow-query-report.ts +28 -0
  175. package/src/util/ai-qa-policy.ts +925 -0
  176. package/src/util/ai-run-evidence-adapters.ts +8347 -0
  177. package/src/util/ai-run-evidence-dashboard.ts +323 -0
  178. package/src/util/ai-run-evidence-eval.ts +1057 -0
  179. package/src/util/ai-run-evidence.ts +1430 -0
  180. package/src/util/ai-runner-artifacts.ts +586 -0
  181. package/src/util/ai-runner-manager-autopilot.ts +961 -0
  182. package/src/util/ai-runner-manager-policy.ts +5011 -0
  183. package/src/util/ai-runner-qa-auth.ts +838 -0
  184. package/src/util/ai-runner-qa-tools.ts +3536 -0
  185. package/src/util/aicoder-runner-v6.ts +3121 -0
  186. package/src/util/common.ts +649 -0
  187. package/src/util/customer-portal-password.ts +183 -0
  188. package/src/util/error-reporter.ts +332 -0
  189. package/src/util/error-tracking.ts +79 -0
  190. package/src/util/openai-usage-cost.ts +114 -0
  191. package/src/util/report-builder-unwinds.ts +180 -0
  192. package/src/util/runner-process-janitor.ts +219 -0
  193. package/src/util/schema-report-builder.ts +448 -0
  194. package/src/util/slow-query-reporter.ts +216 -0
  195. package/src/util/subscription-dependency-context.ts +1096 -0
  196. package/src/util/support-runner-v5.ts +10040 -0
  197. package/src/util/tokenizer.ts +38 -0
  198. package/src/workers/codex-runner.worker.ts +142 -0
  199. package/start_server.sh +5 -0
  200. package/tests/ai-assistant-corpus-build.ts +484 -0
  201. package/tests/ai-assistant-corpus-replay-e2e.ts +774 -0
  202. package/tests/ai-assistant-data-parity-e2e.ts +1989 -0
  203. package/tests/ai-assistant-eval-triage.ts +831 -0
  204. package/tests/ai-assistant-openai-e2e.ts +1061 -0
  205. package/tests/ai-assistant-openai-git-e2e.ts +155 -0
  206. package/tests/ai-assistant-preflight-matrix.ts +215 -0
  207. package/tests/ai-assistant-routing-eval.test.ts +585 -0
  208. package/tests/ai-assistant-snf-live-eval.ts +975 -0
  209. package/tests/ai-assistant-utils.test.ts +4834 -0
  210. package/tests/ai-manager-autopilot-snapshot.test.ts +193 -0
  211. package/tests/ai-manager-recovery-checkpoint.test.ts +1383 -0
  212. package/tests/ai-run-eval.test.ts +132 -0
  213. package/tests/ai-run-evidence.test.ts +3773 -0
  214. package/tests/ai-runner-contract.test.ts +515 -0
  215. package/tests/aicoder-runner-v6.test.ts +822 -0
  216. package/tests/error-reporter.test.ts +145 -0
  217. package/tests/method-publication-generator.test.ts +46 -0
  218. package/tests/report-builder-linking.test.ts +79 -0
  219. package/tests/resolveio-platform-intelligence.test.ts +352 -0
  220. package/tests/server-app-cron-owner.test.ts +127 -0
  221. package/tests/subscription-connect-race.test.ts +158 -0
  222. package/tests/subscription-dependency-context.test.ts +324 -0
  223. package/tests/subscription-manager-collection-tracking.test.ts +86 -0
  224. package/tests/subscription-manager-invalidation.test.ts +86 -0
  225. package/tests/support-runner-v5.test.ts +3201 -0
  226. package/tsconfig.json +34 -0
  227. package/ai/assistant-core-heuristics.d.ts +0 -11
  228. package/ai/assistant-core-heuristics.js +0 -356
  229. package/ai/assistant-core-heuristics.js.map +0 -1
  230. package/ai/resolveio-platform-intelligence-memory-corpus.d.ts +0 -3
  231. package/ai/resolveio-platform-intelligence-memory-corpus.js +0 -214
  232. package/ai/resolveio-platform-intelligence-memory-corpus.js.map +0 -1
  233. package/ai/resolveio-platform-intelligence-memory.d.ts +0 -20
  234. package/ai/resolveio-platform-intelligence-memory.js +0 -341
  235. package/ai/resolveio-platform-intelligence-memory.js.map +0 -1
  236. package/ai/resolveio-platform-intelligence-types.js +0 -4
  237. package/ai/resolveio-platform-intelligence-types.js.map +0 -1
  238. package/ai/resolveio-platform-intelligence.d.ts +0 -6
  239. package/ai/resolveio-platform-intelligence.js +0 -463
  240. package/ai/resolveio-platform-intelligence.js.map +0 -1
  241. package/client-server-app.d.ts +0 -1
  242. package/client-server-app.js +0 -68
  243. package/client-server-app.js.map +0 -1
  244. package/collections/ai-run.collection.d.ts +0 -3
  245. package/collections/ai-run.collection.js +0 -170
  246. package/collections/ai-run.collection.js.map +0 -1
  247. package/collections/ai-terminal-conversation.collection.d.ts +0 -2
  248. package/collections/ai-terminal-conversation.collection.js +0 -140
  249. package/collections/ai-terminal-conversation.collection.js.map +0 -1
  250. package/collections/ai-terminal-issue-report.collection.d.ts +0 -2
  251. package/collections/ai-terminal-issue-report.collection.js +0 -148
  252. package/collections/ai-terminal-issue-report.collection.js.map +0 -1
  253. package/collections/ai-terminal-message.collection.d.ts +0 -2
  254. package/collections/ai-terminal-message.collection.js +0 -121
  255. package/collections/ai-terminal-message.collection.js.map +0 -1
  256. package/collections/app-setting.collection.d.ts +0 -3
  257. package/collections/app-setting.collection.js +0 -103
  258. package/collections/app-setting.collection.js.map +0 -1
  259. package/collections/app-status.collection.d.ts +0 -3
  260. package/collections/app-status.collection.js +0 -57
  261. package/collections/app-status.collection.js.map +0 -1
  262. package/collections/communication-metric.collection.d.ts +0 -2
  263. package/collections/communication-metric.collection.js +0 -133
  264. package/collections/communication-metric.collection.js.map +0 -1
  265. package/collections/counter.collection.d.ts +0 -3
  266. package/collections/counter.collection.js +0 -56
  267. package/collections/counter.collection.js.map +0 -1
  268. package/collections/cron-job-history.collection.d.ts +0 -3
  269. package/collections/cron-job-history.collection.js +0 -137
  270. package/collections/cron-job-history.collection.js.map +0 -1
  271. package/collections/cron-job.collection.d.ts +0 -3
  272. package/collections/cron-job.collection.js +0 -92
  273. package/collections/cron-job.collection.js.map +0 -1
  274. package/collections/customer-notification.collection.d.ts +0 -3
  275. package/collections/customer-notification.collection.js +0 -130
  276. package/collections/customer-notification.collection.js.map +0 -1
  277. package/collections/customer-portal-password.collection.d.ts +0 -3
  278. package/collections/customer-portal-password.collection.js +0 -75
  279. package/collections/customer-portal-password.collection.js.map +0 -1
  280. package/collections/email-history.collection.d.ts +0 -3
  281. package/collections/email-history.collection.js +0 -134
  282. package/collections/email-history.collection.js.map +0 -1
  283. package/collections/email-verified.collection.d.ts +0 -3
  284. package/collections/email-verified.collection.js +0 -62
  285. package/collections/email-verified.collection.js.map +0 -1
  286. package/collections/file.collection.d.ts +0 -3
  287. package/collections/file.collection.js +0 -74
  288. package/collections/file.collection.js.map +0 -1
  289. package/collections/flag-update.collection.d.ts +0 -3
  290. package/collections/flag-update.collection.js +0 -57
  291. package/collections/flag-update.collection.js.map +0 -1
  292. package/collections/flag.collection.d.ts +0 -3
  293. package/collections/flag.collection.js +0 -57
  294. package/collections/flag.collection.js.map +0 -1
  295. package/collections/log-method-latency.collection.d.ts +0 -3
  296. package/collections/log-method-latency.collection.js +0 -77
  297. package/collections/log-method-latency.collection.js.map +0 -1
  298. package/collections/log-subscription.collection.d.ts +0 -3
  299. package/collections/log-subscription.collection.js +0 -80
  300. package/collections/log-subscription.collection.js.map +0 -1
  301. package/collections/log.collection.d.ts +0 -3
  302. package/collections/log.collection.js +0 -93
  303. package/collections/log.collection.js.map +0 -1
  304. package/collections/logged-in-users.collection.d.ts +0 -3
  305. package/collections/logged-in-users.collection.js +0 -67
  306. package/collections/logged-in-users.collection.js.map +0 -1
  307. package/collections/monitor-cpu.collection.d.ts +0 -3
  308. package/collections/monitor-cpu.collection.js +0 -65
  309. package/collections/monitor-cpu.collection.js.map +0 -1
  310. package/collections/monitor-function.collection.d.ts +0 -3
  311. package/collections/monitor-function.collection.js +0 -74
  312. package/collections/monitor-function.collection.js.map +0 -1
  313. package/collections/monitor-memory.collection.d.ts +0 -3
  314. package/collections/monitor-memory.collection.js +0 -77
  315. package/collections/monitor-memory.collection.js.map +0 -1
  316. package/collections/monitor-mongo.collection.d.ts +0 -3
  317. package/collections/monitor-mongo.collection.js +0 -71
  318. package/collections/monitor-mongo.collection.js.map +0 -1
  319. package/collections/notification.collection.d.ts +0 -3
  320. package/collections/notification.collection.js +0 -57
  321. package/collections/notification.collection.js.map +0 -1
  322. package/collections/openai-usage-ledger.collection.d.ts +0 -2
  323. package/collections/openai-usage-ledger.collection.js +0 -188
  324. package/collections/openai-usage-ledger.collection.js.map +0 -1
  325. package/collections/report-builder-dashboard-builder.collection.d.ts +0 -3
  326. package/collections/report-builder-dashboard-builder.collection.js +0 -109
  327. package/collections/report-builder-dashboard-builder.collection.js.map +0 -1
  328. package/collections/report-builder-library.collection.d.ts +0 -3
  329. package/collections/report-builder-library.collection.js +0 -87
  330. package/collections/report-builder-library.collection.js.map +0 -1
  331. package/collections/report-builder-report.collection.d.ts +0 -4
  332. package/collections/report-builder-report.collection.js +0 -184
  333. package/collections/report-builder-report.collection.js.map +0 -1
  334. package/collections/user-group.collection.d.ts +0 -4
  335. package/collections/user-group.collection.js +0 -89
  336. package/collections/user-group.collection.js.map +0 -1
  337. package/collections/user-guide.collection.d.ts +0 -3
  338. package/collections/user-guide.collection.js +0 -57
  339. package/collections/user-guide.collection.js.map +0 -1
  340. package/collections/user.collection.d.ts +0 -4
  341. package/collections/user.collection.js +0 -180
  342. package/collections/user.collection.js.map +0 -1
  343. package/cron/cron.d.ts +0 -14
  344. package/cron/cron.js +0 -216
  345. package/cron/cron.js.map +0 -1
  346. package/fixtures/cron-jobs.d.ts +0 -1
  347. package/fixtures/cron-jobs.js +0 -150
  348. package/fixtures/cron-jobs.js.map +0 -1
  349. package/fixtures/init.d.ts +0 -1
  350. package/fixtures/init.js +0 -91
  351. package/fixtures/init.js.map +0 -1
  352. package/http/auth.d.ts +0 -2
  353. package/http/auth.js +0 -951
  354. package/http/auth.js.map +0 -1
  355. package/http/health.d.ts +0 -1
  356. package/http/health.js +0 -11
  357. package/http/health.js.map +0 -1
  358. package/http/home.d.ts +0 -1
  359. package/http/home.js +0 -134
  360. package/http/home.js.map +0 -1
  361. package/http/slow-query-publication.d.ts +0 -2
  362. package/http/slow-query-publication.js +0 -99
  363. package/http/slow-query-publication.js.map +0 -1
  364. package/index.d.ts +0 -1
  365. package/index.js +0 -19
  366. package/index.js.map +0 -1
  367. package/managers/ai-assistant-codex-manager.manager.d.ts +0 -67
  368. package/managers/ai-assistant-codex-manager.manager.js +0 -1113
  369. package/managers/ai-assistant-codex-manager.manager.js.map +0 -1
  370. package/managers/ai-run-evidence.manager.d.ts +0 -36
  371. package/managers/ai-run-evidence.manager.js +0 -377
  372. package/managers/ai-run-evidence.manager.js.map +0 -1
  373. package/managers/communication-metric.manager.d.ts +0 -16
  374. package/managers/communication-metric.manager.js +0 -134
  375. package/managers/communication-metric.manager.js.map +0 -1
  376. package/managers/cron.manager.d.ts +0 -20
  377. package/managers/cron.manager.js +0 -534
  378. package/managers/cron.manager.js.map +0 -1
  379. package/managers/customer-notification-content.manager.d.ts +0 -55
  380. package/managers/customer-notification-content.manager.js +0 -158
  381. package/managers/customer-notification-content.manager.js.map +0 -1
  382. package/managers/diagnostic-manager-bootstrap.d.ts +0 -9
  383. package/managers/diagnostic-manager-bootstrap.js +0 -260
  384. package/managers/diagnostic-manager-bootstrap.js.map +0 -1
  385. package/managers/error-auto-fix.manager.d.ts +0 -149
  386. package/managers/error-auto-fix.manager.js +0 -3064
  387. package/managers/error-auto-fix.manager.js.map +0 -1
  388. package/managers/local-log.manager.d.ts +0 -18
  389. package/managers/local-log.manager.js +0 -88
  390. package/managers/local-log.manager.js.map +0 -1
  391. package/managers/method.manager.d.ts +0 -84
  392. package/managers/method.manager.js +0 -1964
  393. package/managers/method.manager.js.map +0 -1
  394. package/managers/mongo.manager.d.ts +0 -224
  395. package/managers/mongo.manager.js +0 -5000
  396. package/managers/mongo.manager.js.map +0 -1
  397. package/managers/monitor.manager.d.ts +0 -70
  398. package/managers/monitor.manager.js +0 -550
  399. package/managers/monitor.manager.js.map +0 -1
  400. package/managers/openai-usage-ledger.manager.d.ts +0 -30
  401. package/managers/openai-usage-ledger.manager.js +0 -142
  402. package/managers/openai-usage-ledger.manager.js.map +0 -1
  403. package/managers/slow-query-verifier.manager.d.ts +0 -144
  404. package/managers/slow-query-verifier.manager.js +0 -3857
  405. package/managers/slow-query-verifier.manager.js.map +0 -1
  406. package/managers/slow-query.manager.d.ts +0 -28
  407. package/managers/slow-query.manager.js +0 -468
  408. package/managers/slow-query.manager.js.map +0 -1
  409. package/managers/subscription.manager.d.ts +0 -169
  410. package/managers/subscription.manager.js +0 -3434
  411. package/managers/subscription.manager.js.map +0 -1
  412. package/managers/websocket.manager.d.ts +0 -73
  413. package/managers/websocket.manager.js +0 -673
  414. package/managers/websocket.manager.js.map +0 -1
  415. package/managers/worker-dispatcher.manager.d.ts +0 -120
  416. package/managers/worker-dispatcher.manager.js +0 -1266
  417. package/managers/worker-dispatcher.manager.js.map +0 -1
  418. package/managers/worker-server.manager.d.ts +0 -35
  419. package/managers/worker-server.manager.js +0 -582
  420. package/managers/worker-server.manager.js.map +0 -1
  421. package/methods/accounts.d.ts +0 -2
  422. package/methods/accounts.js +0 -624
  423. package/methods/accounts.js.map +0 -1
  424. package/methods/ai-terminal.d.ts +0 -458
  425. package/methods/ai-terminal.js +0 -27991
  426. package/methods/ai-terminal.js.map +0 -1
  427. package/methods/app-settings.d.ts +0 -2
  428. package/methods/app-settings.js +0 -169
  429. package/methods/app-settings.js.map +0 -1
  430. package/methods/aws.d.ts +0 -2
  431. package/methods/aws.js +0 -877
  432. package/methods/aws.js.map +0 -1
  433. package/methods/collections.d.ts +0 -2
  434. package/methods/collections.js +0 -719
  435. package/methods/collections.js.map +0 -1
  436. package/methods/counters.d.ts +0 -2
  437. package/methods/counters.js +0 -113
  438. package/methods/counters.js.map +0 -1
  439. package/methods/cron-jobs.d.ts +0 -2
  440. package/methods/cron-jobs.js +0 -2475
  441. package/methods/cron-jobs.js.map +0 -1
  442. package/methods/customer-notifications.d.ts +0 -2
  443. package/methods/customer-notifications.js +0 -528
  444. package/methods/customer-notifications.js.map +0 -1
  445. package/methods/diagnostics.d.ts +0 -2
  446. package/methods/diagnostics.js +0 -703
  447. package/methods/diagnostics.js.map +0 -1
  448. package/methods/flag-updates.d.ts +0 -2
  449. package/methods/flag-updates.js +0 -8
  450. package/methods/flag-updates.js.map +0 -1
  451. package/methods/flags.d.ts +0 -2
  452. package/methods/flags.js +0 -8
  453. package/methods/flags.js.map +0 -1
  454. package/methods/logs.d.ts +0 -2
  455. package/methods/logs.js +0 -751
  456. package/methods/logs.js.map +0 -1
  457. package/methods/mongo-explorer.d.ts +0 -2
  458. package/methods/mongo-explorer.js +0 -1808
  459. package/methods/mongo-explorer.js.map +0 -1
  460. package/methods/monitor.d.ts +0 -2
  461. package/methods/monitor.js +0 -543
  462. package/methods/monitor.js.map +0 -1
  463. package/methods/pdf.d.ts +0 -2
  464. package/methods/pdf.js +0 -1216
  465. package/methods/pdf.js.map +0 -1
  466. package/methods/publications.d.ts +0 -1
  467. package/methods/publications.js +0 -183
  468. package/methods/publications.js.map +0 -1
  469. package/methods/report-builder.d.ts +0 -2
  470. package/methods/report-builder.js +0 -3094
  471. package/methods/report-builder.js.map +0 -1
  472. package/methods/support.d.ts +0 -2
  473. package/methods/support.js +0 -430
  474. package/methods/support.js.map +0 -1
  475. package/models/ai-run.model.d.ts +0 -19
  476. package/models/ai-run.model.js +0 -4
  477. package/models/ai-run.model.js.map +0 -1
  478. package/models/ai-terminal-conversation.model.d.ts +0 -17
  479. package/models/ai-terminal-conversation.model.js +0 -4
  480. package/models/ai-terminal-conversation.model.js.map +0 -1
  481. package/models/ai-terminal-issue-report.model.d.ts +0 -19
  482. package/models/ai-terminal-issue-report.model.js +0 -4
  483. package/models/ai-terminal-issue-report.model.js.map +0 -1
  484. package/models/ai-terminal-message.model.d.ts +0 -22
  485. package/models/ai-terminal-message.model.js +0 -4
  486. package/models/ai-terminal-message.model.js.map +0 -1
  487. package/models/app-setting.model.d.ts +0 -16
  488. package/models/app-setting.model.js +0 -4
  489. package/models/app-setting.model.js.map +0 -1
  490. package/models/app-status.model.js +0 -4
  491. package/models/app-status.model.js.map +0 -1
  492. package/models/billing-logged-in-users.model.js +0 -4
  493. package/models/billing-logged-in-users.model.js.map +0 -1
  494. package/models/collection-document.model.d.ts +0 -21
  495. package/models/collection-document.model.js +0 -4
  496. package/models/collection-document.model.js.map +0 -1
  497. package/models/communication-metric.model.d.ts +0 -20
  498. package/models/communication-metric.model.js +0 -4
  499. package/models/communication-metric.model.js.map +0 -1
  500. package/models/counter.model.js +0 -4
  501. package/models/counter.model.js.map +0 -1
  502. package/models/cron-job-history.model.d.ts +0 -15
  503. package/models/cron-job-history.model.js +0 -4
  504. package/models/cron-job-history.model.js.map +0 -1
  505. package/models/cron-job.model.d.ts +0 -14
  506. package/models/cron-job.model.js +0 -4
  507. package/models/cron-job.model.js.map +0 -1
  508. package/models/customer-notification.model.d.ts +0 -26
  509. package/models/customer-notification.model.js +0 -4
  510. package/models/customer-notification.model.js.map +0 -1
  511. package/models/customer-portal-password.model.d.ts +0 -11
  512. package/models/customer-portal-password.model.js +0 -4
  513. package/models/customer-portal-password.model.js.map +0 -1
  514. package/models/dialog.model.d.ts +0 -23
  515. package/models/dialog.model.js +0 -4
  516. package/models/dialog.model.js.map +0 -1
  517. package/models/email-history.model.d.ts +0 -32
  518. package/models/email-history.model.js.map +0 -1
  519. package/models/email-verified.model.js +0 -4
  520. package/models/email-verified.model.js.map +0 -1
  521. package/models/file.model.js +0 -4
  522. package/models/file.model.js.map +0 -1
  523. package/models/flag-update.model.js +0 -4
  524. package/models/flag-update.model.js.map +0 -1
  525. package/models/flag.model.js +0 -4
  526. package/models/flag.model.js.map +0 -1
  527. package/models/log-method-latency.model.d.ts +0 -10
  528. package/models/log-method-latency.model.js +0 -4
  529. package/models/log-method-latency.model.js.map +0 -1
  530. package/models/log-subscription.model.js +0 -4
  531. package/models/log-subscription.model.js.map +0 -1
  532. package/models/log.model.d.ts +0 -17
  533. package/models/log.model.js +0 -4
  534. package/models/log.model.js.map +0 -1
  535. package/models/logged-in-users.model.js +0 -4
  536. package/models/logged-in-users.model.js.map +0 -1
  537. package/models/method-response.model.js +0 -4
  538. package/models/method-response.model.js.map +0 -1
  539. package/models/method.model.d.ts +0 -26
  540. package/models/method.model.js +0 -4
  541. package/models/method.model.js.map +0 -1
  542. package/models/monitor-cpu.model.js +0 -4
  543. package/models/monitor-cpu.model.js.map +0 -1
  544. package/models/monitor-function.model.d.ts +0 -14
  545. package/models/monitor-function.model.js +0 -4
  546. package/models/monitor-function.model.js.map +0 -1
  547. package/models/monitor-memory.model.d.ts +0 -15
  548. package/models/monitor-memory.model.js +0 -4
  549. package/models/monitor-memory.model.js.map +0 -1
  550. package/models/monitor-mongo.model.d.ts +0 -13
  551. package/models/monitor-mongo.model.js +0 -4
  552. package/models/monitor-mongo.model.js.map +0 -1
  553. package/models/notification.model.js +0 -4
  554. package/models/notification.model.js.map +0 -1
  555. package/models/openai-usage-ledger.model.d.ts +0 -30
  556. package/models/openai-usage-ledger.model.js +0 -4
  557. package/models/openai-usage-ledger.model.js.map +0 -1
  558. package/models/pagination.model.d.ts +0 -11
  559. package/models/pagination.model.js +0 -28
  560. package/models/pagination.model.js.map +0 -1
  561. package/models/permission.model.d.ts +0 -12
  562. package/models/permission.model.js +0 -4
  563. package/models/permission.model.js.map +0 -1
  564. package/models/report-builder-dashboard-builder.model.d.ts +0 -25
  565. package/models/report-builder-dashboard-builder.model.js +0 -4
  566. package/models/report-builder-dashboard-builder.model.js.map +0 -1
  567. package/models/report-builder-library.model.d.ts +0 -17
  568. package/models/report-builder-library.model.js +0 -4
  569. package/models/report-builder-library.model.js.map +0 -1
  570. package/models/report-builder-report.model.d.ts +0 -121
  571. package/models/report-builder-report.model.js +0 -4
  572. package/models/report-builder-report.model.js.map +0 -1
  573. package/models/report-builder.model.d.ts +0 -61
  574. package/models/report-builder.model.js +0 -4
  575. package/models/report-builder.model.js.map +0 -1
  576. package/models/select-data-label.model.d.ts +0 -9
  577. package/models/select-data-label.model.js +0 -4
  578. package/models/select-data-label.model.js.map +0 -1
  579. package/models/server-message.model.d.ts +0 -32
  580. package/models/server-message.model.js +0 -4
  581. package/models/server-message.model.js.map +0 -1
  582. package/models/slow-query-report.model.d.ts +0 -23
  583. package/models/slow-query-report.model.js +0 -4
  584. package/models/slow-query-report.model.js.map +0 -1
  585. package/models/subscription.model.d.ts +0 -31
  586. package/models/subscription.model.js +0 -4
  587. package/models/subscription.model.js.map +0 -1
  588. package/models/support-ticket.model.d.ts +0 -87
  589. package/models/support-ticket.model.js +0 -4
  590. package/models/support-ticket.model.js.map +0 -1
  591. package/models/user-group.model.d.ts +0 -20
  592. package/models/user-group.model.js +0 -4
  593. package/models/user-group.model.js.map +0 -1
  594. package/models/user-guide.model.js +0 -4
  595. package/models/user-guide.model.js.map +0 -1
  596. package/models/user.model.d.ts +0 -84
  597. package/models/user.model.js +0 -4
  598. package/models/user.model.js.map +0 -1
  599. package/private/images/ResolveIO.png +0 -0
  600. package/public_api.js +0 -127
  601. package/public_api.js.map +0 -1
  602. package/publications/ai-terminal.d.ts +0 -1
  603. package/publications/ai-terminal.js +0 -122
  604. package/publications/ai-terminal.js.map +0 -1
  605. package/publications/app-settings.d.ts +0 -2
  606. package/publications/app-settings.js +0 -28
  607. package/publications/app-settings.js.map +0 -1
  608. package/publications/app-status.d.ts +0 -2
  609. package/publications/app-status.js +0 -16
  610. package/publications/app-status.js.map +0 -1
  611. package/publications/cron-jobs.d.ts +0 -2
  612. package/publications/cron-jobs.js +0 -88
  613. package/publications/cron-jobs.js.map +0 -1
  614. package/publications/customer-notifications.d.ts +0 -2
  615. package/publications/customer-notifications.js +0 -161
  616. package/publications/customer-notifications.js.map +0 -1
  617. package/publications/files.d.ts +0 -2
  618. package/publications/files.js +0 -36
  619. package/publications/files.js.map +0 -1
  620. package/publications/flags-update.d.ts +0 -2
  621. package/publications/flags-update.js +0 -22
  622. package/publications/flags-update.js.map +0 -1
  623. package/publications/flags.d.ts +0 -2
  624. package/publications/flags.js +0 -22
  625. package/publications/flags.js.map +0 -1
  626. package/publications/logs.d.ts +0 -2
  627. package/publications/logs.js +0 -164
  628. package/publications/logs.js.map +0 -1
  629. package/publications/notifications.d.ts +0 -2
  630. package/publications/notifications.js +0 -16
  631. package/publications/notifications.js.map +0 -1
  632. package/publications/report-builder-dashboard-builders.d.ts +0 -2
  633. package/publications/report-builder-dashboard-builders.js +0 -42
  634. package/publications/report-builder-dashboard-builders.js.map +0 -1
  635. package/publications/report-builder-libraries.d.ts +0 -2
  636. package/publications/report-builder-libraries.js +0 -90
  637. package/publications/report-builder-libraries.js.map +0 -1
  638. package/publications/report-builder-reports.d.ts +0 -2
  639. package/publications/report-builder-reports.js +0 -50
  640. package/publications/report-builder-reports.js.map +0 -1
  641. package/publications/super-admin.d.ts +0 -2
  642. package/publications/super-admin.js +0 -16
  643. package/publications/super-admin.js.map +0 -1
  644. package/publications/user-groups.d.ts +0 -1
  645. package/publications/user-groups.js +0 -16
  646. package/publications/user-groups.js.map +0 -1
  647. package/publications/user-guides.d.ts +0 -1
  648. package/publications/user-guides.js +0 -16
  649. package/publications/user-guides.js.map +0 -1
  650. package/resolveio-server-app.d.ts +0 -70
  651. package/resolveio-server-app.js +0 -801
  652. package/resolveio-server-app.js.map +0 -1
  653. package/server-app.d.ts +0 -228
  654. package/server-app.js +0 -3566
  655. package/server-app.js.map +0 -1
  656. package/services/codex-client.d.ts +0 -128
  657. package/services/codex-client.js +0 -1629
  658. package/services/codex-client.js.map +0 -1
  659. package/services/openai-client.d.ts +0 -46
  660. package/services/openai-client.js +0 -318
  661. package/services/openai-client.js.map +0 -1
  662. package/types/error-report.d.ts +0 -25
  663. package/types/error-report.js +0 -4
  664. package/types/error-report.js.map +0 -1
  665. package/types/slow-query-report.d.ts +0 -27
  666. package/types/slow-query-report.js +0 -6
  667. package/types/slow-query-report.js.map +0 -1
  668. package/util/ai-qa-policy.d.ts +0 -124
  669. package/util/ai-qa-policy.js +0 -736
  670. package/util/ai-qa-policy.js.map +0 -1
  671. package/util/ai-run-evidence-adapters.d.ts +0 -109
  672. package/util/ai-run-evidence-adapters.js +0 -7234
  673. package/util/ai-run-evidence-adapters.js.map +0 -1
  674. package/util/ai-run-evidence-dashboard.d.ts +0 -88
  675. package/util/ai-run-evidence-dashboard.js +0 -343
  676. package/util/ai-run-evidence-dashboard.js.map +0 -1
  677. package/util/ai-run-evidence-eval.d.ts +0 -86
  678. package/util/ai-run-evidence-eval.js +0 -1018
  679. package/util/ai-run-evidence-eval.js.map +0 -1
  680. package/util/ai-run-evidence.d.ts +0 -244
  681. package/util/ai-run-evidence.js +0 -1096
  682. package/util/ai-run-evidence.js.map +0 -1
  683. package/util/ai-runner-artifacts.d.ts +0 -82
  684. package/util/ai-runner-artifacts.js +0 -713
  685. package/util/ai-runner-artifacts.js.map +0 -1
  686. package/util/ai-runner-manager-autopilot.d.ts +0 -210
  687. package/util/ai-runner-manager-autopilot.js +0 -642
  688. package/util/ai-runner-manager-autopilot.js.map +0 -1
  689. package/util/ai-runner-manager-policy.d.ts +0 -807
  690. package/util/ai-runner-manager-policy.js +0 -3501
  691. package/util/ai-runner-manager-policy.js.map +0 -1
  692. package/util/ai-runner-qa-auth.d.ts +0 -5
  693. package/util/ai-runner-qa-auth.js +0 -839
  694. package/util/ai-runner-qa-auth.js.map +0 -1
  695. package/util/ai-runner-qa-tools.d.ts +0 -26
  696. package/util/ai-runner-qa-tools.js +0 -3520
  697. package/util/ai-runner-qa-tools.js.map +0 -1
  698. package/util/aicoder-runner-v6.d.ts +0 -426
  699. package/util/aicoder-runner-v6.js +0 -2464
  700. package/util/aicoder-runner-v6.js.map +0 -1
  701. package/util/common.d.ts +0 -31
  702. package/util/common.js +0 -683
  703. package/util/common.js.map +0 -1
  704. package/util/customer-portal-password.d.ts +0 -13
  705. package/util/customer-portal-password.js +0 -209
  706. package/util/customer-portal-password.js.map +0 -1
  707. package/util/error-reporter.d.ts +0 -52
  708. package/util/error-reporter.js +0 -326
  709. package/util/error-reporter.js.map +0 -1
  710. package/util/error-tracking.d.ts +0 -13
  711. package/util/error-tracking.js +0 -120
  712. package/util/error-tracking.js.map +0 -1
  713. package/util/openai-usage-cost.d.ts +0 -6
  714. package/util/openai-usage-cost.js +0 -103
  715. package/util/openai-usage-cost.js.map +0 -1
  716. package/util/report-builder-unwinds.d.ts +0 -15
  717. package/util/report-builder-unwinds.js +0 -156
  718. package/util/report-builder-unwinds.js.map +0 -1
  719. package/util/runner-process-janitor.d.ts +0 -27
  720. package/util/runner-process-janitor.js +0 -208
  721. package/util/runner-process-janitor.js.map +0 -1
  722. package/util/schema-report-builder.d.ts +0 -6
  723. package/util/schema-report-builder.js +0 -481
  724. package/util/schema-report-builder.js.map +0 -1
  725. package/util/slow-query-reporter.d.ts +0 -28
  726. package/util/slow-query-reporter.js +0 -226
  727. package/util/slow-query-reporter.js.map +0 -1
  728. package/util/subscription-dependency-context.d.ts +0 -34
  729. package/util/subscription-dependency-context.js +0 -1283
  730. package/util/subscription-dependency-context.js.map +0 -1
  731. package/util/support-runner-v5.d.ts +0 -1426
  732. package/util/support-runner-v5.js +0 -7624
  733. package/util/support-runner-v5.js.map +0 -1
  734. package/util/tokenizer.d.ts +0 -5
  735. package/util/tokenizer.js +0 -41
  736. package/util/tokenizer.js.map +0 -1
  737. package/workers/codex-runner.worker.d.ts +0 -1
  738. package/workers/codex-runner.worker.js +0 -192
  739. package/workers/codex-runner.worker.js.map +0 -1
  740. /package/{private → src/private}/email-templates/enrollment.html +0 -0
  741. /package/{private → src/private}/email-templates/forgot-password.html +0 -0
  742. /package/{private → src/private}/email-templates/support-ticket-deleted.html +0 -0
  743. /package/{private → src/private}/email-templates/support-ticket-modified.html +0 -0
  744. /package/{private → src/private}/email-templates/support-ticket.html +0 -0
  745. /package/{public_api.d.ts → src/public_api.ts} +0 -0
@@ -0,0 +1,3201 @@
1
+ import * as assert from 'node:assert/strict';
2
+ import {
3
+ buildResolveIOSupportDiagnosisEvidencePack,
4
+ buildResolveIOSupportDiagnosisProofMatrix,
5
+ buildResolveIOSupportEvidenceProbeContract,
6
+ buildResolveIOSupportIssueClassProbePlan,
7
+ buildResolveIOSupportIssueClassProbes,
8
+ buildResolveIOSupportOwnerScopedRepairContract,
9
+ buildResolveIOSupportClarificationContract,
10
+ buildResolveIOSupportContinuationProofCheckpoint,
11
+ buildResolveIOSupportManagerExecutionPacket,
12
+ buildResolveIOSupportV5MicrotaskPrompt,
13
+ buildResolveIOSupportV5DiagnoseFirstPrompt,
14
+ buildResolveIOSupportV5Incident,
15
+ buildResolveIOSupportV5PromptBudget,
16
+ buildResolveIOSupportV5ScopeDigest,
17
+ changedFilesOutsideResolveIOSupportDiagnosisOwnerFiles,
18
+ decideResolveIOSupportCustomerReplyPolicy,
19
+ decideResolveIOSupportV5AutonomousNextAction,
20
+ decideResolveIOSupportV5RepairGate,
21
+ decideResolveIOSupportV5RepeatedFailureStop,
22
+ decideResolveIOSupportV5Continuation,
23
+ evaluateResolveIOSupportBusinessProofReadiness,
24
+ evaluateResolveIOSupportDiagnosisEvidenceQuality,
25
+ evaluateResolveIOSupportEvidenceFreshness,
26
+ evaluateResolveIOSupportPreflightGate,
27
+ extractResolveIOSupportDiagnosisGateFromText,
28
+ fingerprintResolveIOSupportV5Blocker,
29
+ initializeResolveIOSupportV5State,
30
+ recordResolveIOSupportV5MicrotaskUsage,
31
+ recordResolveIOSupportV5Step,
32
+ selectResolveIOSupportV5ActiveMicrotask,
33
+ selectResolveIOSupportSimilarCaseHints,
34
+ summarizeResolveIOSupportV5MicrotaskUsage,
35
+ validateResolveIOSupportClarificationContract,
36
+ validateResolveIOSupportCustomerReplyReadinessContract,
37
+ validateResolveIOSupportDiagnosisGate,
38
+ validateResolveIOSupportEvidenceProbeContract,
39
+ validateResolveIOSupportIssueClassProbePlan,
40
+ validateResolveIOSupportNextActionContract
41
+ } from '../src/util/support-runner-v5';
42
+
43
+ const initialized = initializeResolveIOSupportV5State({
44
+ jobId: 'job-1',
45
+ ticketId: 'ticket-1',
46
+ ticketNumber: '004999',
47
+ title: 'Support ticket #004999',
48
+ description: 'Fix BOL driver display and QA it.',
49
+ approvedScopeRequirements: ['BOL driver display', 'disabled driver canonical rejection'],
50
+ prBranch: 'feature/support-004999',
51
+ buildThreadKey: 'support:ticket-1:job:job-1:build',
52
+ qaThreadKey: 'support:ticket-1:job:job-1:qa',
53
+ now: '2026-06-06T00:00:00.000Z'
54
+ });
55
+
56
+ assert.equal(initialized.supportWorkflowVersion, 'v5');
57
+ assert.equal(initialized.supportV5SupervisorState.noEmailUnlessApproved, true);
58
+ assert.equal(initialized.supportV5LaneMemory.build.model, 'gpt-5.3-codex');
59
+ assert.equal(initialized.supportV5LaneMemory.qa.model, 'gpt-5.4-mini');
60
+ assert.equal(initialized.supportV5LaneMemory.build.threadKey, 'support:ticket-1:job:job-1:build');
61
+ assert.match(initialized.supportV5SupervisorState.approvedScope, /BOL driver display/);
62
+ assert.ok(initialized.supportV5MicrotaskLedger.length >= 4);
63
+ assert.equal(initialized.supportV5MicrotaskLedger[0].type, 'diagnosis_gate');
64
+ assert.equal(initialized.supportV5MicrotaskLedger[0].lane, 'build');
65
+ assert.equal(initialized.supportV5MicrotaskLedger[1].lane, 'build');
66
+ assert.equal(initialized.supportV5MicrotaskLedger[2].lane, 'qa');
67
+ assert.equal(initialized.supportV5MicrotaskLedger[0].threadKey, 'support:ticket-1:job:job-1:build');
68
+ assert.equal(initialized.supportV5MicrotaskLedger[2].threadKey, 'support:ticket-1:job:job-1:qa');
69
+ assert.equal(initialized.supportV5ActiveMicrotaskId, initialized.supportV5MicrotaskLedger[0].microtaskId);
70
+ assert.equal(selectResolveIOSupportV5ActiveMicrotask(initialized.supportV5MicrotaskLedger)?.type, 'diagnosis_gate');
71
+ assert.match(initialized.supportV5ScopeDigest || '', /Approved scope/);
72
+ assert.equal(validateResolveIOSupportDiagnosisGate(initialized.supportV5DiagnosisGate).valid, false);
73
+ assert.deepEqual(initialized.supportV5RecoveryDispatchHistory, []);
74
+ assert.equal(initialized.supportV5RecoveryDirective, undefined);
75
+
76
+ const initialDiagnosisEvidencePack = buildResolveIOSupportDiagnosisEvidencePack({
77
+ issueClass: 'filter_query_mismatch',
78
+ ownerFiles: ['server/src/publications/support-tickets.ts'],
79
+ text: 'Support ticket list customer filter returns rows for the wrong customer.',
80
+ now: '2026-06-06T00:00:00.000Z'
81
+ });
82
+ const initializedWithDiagnosisEvidencePack = initializeResolveIOSupportV5State({
83
+ jobId: 'job-pack',
84
+ ticketId: 'ticket-pack',
85
+ ticketNumber: '004998',
86
+ title: 'Support ticket #004998',
87
+ description: 'Preserve diagnosis evidence pack.',
88
+ existing: {
89
+ supportV5DiagnosisEvidencePack: initialDiagnosisEvidencePack
90
+ } as any,
91
+ now: '2026-06-06T00:00:00.000Z'
92
+ });
93
+ assert.equal(initializedWithDiagnosisEvidencePack.supportV5DiagnosisEvidencePack?.packId, initialDiagnosisEvidencePack.packId);
94
+ assert.equal(initializedWithDiagnosisEvidencePack.supportV5DiagnosisEvidencePack?.sourceEditsAllowed, false);
95
+
96
+ const validDiagnosis = {
97
+ issue_case: {
98
+ customer_complaint: 'Disabled drivers appear in the BOL driver dropdown.',
99
+ expected_result: 'Disabled drivers are excluded or rejected.',
100
+ observed_result: 'Disabled driver can be selected.',
101
+ route_module: '/dashboard/driver/truck-treating/bol-detail/bol/route',
102
+ account_customer_context: '004999 reporter, truck treating route fixture',
103
+ reproduction_status: 'reproduced' as const
104
+ },
105
+ issue_class: 'filter_query_mismatch' as const,
106
+ accepted_hypothesis: {
107
+ statement: 'The driver dropdown publication does not filter disabled users.',
108
+ falsifiable_test: 'Inspect the publication/method query and run the dropdown with a disabled driver fixture.',
109
+ evidence: ['Ticket names disabled driver selection.', 'Publication path returns drivers without active filter.']
110
+ },
111
+ rejected_alternatives: ['Route auth failure rejected because the BOL page hydrates.', 'Pure CSS issue rejected because persisted selected id changes.'],
112
+ failing_path: {
113
+ frontend: 'geochem/angular/app/bol/new/bol-new.component.ts driver select',
114
+ backend: 'geochem/server/src/publications/drivers.ts',
115
+ description: 'Frontend driver select consumes an unfiltered backend driver list.'
116
+ },
117
+ owner_files: [
118
+ 'geochem/angular/app/bol/new/bol-new.component.ts',
119
+ 'geochem/server/src/publications/drivers.ts'
120
+ ],
121
+ proof_plan: {
122
+ before: 'Seed/identify a disabled driver available to the BOL route.',
123
+ action: 'Open the BOL route, open the driver dropdown, and attempt selecting the disabled driver.',
124
+ after: 'Disabled driver is absent or rejected and the active driver remains selected.',
125
+ business_assertion: 'Disabled driver cannot replace the selected active driver.',
126
+ route: '/dashboard/driver/truck-treating/bol-detail/bol/route',
127
+ data_assertion: 'Persisted BOL driver id remains active after save attempt.',
128
+ business_proof_contract: {
129
+ issue_class: 'filter_query_mismatch' as const,
130
+ setup_state: 'A BOL route has one active driver and one disabled driver in the candidate dataset.',
131
+ action_under_test: 'Open the driver dropdown and attempt to choose the disabled driver.',
132
+ expected_business_state_change: 'The disabled driver is excluded or rejected while the active driver remains selected.',
133
+ prohibited_false_pass: 'Route load, screenshot visibility, or a populated dropdown alone is not acceptance.',
134
+ proof_artifacts: ['browser trace', 'dropdown DOM snapshot', 'persisted BOL driver id check'],
135
+ data_or_dom_assertion: 'The disabled driver option is absent or blocked and the saved BOL driver id remains active.'
136
+ }
137
+ },
138
+ evidence: [
139
+ { type: 'ticket' as const, summary: 'Ticket reports disabled driver selection in BOL workflow.' },
140
+ { type: 'browser' as const, summary: 'BOL driver dropdown allowed the disabled driver before repair.', artifactPath: 'qa-artifacts/004999-disabled-driver-before.png' },
141
+ { type: 'code' as const, summary: 'Publication lacks disabled-user filter.' }
142
+ ],
143
+ status: 'passed' as const
144
+ };
145
+
146
+ const validDiagnosisResult = validateResolveIOSupportDiagnosisGate(validDiagnosis);
147
+ assert.equal(validDiagnosisResult.valid, true);
148
+ assert.equal(validDiagnosisResult.normalized?.status, 'passed');
149
+ assert.equal(validDiagnosisResult.evidenceQuality?.hasReproductionEvidence, true);
150
+ assert.equal(validDiagnosisResult.evidenceQuality?.artifactBackedEvidenceCount, 1);
151
+ assert.equal(validDiagnosisResult.proofMatrix?.status, 'ready');
152
+ assert.equal(validDiagnosisResult.proofMatrix?.ownerFileCoverage.every((entry) => entry.covered), true);
153
+ assert.equal(validDiagnosisResult.proofMatrix?.businessProofArtifacts.some((artifact) => /dropdown DOM snapshot|persisted BOL driver id check/i.test(artifact)), true);
154
+ assert.equal(buildResolveIOSupportIssueClassProbes(validDiagnosis)[0].issue_class, 'filter_query_mismatch');
155
+ assert.match(buildResolveIOSupportIssueClassProbes(validDiagnosis)[0].expected_evidence, /dropdown DOM snapshot/);
156
+ assert.equal(buildResolveIOSupportIssueClassProbes(validDiagnosis)[0].failure_class, 'business');
157
+ assert.equal(buildResolveIOSupportIssueClassProbes(validDiagnosis)[0].acceptance_gate, 'aiqa_business_assertion');
158
+ assert.ok(buildResolveIOSupportIssueClassProbes(validDiagnosis)[0].required_artifacts.some((artifact) => /included\/excluded row proof|dropdown DOM snapshot/i.test(artifact)));
159
+ assert.equal(buildResolveIOSupportIssueClassProbes(validDiagnosis)[0].state_transition.assertion, validDiagnosis.proof_plan.business_proof_contract.data_or_dom_assertion);
160
+ assert.ok(buildResolveIOSupportIssueClassProbes(validDiagnosis)[0].browser_steps.length >= 3);
161
+ assert.equal(buildResolveIOSupportIssueClassProbes(validDiagnosis)[0].active_qa_row?.acceptance_gate, 'aiqa_business_assertion');
162
+ assert.equal(buildResolveIOSupportIssueClassProbes(validDiagnosis)[0].active_qa_row?.route_only_is_false_pass, true);
163
+ assert.equal(buildResolveIOSupportIssueClassProbes(validDiagnosis)[0].aiqa_business_assertion_template.metadata.supportDiagnosisProof, true);
164
+
165
+ const structuredDiagnosisResult = validateResolveIOSupportDiagnosisGate({
166
+ ...validDiagnosis,
167
+ owner_files: [
168
+ { path: 'geochem/angular/app/bol/new/bol-new.component.ts' },
169
+ { ownerFile: 'geochem/server/src/publications/drivers.ts' }
170
+ ],
171
+ accepted_hypothesis: {
172
+ ...validDiagnosis.accepted_hypothesis,
173
+ evidence: [
174
+ { summary: 'Ticket names disabled driver selection.' },
175
+ { message: 'Publication path returns drivers without active filter.' }
176
+ ]
177
+ },
178
+ evidence: [
179
+ { type: 'ticket' as const, summary: 'Ticket reports disabled driver selection in BOL workflow.', ownerFiles: [{ path: 'geochem/server/src/publications/drivers.ts' }] },
180
+ { type: 'browser' as const, summary: 'BOL driver dropdown allowed the disabled driver before repair.', artifactPath: 'qa-artifacts/004999-disabled-driver-before.png' },
181
+ { type: 'code' as const, summary: 'Publication lacks disabled-user filter.', ownerFiles: [{ ownerFile: 'geochem/server/src/publications/drivers.ts' }] }
182
+ ]
183
+ });
184
+ assert.equal(structuredDiagnosisResult.valid, true);
185
+ assert.deepEqual(structuredDiagnosisResult.normalized?.owner_files, validDiagnosis.owner_files);
186
+ assert.deepEqual(structuredDiagnosisResult.normalized?.accepted_hypothesis.evidence, validDiagnosis.accepted_hypothesis.evidence);
187
+ assert.equal(structuredDiagnosisResult.normalized?.owner_files.some((entry) => entry === '[object Object]'), false);
188
+
189
+ const workspacePrefixedOwnerDiagnosisResult = validateResolveIOSupportDiagnosisGate({
190
+ ...validDiagnosis,
191
+ owner_files: validDiagnosis.owner_files.map((ownerFile) => `/var/app/resolveio-ai-workspace/6a35c4162031d2a4a0216ce4/resolveio-all/${ownerFile}`)
192
+ });
193
+ assert.equal(workspacePrefixedOwnerDiagnosisResult.valid, true, workspacePrefixedOwnerDiagnosisResult.blockers.join(' | '));
194
+ assert.deepEqual(workspacePrefixedOwnerDiagnosisResult.normalized?.owner_files, validDiagnosis.owner_files);
195
+ assert.equal(workspacePrefixedOwnerDiagnosisResult.blockers.some((blocker) => /broad or unsafe path/i.test(blocker)), false);
196
+
197
+ const looseFeatureRequestDiagnosisResult = validateResolveIOSupportDiagnosisGate({
198
+ support_diagnosis_gate: {
199
+ issue_case: 'Customer requested a new QC/Sample Testing dashboard workflow with field-entry and manager views modeled after Dry Media.',
200
+ issue_class: 'feature_request',
201
+ accepted_hypothesis: 'This is a feature request, not an existing behavior failure, because no QC dashboard route, permission, navigation entry, or client route exists today.',
202
+ rejected_alternatives: [
203
+ { hypothesis: 'Existing QC dashboard route is broken.', reason_rejected: 'No QC dashboard route is defined in dashboard routing or the server client-route allowlist.' },
204
+ { hypothesis: 'Existing QC UI is hidden by a user-role guard.', why_rejected: 'No QC screen or permission target exists in the current navigation/permission chain.' }
205
+ ],
206
+ failing_path: {
207
+ path: 'none_existing',
208
+ route_module_permission_gate: 'No QC dashboard route and no QC dashboard permission entry found.',
209
+ template_conditional_rendering_gate: 'No QC template/helper visibility guard found.',
210
+ backing_role_group_config_publications_gate: 'No QC-specific role/group/config publication gate found for a dashboard path.'
211
+ },
212
+ owner_files: [
213
+ '/var/app/resolveio-ai-workspace/6a35a876bccf4dc547494e27/resolveio-all/h2sko/.resolveio-support-context/manual-ticket.request.txt',
214
+ '/var/app/resolveio-ai-workspace/6a35a876bccf4dc547494e27/resolveio-all/h2sko/angular/app/dashboard/dashboard.routing.ts',
215
+ '/var/app/resolveio-ai-workspace/6a35a876bccf4dc547494e27/resolveio-all/h2sko/angular/app/dashboard/dashboard.permission.ts',
216
+ '/var/app/resolveio-ai-workspace/6a35a876bccf4dc547494e27/resolveio-all/h2sko/angular/app/app.component.ts',
217
+ '/var/app/resolveio-ai-workspace/6a35a876bccf4dc547494e27/resolveio-all/h2sko/server/src/client-routes.ts'
218
+ ],
219
+ proof_plan: [
220
+ {
221
+ action: 'Map spreadsheet tabs Specs and Batch Entry to a QC schema and field-entry flow.',
222
+ proof_required: 'Representative QC records persist mapped fields and pass/fail status inputs.'
223
+ },
224
+ {
225
+ action: 'Implement a QC manager/list flow with date, media, source, and type filters.',
226
+ proof_required: 'Date/media/source/type filtering and sorting return expected QC rows.'
227
+ },
228
+ {
229
+ action: 'Add QC route, permission, and navigation wiring.',
230
+ proof_required: 'Authorized users can access QC screens and route-only evidence is not accepted without saved QC data proof.'
231
+ }
232
+ ],
233
+ evidence: [
234
+ { file: '/var/app/resolveio-ai-workspace/6a35a876bccf4dc547494e27/resolveio-all/h2sko/.resolveio-support-context/manual-ticket.request.txt', fact: 'Subject is New Feature Request and asks for QC/Sample Testing functionality.' },
235
+ { file: '/var/app/resolveio-ai-workspace/6a35a876bccf4dc547494e27/resolveio-all/h2sko/angular/app/dashboard/dashboard.routing.ts', fact: 'Dashboard routes include Dry Media and sample paths but no QC dashboard route.' },
236
+ { file: '/var/app/resolveio-ai-workspace/6a35a876bccf4dc547494e27/resolveio-all/h2sko/angular/app/dashboard/dashboard.permission.ts', fact: 'Dashboard permissions include Dry Media and Lab Sample entries but no QC dashboard permission.' },
237
+ { file: '/var/app/resolveio-ai-workspace/6a35a876bccf4dc547494e27/resolveio-all/h2sko/angular/app/app.component.ts', fact: 'Navigation includes Dry Media entries but no QC nav item.' },
238
+ { file: '/var/app/resolveio-ai-workspace/6a35a876bccf4dc547494e27/resolveio-all/h2sko/server/src/client-routes.ts', fact: 'Client route allowlist includes Dry Media/sample routes but no QC dashboard route.' }
239
+ ],
240
+ status: 'passed'
241
+ }
242
+ });
243
+ assert.equal(looseFeatureRequestDiagnosisResult.valid, true);
244
+ assert.equal(looseFeatureRequestDiagnosisResult.normalized?.issue_class, 'missing_wrong_data');
245
+ assert.equal(looseFeatureRequestDiagnosisResult.normalized?.issue_case.reproduction_status, 'classified');
246
+ assert.deepEqual(looseFeatureRequestDiagnosisResult.normalized?.owner_files, [
247
+ 'h2sko/angular/app/dashboard/dashboard.routing.ts',
248
+ 'h2sko/angular/app/dashboard/dashboard.permission.ts',
249
+ 'h2sko/angular/app/app.component.ts',
250
+ 'h2sko/server/src/client-routes.ts'
251
+ ]);
252
+ assert.match(looseFeatureRequestDiagnosisResult.normalized?.issue_case.expected_result || '', /requested workflow/i);
253
+ assert.match(looseFeatureRequestDiagnosisResult.normalized?.proof_plan.business_assertion || '', /Representative QC records persist/i);
254
+ assert.match(looseFeatureRequestDiagnosisResult.normalized?.proof_plan.business_proof_contract?.data_or_dom_assertion || '', /Representative QC records persist/i);
255
+ assert.ok(looseFeatureRequestDiagnosisResult.normalized?.rejected_alternatives.every((entry) => !/\[object Object\]/.test(entry)));
256
+ assert.ok(looseFeatureRequestDiagnosisResult.evidenceQuality?.hasRootCauseEvidence);
257
+
258
+ const live4433DiagnosisResult = validateResolveIOSupportDiagnosisGate({
259
+ support_diagnosis_gate: {
260
+ status: 'passed',
261
+ issue_case: 'On `/part/detail/:id`, the user is comparing two different tables: a check-in entry showing `Kermit` and checkout history entries showing `Midland` for the same part.',
262
+ issue_class: 'data_mismatch',
263
+ accepted_hypothesis: {
264
+ summary: 'Existing behavior, not a runtime defect. Check-ins and checkouts are intentionally separate data streams on this screen, and checkout warehouse values come from checkout records (`warehouse_location`).',
265
+ why: 'Template and publication wiring show distinct sources for check-in vs checkout rows.'
266
+ },
267
+ rejected_alternatives: [
268
+ { hypothesis: 'Permission/role gating is changing warehouse visibility/value.', reason: 'No field-level permission/helper gate controls these warehouse cells; only edit/delete actions are role-wrapped.' },
269
+ { hypothesis: 'Report logic is rewriting warehouse on Part Detail.', reason: 'Part Detail history tables are fed by publications, not report methods.' },
270
+ { hypothesis: 'Checkout query is incorrectly constrained to Midland.', reason: 'Checkout publication filter is by part item id, so all checkout warehouses for that part are returned.' }
271
+ ],
272
+ failing_path: {
273
+ route: '/part/detail/:id',
274
+ chain: 'PartDetailComponent subscribes to `checkinsWithItemId` and `checkoutsWithItemId` separately; template renders separate check-in and checkout sections; checkout table binds `checkout.warehouse_location`.',
275
+ visibility_gate_trace: 'No route/module/helper gating found for these specific warehouse fields in the two history tables.'
276
+ },
277
+ owner_files: [
278
+ '/var/app/resolveio-ai-workspace/6a35dfb2feca513d5ef50c00/resolveio-all/h2sko/angular/app/part/detail/part-detail.component.html',
279
+ '/var/app/resolveio-ai-workspace/6a35dfb2feca513d5ef50c00/resolveio-all/h2sko/angular/app/part/detail/part-detail.component.ts',
280
+ '/var/app/resolveio-ai-workspace/6a35dfb2feca513d5ef50c00/resolveio-all/h2sko/server/src/publications/checkins.ts',
281
+ '/var/app/resolveio-ai-workspace/6a35dfb2feca513d5ef50c00/resolveio-all/h2sko/server/src/publications/checkouts.ts'
282
+ ],
283
+ proof_plan: [
284
+ { step: 'Match screenshot table title/columns to exact Part Detail section.', proof: 'Shown table is `Part Check-out History`.' },
285
+ { step: 'Trace component subscriptions.', proof: 'Check-ins and checkouts are loaded from separate publications.' },
286
+ { step: 'Trace publication queries.', proof: 'Both are filtered by part item id and return independent record sets.' },
287
+ { step: 'Classify outcome.', proof: 'General Inquiry / existing behavior unless customer requests behavior change.' }
288
+ ],
289
+ evidence: [
290
+ { artifact: '/var/app/resolveio-ai-workspace/6a35dfb2feca513d5ef50c00/resolveio-all/h2sko/.resolveio-support-context/manual-ticket.request.txt', finding: 'Reporter says inventory entered in Kermit on June 1 and later saw Midland.' },
291
+ { artifact: '/var/app/resolveio-ai-workspace/6a35dfb2feca513d5ef50c00/resolveio-all/h2sko/.resolveio-support-context/attachments/Screenshot_2026-06-09_at_11.16.41_AM.png', finding: 'Visible grid is `Part Check-out History` with Midland rows.' },
292
+ { artifact: '/var/app/resolveio-ai-workspace/6a35dfb2feca513d5ef50c00/resolveio-all/h2sko/angular/app/part/detail/part-detail.component.html', finding: 'Separate `Other Part Check-in History` and `Part Check-out History` sections; checkout warehouse uses `checkout.warehouse_location`.' },
293
+ { artifact: '/var/app/resolveio-ai-workspace/6a35dfb2feca513d5ef50c00/resolveio-all/h2sko/angular/app/part/detail/part-detail.component.ts', finding: 'Separate subscriptions: `checkinsWithItemId` and `checkoutsWithItemId`.' },
294
+ { artifact: '/var/app/resolveio-ai-workspace/6a35dfb2feca513d5ef50c00/resolveio-all/h2sko/server/src/publications/checkins.ts', finding: '`checkinsWithItemId` returns `Checkins.find({\'items.id\': id_item})`.' },
295
+ { artifact: '/var/app/resolveio-ai-workspace/6a35dfb2feca513d5ef50c00/resolveio-all/h2sko/server/src/publications/checkouts.ts', finding: '`checkoutsWithItemId` returns `Checkouts.find({\'items.id\': id_item})`.' }
296
+ ]
297
+ }
298
+ });
299
+ assert.equal(live4433DiagnosisResult.valid, true, live4433DiagnosisResult.blockers.join(' | '));
300
+ assert.deepEqual(live4433DiagnosisResult.normalized?.owner_files, [
301
+ 'h2sko/angular/app/part/detail/part-detail.component.html',
302
+ 'h2sko/angular/app/part/detail/part-detail.component.ts',
303
+ 'h2sko/server/src/publications/checkins.ts',
304
+ 'h2sko/server/src/publications/checkouts.ts'
305
+ ]);
306
+ assert.ok(live4433DiagnosisResult.normalized?.evidence.some((entry) => entry.type === 'code' && entry.ownerFiles?.includes('h2sko/server/src/publications/checkouts.ts')));
307
+ assert.ok(live4433DiagnosisResult.evidenceQuality?.hasRootCauseEvidence);
308
+
309
+ const live4433ContractAliasDiagnosisResult = validateResolveIOSupportDiagnosisGate({
310
+ support_diagnosis_gate: {
311
+ issue_case: {
312
+ customer_symptom: 'Part inventory entered for Kermit appears under Midland in inventory reporting.',
313
+ reproduction_status: 'blocked_runtime',
314
+ reproduction_blocker: 'No accessible staged runtime Mongo/log artifact files were present; diagnosis is based on ticket attachment plus repository code-path evidence.'
315
+ },
316
+ issue_class: 'logic_rework',
317
+ accepted_hypothesis: 'Warehouse attribution drifts in `server/src/methods/report.ts` because `consumePartLayers` consumes non-matching warehouse layers via fallback when matching layers are absent, so Midland checkout activity can deplete Kermit-owned layers and later reconciliation preserves incorrect warehouse attribution.',
318
+ rejected_alternatives: [
319
+ 'Frontend render/binding defect in report components.',
320
+ 'Checkout form write-path storing wrong warehouse id/name.',
321
+ 'Attachment asset (logo) defining business behavior.'
322
+ ],
323
+ failing_path: 'Frontend reads warehouse from snapshot report rows -> backend `reportDailyInventorySnapshot`/`buildPartInventorySnapshotRows` -> `buildPartInventoryLayers` -> `consumePartLayers` fallback consumption in `server/src/methods/report.ts` causes cross-warehouse layer depletion.',
324
+ owner_files: ['server/src/methods/report.ts'],
325
+ proof_plan: {
326
+ before: 'Use a part with Kermit check-in quantity and later Midland checkout records for the same part.',
327
+ action: 'Run snapshot/report method for the affected day and inspect generated `Parts` rows for that part.',
328
+ after: 'Kermit-origin quantity remains attributed to Kermit; Midland no longer captures that quantity due to non-matching fallback consumption.',
329
+ business_proof_contract: {
330
+ setup_state: 'Part history contains explicit Kermit inventory add plus Midland checkout activity.',
331
+ action_under_test: 'Part layer warehouse consumption and attribution during snapshot generation.',
332
+ expected_business_state_change: 'Warehouse attribution follows true ownership (Kermit entry reports under Kermit).',
333
+ prohibited_false_pass: 'Build/route/screenshot-only pass without row-level warehouse assertion for the affected part.',
334
+ proof_artifacts: ['runner-evidence/diagnosis-gate.json', 'qa-artifacts/aiqa-business-assertion.json'],
335
+ dom_data_assertion: 'Affected part row warehouse value equals Kermit and is not Midland for Kermit-origin quantity.'
336
+ }
337
+ },
338
+ evidence: [
339
+ { type: 'ticket_attachment', artifact: '.resolveio-support-context/attachments/Screenshot_2026-06-09_at_11.16.41_AM.png', detail: 'Used as symptom evidence for Kermit vs Midland mismatch context.' },
340
+ { type: 'code', artifact: 'server/src/methods/report.ts:440-457', detail: '`consumePartLayers` consumes fallback non-matching warehouse layers.' },
341
+ { type: 'code', artifact: 'server/src/methods/report.ts:577-583', detail: 'Checkout warehouse keys drive layer consumption path.' }
342
+ ],
343
+ status: 'passed'
344
+ }
345
+ });
346
+ assert.equal(live4433ContractAliasDiagnosisResult.valid, true, live4433ContractAliasDiagnosisResult.blockers.join(' | '));
347
+ assert.equal(live4433ContractAliasDiagnosisResult.normalized?.issue_class, 'missing_wrong_data');
348
+ assert.match(live4433ContractAliasDiagnosisResult.normalized?.proof_plan.business_assertion || '', /Kermit/i);
349
+ assert.match(live4433ContractAliasDiagnosisResult.normalized?.proof_plan.business_proof_contract?.data_or_dom_assertion || '', /Kermit/i);
350
+ assert.ok(live4433ContractAliasDiagnosisResult.normalized?.evidence.some((entry) => entry.type === 'code' && entry.ownerFiles?.includes('server/src/methods/report.ts')));
351
+
352
+ const live4433BlockedRuntimeStringDiagnosisResult = validateResolveIOSupportDiagnosisGate({
353
+ support_diagnosis_gate: {
354
+ issue_case: 'Ticket symptom: quantity entered for Kermit appears under Midland in inventory report output; runtime reproduction is blocked in this workspace because staged Mongo/log artifacts under `/var/ai-workspace/6a283cf6f78633bb8dbc0ef6` are not accessible here.',
355
+ issue_class: 'missing_wrong_data',
356
+ accepted_hypothesis: '`server/src/methods/report.ts` is the failing owner. In `consumePartLayers`, fallback consumption of non-matching warehouse layers can deplete Kermit-owned layers during Midland checkout replay, shifting final `warehouse_location` attribution to Midland.',
357
+ rejected_alternatives: [
358
+ 'Frontend rendering defect in report view (UI prints backend `warehouse_location` directly).',
359
+ 'Checkout write-path defect (selected yard is written to warehouse fields on submit path).'
360
+ ],
361
+ failing_path: 'Report UI Warehouse column <- `reportDailyInventorySnapshot` <- `buildPartInventorySnapshotRows` <- `buildPartInventoryLayers` <- `consumePartLayers` fallback non-matching layer consumption (`server/src/methods/report.ts`).',
362
+ owner_files: ['server/src/methods/report.ts'],
363
+ proof_plan: {
364
+ before: 'Use an affected part with Kermit check-in quantity and later Midland checkout activity.',
365
+ action: 'Run snapshot/report generation for the affected date and inspect `Parts` rows for that part.',
366
+ after: 'Kermit-origin quantity remains under Kermit and is not attributed to Midland.',
367
+ business_proof_contract: {
368
+ setup_state: 'Part history contains explicit Kermit inventory add plus Midland checkout records for the same part.',
369
+ action_under_test: 'Warehouse-aware layer consumption and attribution in snapshot reconstruction.',
370
+ expected_business_state_change: 'Warehouse attribution matches true ownership source.',
371
+ prohibited_false_pass: 'Build pass, route load, or screenshot-only evidence without row-level warehouse assertion for the affected part.',
372
+ proof_artifacts: ['runner-evidence/diagnosis-gate.json', 'qa-artifacts/aiqa-business-assertion.json'],
373
+ dom_data_assertion: 'Affected part row(s) show `warehouse_location` = Kermit canonical yard name for Kermit-origin quantity, not Midland.'
374
+ }
375
+ },
376
+ evidence: [
377
+ { type: 'ticket', artifact: '.resolveio-support-context/attachments/Screenshot_2026-06-09_at_11.16.41_AM.png', detail: 'Symptom evidence for Kermit vs Midland mismatch.' },
378
+ { type: 'code', artifact: 'server/src/methods/report.ts:440-457', detail: '`consumePartLayers` consumes fallback non-matching layers.' },
379
+ { type: 'code', artifact: 'server/src/methods/report.ts:577-583', detail: 'Checkout warehouse keys are passed into layer consumption.' },
380
+ { type: 'code', artifact: 'server/src/methods/report.ts:487-548', detail: 'Post-consumption reconciliation preserves resulting attribution.' },
381
+ { type: 'code', artifact: 'angular/app/report/part-inventory-snapshot/report-part-inventory-snapshot.component.ts', detail: 'Frontend displays backend `warehouse_location` without remapping.' }
382
+ ],
383
+ status: 'passed'
384
+ }
385
+ });
386
+ assert.equal(live4433BlockedRuntimeStringDiagnosisResult.valid, true, live4433BlockedRuntimeStringDiagnosisResult.blockers.join(' | '));
387
+ assert.equal(live4433BlockedRuntimeStringDiagnosisResult.normalized?.issue_case.reproduction_status, 'blocked');
388
+ assert.match(live4433BlockedRuntimeStringDiagnosisResult.normalized?.issue_case.reproduction_blocker || '', /runtime reproduction is blocked/i);
389
+
390
+ const live4433CurrentRunnerDiagnosisResult = validateResolveIOSupportDiagnosisGate({
391
+ support_diagnosis_gate: {
392
+ issue_case: {
393
+ id_support_ticket: '6a283cf6f78633bb8dbc0ef6',
394
+ support_ticket_number: '004433',
395
+ issue: 'Part inventory entered for Kermit is reported under Midland in pulled inventory output.',
396
+ reproduction: 'blocked',
397
+ blocking_artifacts: [
398
+ '/var/ai-workspace/6a283cf6f78633bb8dbc0ef6/mongo-context (record-level exports for checkins/checkouts/inventory-daily-snapshots not present)',
399
+ '/var/ai-workspace/6a283cf6f78633bb8dbc0ef6/logs-context (searchable runtime error/query logs not present)'
400
+ ]
401
+ },
402
+ issue_class: 'missing_wrong_data',
403
+ accepted_hypothesis: {
404
+ statement: 'In `server/src/methods/report.ts`, part-layer reconciliation can attribute remaining quantity to the wrong warehouse because `consumePartLayers(...)` consumes `matchingLayers.concat(fallbackLayers)`, so a checkout with non-matching warehouse keys can still consume other-yard layers and leave the final layer assignment under a different warehouse.',
405
+ falsifiable_test: 'Reconstruct ticket part history from checkins/checkouts for the report date and verify whether checkout rows whose `id_warehouse_location/warehouse_location` do not match Kermit still consume Kermit layers in `consumePartLayers`.'
406
+ },
407
+ rejected_alternatives: [
408
+ { alternative: 'Angular rendering bug in part detail warehouse column.', why_rejected: '`part-detail.component.html` renders `checkout.warehouse_location` directly.' },
409
+ { alternative: 'Manual part inventory write path saves wrong warehouse.', why_rejected: '`addPartInventory()` writes selected yard fields.' }
410
+ ],
411
+ failing_path: {
412
+ frontend_event_data_path: 'Part inventory/report pull that shows warehouse attribution for parts.',
413
+ backend_method_publication_query_path: '`server/src/methods/report.ts` -> `reportDailyInventorySnapshot` -> `buildPartInventoryLayers` -> `consumePartLayers`.',
414
+ data_path: '`checkins` + `checkouts` -> `inventory-daily-snapshots` rows.'
415
+ },
416
+ owner_files: ['server/src/methods/report.ts'],
417
+ proof_plan: {
418
+ before: 'Capture current report response rows for the affected part/date showing warehouse misattribution.',
419
+ action: 'Apply minimal fix inside `consumePartLayers` to stop consuming non-matching warehouse fallback layers for warehouse-scoped checkout consumption.',
420
+ after: 'Re-run same report path and assert affected part quantity is attributed to Kermit, not Midland.',
421
+ business_proof_contract: {
422
+ setup_state: 'Affected part has Kermit inventory entry (6 units) and later report currently shows Midland attribution.',
423
+ action_under_test: 'Run `reportDailyInventorySnapshot` for the ticket date/path used by customer.',
424
+ expected_business_state_change: 'Row for affected part quantity is assigned to Kermit warehouse identity.',
425
+ prohibited_false_pass: 'Build pass, route load, or screenshot-only proof without row-level data assertion on affected part warehouse.',
426
+ proof_artifacts: ['runner-evidence/diagnosis-gate.json', 'qa-artifacts/aiqa-business-assertion.json'],
427
+ dom_data_assertion: 'For target `metadata.id_part`, returned `rows` contain the affected quantity with `warehouse_location` mapped to Kermit, and do not misattribute that quantity to Midland.'
428
+ }
429
+ },
430
+ evidence: [
431
+ { type: 'attachment', artifact: '/var/ai-workspace/6a283cf6f78633bb8dbc0ef6/attachments/Screenshot 2026-06-09 at 11.16.41AM.png', supports: 'Customer-observed warehouse mismatch context.' },
432
+ { type: 'code', artifact: '/var/app/resolveio-ai-workspace/6a35ef09648b18962650d369/resolveio-all/h2sko/server/src/methods/report.ts', supports: 'Fallback layer consumption path in part inventory reconciliation is the smallest owning break path.' }
433
+ ],
434
+ status: 'passed'
435
+ }
436
+ });
437
+ assert.equal(live4433CurrentRunnerDiagnosisResult.valid, true, live4433CurrentRunnerDiagnosisResult.blockers.join(' | '));
438
+ assert.equal(live4433CurrentRunnerDiagnosisResult.normalized?.issue_case.reproduction_status, 'blocked');
439
+ assert.match(live4433CurrentRunnerDiagnosisResult.normalized?.issue_case.customer_complaint || '', /Kermit.*Midland/i);
440
+ assert.match(live4433CurrentRunnerDiagnosisResult.normalized?.issue_case.reproduction_blocker || '', /mongo-context/i);
441
+ assert.deepEqual(live4433CurrentRunnerDiagnosisResult.normalized?.owner_files, ['server/src/methods/report.ts']);
442
+ assert.ok(live4433CurrentRunnerDiagnosisResult.normalized?.accepted_hypothesis.evidence.some((entry) => /Fallback layer consumption path/i.test(entry)));
443
+
444
+ const live4433TicketAttachmentOnlyDiagnosisResult = validateResolveIOSupportDiagnosisGate({
445
+ support_diagnosis_gate: {
446
+ issue_case: 'Inventory entered for Kermit is reported under Midland in part inventory reporting.',
447
+ issue_class: 'missing_wrong_data',
448
+ accepted_hypothesis: 'The owning defect is in `server/src/methods/report.ts`: `consumePartLayers` consumes fallback non-matching warehouse layers when matching layers are unavailable, so Midland checkout replay can consume Kermit-owned layers and shift `warehouse_location` attribution.',
449
+ rejected_alternatives: [
450
+ 'Frontend report rendering/binding bug.',
451
+ 'Checkout write-path storing incorrect warehouse fields.',
452
+ 'Logo/branding attachment affecting inventory behavior.'
453
+ ],
454
+ failing_path: 'Report UI warehouse display -> `reportDailyInventorySnapshot` -> `buildPartInventorySnapshotRows` -> `buildPartInventoryLayers` -> `consumePartLayers` fallback non-matching layer consumption in `server/src/methods/report.ts`.',
455
+ owner_files: ['server/src/methods/report.ts'],
456
+ proof_plan: {
457
+ before: 'Use an affected part with Kermit check-in quantity and later Midland checkout activity for the same part.',
458
+ action: 'Run snapshot/report generation for the affected date and inspect generated `Parts` rows for that part.',
459
+ after: 'Kermit-origin quantity remains attributed to Kermit and is not attributed to Midland.',
460
+ business_proof_contract: {
461
+ setup_state: 'Part history contains explicit Kermit inventory add and Midland checkout records.',
462
+ action_under_test: 'Warehouse-aware layer consumption and attribution during snapshot reconstruction.',
463
+ expected_business_state_change: 'Reported warehouse attribution matches true ownership source.',
464
+ prohibited_false_pass: 'Build pass, route load, or screenshot-only proof without row-level warehouse assertion for the affected part.',
465
+ proof_artifacts: ['runner-evidence/diagnosis-gate.json', 'qa-artifacts/aiqa-business-assertion.json'],
466
+ dom_data_assertion: 'Affected part rows show `warehouse_location` = Kermit canonical yard name for Kermit-origin quantity, not Midland.'
467
+ }
468
+ },
469
+ evidence: [
470
+ { type: 'attachment', artifact: '.resolveio-support-context/attachments/Screenshot_2026-06-09_at_11.16.41_AM.png', detail: 'Primary symptom evidence for Kermit vs Midland mismatch.' },
471
+ { type: 'code', artifact: 'server/src/methods/report.ts:440-457', detail: '`consumePartLayers` consumes fallback non-matching layers.' },
472
+ { type: 'code', artifact: 'server/src/methods/report.ts:577-583', detail: 'Checkout warehouse keys are passed into layer consumption.' },
473
+ { type: 'code', artifact: 'server/src/methods/report.ts:487-548', detail: 'Reconciliation after consumption preserves resulting attribution.' },
474
+ { type: 'code', artifact: 'angular/app/report/part-inventory-snapshot/report-part-inventory-snapshot.component.ts', detail: 'Frontend displays backend `warehouse_location` directly.' }
475
+ ],
476
+ status: 'passed'
477
+ }
478
+ });
479
+ assert.equal(live4433TicketAttachmentOnlyDiagnosisResult.valid, true, live4433TicketAttachmentOnlyDiagnosisResult.blockers.join(' | '));
480
+ assert.equal(live4433TicketAttachmentOnlyDiagnosisResult.normalized?.issue_case.reproduction_status, 'classified');
481
+
482
+ const classifiedFeatureRequestWithoutEvidence = validateResolveIOSupportDiagnosisGate({
483
+ support_diagnosis_gate: {
484
+ issue_case: 'Customer requested a new QC manager screen for sample testing.',
485
+ issue_class: 'feature_request',
486
+ accepted_hypothesis: 'This is net-new feature scope because no QC manager route/module exists.',
487
+ rejected_alternatives: [{ hypothesis: 'Existing QC manager is broken.', why_rejected: 'No QC manager route exists.' }],
488
+ failing_path: 'none_existing route/module',
489
+ owner_files: [
490
+ '/var/app/resolveio-ai-workspace/job/resolveio-all/h2sko/angular/app/dashboard/dashboard.routing.ts',
491
+ '/var/app/resolveio-ai-workspace/job/resolveio-all/h2sko/server/src/client-routes.ts'
492
+ ],
493
+ proof_plan: [{
494
+ action: 'Add QC manager route and persist a sample-testing record.',
495
+ proof_required: 'Saved QC sample record appears in the manager list with pass/fail status.'
496
+ }],
497
+ status: 'passed'
498
+ }
499
+ });
500
+ assert.equal(classifiedFeatureRequestWithoutEvidence.valid, true);
501
+ assert.ok(classifiedFeatureRequestWithoutEvidence.normalized?.evidence.some((entry) => entry.type === 'ticket'));
502
+ assert.ok(classifiedFeatureRequestWithoutEvidence.normalized?.evidence.some((entry) => entry.type === 'code' && entry.ownerFiles?.includes('h2sko/angular/app/dashboard/dashboard.routing.ts')));
503
+
504
+ const malformedStructuredDiagnosisResult = validateResolveIOSupportDiagnosisGate({
505
+ issue_case: {
506
+ reproduction_status: 'reproduced'
507
+ },
508
+ issue_class: 'missing_wrong_data',
509
+ accepted_hypothesis: {
510
+ statement: 'syncChemicalYardInventoryLocations repopulates every chemical yard list.',
511
+ evidence: [{ summary: 'Dry-run proof exists.' }]
512
+ },
513
+ rejected_alternatives: [{ summary: 'Route auth was not the cause.' }],
514
+ failing_path: {
515
+ backend: 'geochem/server/src/methods/chemicals.ts'
516
+ },
517
+ owner_files: [{ path: 'geochem/server/src/methods/chemicals.ts' }],
518
+ proof_plan: {},
519
+ evidence: [{ type: 'mongo' as const, summary: 'Bossier yard before/after dry-run proof.' }],
520
+ status: 'passed'
521
+ });
522
+ assert.equal(malformedStructuredDiagnosisResult.valid, false);
523
+ assert.equal(malformedStructuredDiagnosisResult.status, 'incomplete');
524
+ assert.equal(malformedStructuredDiagnosisResult.normalized?.status, 'incomplete');
525
+ assert.deepEqual(malformedStructuredDiagnosisResult.normalized?.owner_files, ['geochem/server/src/methods/chemicals.ts']);
526
+ assert.equal(malformedStructuredDiagnosisResult.normalized?.owner_files.some((entry) => entry === '[object Object]'), false);
527
+ assert.ok(malformedStructuredDiagnosisResult.blockers.some((blocker) => /issue_case\.customer_complaint/i.test(blocker)));
528
+ assert.ok(malformedStructuredDiagnosisResult.blockers.some((blocker) => /proof_plan\.action/i.test(blocker)));
529
+
530
+ const validProbePlan = buildResolveIOSupportIssueClassProbePlan(validDiagnosis, '2026-06-06T00:00:05.000Z');
531
+ const validProbePlanValidation = validateResolveIOSupportIssueClassProbePlan(validProbePlan, validDiagnosis);
532
+ assert.equal(validProbePlan.status, 'ready');
533
+ assert.equal(validProbePlanValidation.valid, true);
534
+ assert.equal(validProbePlan.activeProbe?.issue_class, validDiagnosis.issue_class);
535
+ assert.equal(validProbePlan.activeProbe?.route, validDiagnosis.proof_plan.route);
536
+ assert.equal(validProbePlan.acceptanceGate, 'aiqa_business_assertion');
537
+ assert.ok(validProbePlan.requiredArtifacts.some((artifact) => /dropdown DOM snapshot/i.test(artifact)));
538
+ assert.ok(validProbePlan.falsePassBlockers.some((blocker) => /route screenshot|filter control visible|route loaded|screenshot/i.test(blocker)));
539
+ assert.equal(validProbePlan.qaRows.length, 1);
540
+ assert.equal(validProbePlan.activeQaRow?.requires_aiqa_business_assertion, true);
541
+ assert.match(validProbePlan.activeQaRow?.expected_dom_or_data_proof || '', /dropdown DOM snapshot|included\/excluded row proof|disabled driver/i);
542
+
543
+ const malformedProbePlan = validateResolveIOSupportIssueClassProbePlan({
544
+ ...validProbePlan,
545
+ probes: [{
546
+ ...(validProbePlan.activeProbe as any),
547
+ route: '',
548
+ required_artifacts: [],
549
+ state_transition: {
550
+ ...(validProbePlan.activeProbe as any).state_transition,
551
+ assertion: ''
552
+ }
553
+ }]
554
+ }, validDiagnosis);
555
+ assert.equal(malformedProbePlan.valid, false);
556
+ assert.ok(malformedProbePlan.blockers.some((blocker) => /route or module/i.test(blocker)));
557
+ assert.ok(malformedProbePlan.blockers.some((blocker) => /at least two proof artifacts/i.test(blocker)));
558
+ assert.ok(malformedProbePlan.blockers.some((blocker) => /assertion is required/i.test(blocker)));
559
+
560
+ const similarCaseHints = selectResolveIOSupportSimilarCaseHints({
561
+ issueClass: validDiagnosis.issue_class,
562
+ ownerFiles: validDiagnosis.owner_files,
563
+ text: 'Disabled drivers should not appear in the BOL dropdown.',
564
+ candidates: [
565
+ {
566
+ source: 'airun',
567
+ id: 'run-accepted-1',
568
+ ticketNumber: '004333',
569
+ title: 'Disabled users leaked into driver dropdown',
570
+ outcome: 'accepted',
571
+ issueClass: 'filter_query_mismatch',
572
+ ownerFiles: ['geochem/server/src/publications/drivers.ts'],
573
+ commitSha: '1111111111111111111111111111111111111111'
574
+ },
575
+ {
576
+ source: 'airun',
577
+ id: 'run-rejected-1',
578
+ ticketNumber: '004334',
579
+ title: 'Rejected guess for dropdown',
580
+ outcome: 'rejected',
581
+ issueClass: 'filter_query_mismatch',
582
+ ownerFiles: ['geochem/server/src/publications/drivers.ts']
583
+ },
584
+ {
585
+ source: 'git_commit',
586
+ commitSha: '2222222222222222222222222222222222222222',
587
+ commitMessage: 'Filter disabled driver records before BOL selection',
588
+ ownerFiles: ['geochem/server/src/publications/drivers.ts']
589
+ },
590
+ {
591
+ source: 'support_ticket',
592
+ ticketNumber: '004335',
593
+ title: 'BOL page wording update',
594
+ outcome: 'accepted',
595
+ issueClass: 'missing_wrong_data',
596
+ ownerFiles: ['geochem/angular/app/bol/new/bol-new.component.ts']
597
+ }
598
+ ],
599
+ now: '2026-06-06T00:00:10.000Z'
600
+ });
601
+ assert.equal(similarCaseHints.ranked[0].ticketNumber, '004333');
602
+ assert.ok(similarCaseHints.ranked[0].structuredSignals.some((signal) => /issue_class/.test(signal)));
603
+ assert.ok(similarCaseHints.ranked[0].structuredSignals.some((signal) => /owner_file_overlap/.test(signal)));
604
+ assert.equal(similarCaseHints.ranked.some((hint) => hint.ticketNumber === '004334'), false);
605
+ assert.equal(similarCaseHints.similarCommits.some((hint) => hint.commitSha === '2222222222222222222222222222222222222222'), true);
606
+ assert.equal(similarCaseHints.ranked.every((hint) => hint.advisoryOnly === true), true);
607
+
608
+ const missingDiagnosisEvidencePack = buildResolveIOSupportDiagnosisEvidencePack({
609
+ bundle: initialized,
610
+ similarCaseHints,
611
+ issueClass: validDiagnosis.issue_class,
612
+ ownerFiles: validDiagnosis.owner_files,
613
+ text: 'Disabled drivers should not appear in the BOL dropdown.',
614
+ now: '2026-06-06T00:00:20.000Z'
615
+ });
616
+ assert.equal(missingDiagnosisEvidencePack.status, 'needs_diagnosis');
617
+ assert.equal(missingDiagnosisEvidencePack.readOnly, true);
618
+ assert.equal(missingDiagnosisEvidencePack.sourceEditsAllowed, false);
619
+ assert.equal(missingDiagnosisEvidencePack.requiredOutputKey, 'support_diagnosis_gate');
620
+ assert.ok(missingDiagnosisEvidencePack.requiredFields.includes('accepted_hypothesis'));
621
+ assert.ok(missingDiagnosisEvidencePack.requiredEvidence.some((entry) => /falsifiable root-cause hypothesis/i.test(entry)));
622
+ assert.ok(missingDiagnosisEvidencePack.forbiddenActions.some((entry) => /similar tickets or commits as proof/i.test(entry)));
623
+ assert.equal(missingDiagnosisEvidencePack.similarCaseSelection.ranked[0].ticketNumber, '004333');
624
+ assert.equal(missingDiagnosisEvidencePack.similarCaseSelection.ranked.every((hint) => hint.advisoryOnly === true), true);
625
+ assert.equal(missingDiagnosisEvidencePack.issueClassProbeCatalog.length, 7);
626
+
627
+ const validDiagnosisEvidencePack = buildResolveIOSupportDiagnosisEvidencePack({
628
+ diagnosisGate: validDiagnosis,
629
+ similarCaseHints,
630
+ now: '2026-06-06T00:00:21.000Z'
631
+ });
632
+ assert.equal(validDiagnosisEvidencePack.status, 'diagnosis_ready');
633
+ assert.equal(validDiagnosisEvidencePack.diagnosisValid, true);
634
+ assert.equal(validDiagnosisEvidencePack.structuredFacts.productRepairBlockedUntilDiagnosisPassed, false);
635
+ assert.ok(validDiagnosisEvidencePack.ownerFileHints.includes('geochem/server/src/publications/drivers.ts'));
636
+
637
+ const weakDiagnosisEvidence = validateResolveIOSupportDiagnosisGate({
638
+ ...validDiagnosis,
639
+ similar_tickets: similarCaseHints.similarTickets,
640
+ similar_commits: similarCaseHints.similarCommits,
641
+ evidence: [{ type: 'ticket' as const, summary: 'Customer says the driver dropdown is wrong.' }]
642
+ });
643
+ assert.equal(weakDiagnosisEvidence.valid, false);
644
+ assert.ok(weakDiagnosisEvidence.blockers.some((blocker) => /root-cause proof/i.test(blocker)));
645
+ assert.ok(weakDiagnosisEvidence.blockers.some((blocker) => /reproduction evidence/i.test(blocker)));
646
+
647
+ const unartifactedRuntimeEvidence = validateResolveIOSupportDiagnosisGate({
648
+ ...validDiagnosis,
649
+ evidence: [
650
+ { type: 'ticket' as const, summary: 'Ticket reports disabled driver selection.' },
651
+ { type: 'browser' as const, summary: 'Browser reproduced the disabled driver selection.' },
652
+ { type: 'code' as const, summary: 'Publication lacks disabled-user filter.' }
653
+ ]
654
+ });
655
+ assert.equal(unartifactedRuntimeEvidence.valid, false);
656
+ assert.ok(unartifactedRuntimeEvidence.blockers.some((blocker) => /artifactPath/i.test(blocker)));
657
+
658
+ const routeOnlyReproductionDiagnosis = validateResolveIOSupportDiagnosisGate({
659
+ ...validDiagnosis,
660
+ evidence: [
661
+ { type: 'ticket' as const, summary: 'Ticket reports disabled driver selection in BOL workflow.' },
662
+ { type: 'browser' as const, summary: 'Route loaded and screenshot captured.', artifactPath: 'qa-artifacts/004999-route-loaded.png' },
663
+ { type: 'code' as const, summary: 'Publication lacks disabled-user filter.' }
664
+ ]
665
+ });
666
+ assert.equal(routeOnlyReproductionDiagnosis.valid, false);
667
+ assert.equal(routeOnlyReproductionDiagnosis.evidenceQuality?.routeOnlyReproductionEvidenceCount, 1);
668
+ assert.equal(routeOnlyReproductionDiagnosis.evidenceQuality?.businessReproductionEvidenceCount, 0);
669
+ assert.ok(routeOnlyReproductionDiagnosis.blockers.some((blocker) => /route-load or screenshot-only/i.test(blocker)));
670
+ assert.ok(routeOnlyReproductionDiagnosis.proofMatrix?.blockers.some((blocker) => /business reproduction evidence/i.test(blocker)));
671
+
672
+ const classifiedCodeOnlyEvidence = validateResolveIOSupportDiagnosisGate({
673
+ ...validDiagnosis,
674
+ issue_case: {
675
+ ...validDiagnosis.issue_case,
676
+ reproduction_status: 'classified' as const
677
+ },
678
+ evidence: [
679
+ { type: 'ticket' as const, summary: 'Ticket gives a complete failing data example.' },
680
+ { type: 'code' as const, summary: 'Query inspection found the disabled-driver predicate missing.' }
681
+ ]
682
+ });
683
+ assert.equal(classifiedCodeOnlyEvidence.valid, true);
684
+ assert.equal(evaluateResolveIOSupportDiagnosisEvidenceQuality(classifiedCodeOnlyEvidence.normalized).hasRootCauseEvidence, true);
685
+
686
+ const structuredOwnerEvidenceDiagnosis = validateResolveIOSupportDiagnosisGate({
687
+ ...validDiagnosis,
688
+ failing_path: {
689
+ ...validDiagnosis.failing_path,
690
+ frontend: 'Driver select consumes candidate records.',
691
+ backend: 'Driver publication produces the candidate list.'
692
+ },
693
+ evidence: [
694
+ { type: 'ticket' as const, summary: 'Ticket reports disabled driver selection in BOL workflow.' },
695
+ { type: 'qa' as const, summary: 'Browser QA reproduced disabled driver selection.', artifactPath: 'qa-artifacts/004999-disabled-driver-before.json' },
696
+ { type: 'code' as const, summary: 'Frontend driver select owner file inspected.', ownerFiles: ['geochem/angular/app/bol/new/bol-new.component.ts'] },
697
+ { type: 'code' as const, summary: 'Backend driver publication owner file inspected.', ownerFiles: ['geochem/server/src/publications/drivers.ts'] }
698
+ ]
699
+ });
700
+ assert.equal(structuredOwnerEvidenceDiagnosis.valid, true);
701
+ assert.equal(structuredOwnerEvidenceDiagnosis.proofMatrix?.ownerFileCoverage.every((entry) => entry.covered), true);
702
+
703
+ const unsupportedOwnerProofMatrix = buildResolveIOSupportDiagnosisProofMatrix({
704
+ ...validDiagnosis,
705
+ owner_files: [
706
+ ...validDiagnosis.owner_files,
707
+ 'geochem/server/src/methods/unrelated-driver-cleanup.ts'
708
+ ]
709
+ });
710
+ assert.equal(unsupportedOwnerProofMatrix.status, 'incomplete');
711
+ assert.ok(unsupportedOwnerProofMatrix.blockers.some((blocker) => /not tied to failing_path or structured evidence ownerFiles/i.test(blocker)));
712
+
713
+ const unsupportedOwnerDiagnosis = validateResolveIOSupportDiagnosisGate({
714
+ ...validDiagnosis,
715
+ owner_files: [
716
+ ...validDiagnosis.owner_files,
717
+ 'geochem/server/src/methods/unrelated-driver-cleanup.ts'
718
+ ]
719
+ });
720
+ assert.equal(unsupportedOwnerDiagnosis.valid, false);
721
+ assert.ok(unsupportedOwnerDiagnosis.blockers.some((blocker) => /Proof matrix owner_files/i.test(blocker)));
722
+
723
+ const ownerScopedRepairContract = buildResolveIOSupportOwnerScopedRepairContract({
724
+ diagnosisGate: validDiagnosis,
725
+ changedFiles: validDiagnosis.owner_files,
726
+ now: '2026-06-06T00:00:12.000Z'
727
+ });
728
+ assert.equal(ownerScopedRepairContract.status, 'ready');
729
+ assert.equal(ownerScopedRepairContract.sourceEditsAllowed, true);
730
+ assert.equal(ownerScopedRepairContract.canRunWithoutCodexMonitor, true);
731
+ assert.deepEqual(ownerScopedRepairContract.ownerFiles, validDiagnosis.owner_files);
732
+ assert.equal(ownerScopedRepairContract.patchUnits.length, validDiagnosis.owner_files.length);
733
+ assert.ok(ownerScopedRepairContract.requiredEvidenceAfterRepair.some((entry) => /AIQaBusinessAssertion/i.test(entry)));
734
+ assert.ok(ownerScopedRepairContract.forbiddenActions.some((entry) => /outside owner_files/i.test(entry)));
735
+
736
+ const pricingDiagnosisWithProjectRootChangedFile = {
737
+ issue_case: {
738
+ customer_complaint: 'Pricing upload completes, but the import does not attach every expected pricing row.',
739
+ expected_result: 'Imported pricing rows are saved and attached to the pricing workflow after upload.',
740
+ observed_result: 'Upload succeeds, but only part of the pricing data is attached/imported.',
741
+ route_module: '/dashboard/manage/pricing',
742
+ account_customer_context: '004345 production pricing import templates',
743
+ reproduction_status: 'reproduced' as const
744
+ },
745
+ issue_class: 'upload_import' as const,
746
+ accepted_hypothesis: {
747
+ statement: 'The production pricing import view and backend import method disagree on imported-row attachment/count handling.',
748
+ falsifiable_test: 'Run the uploaded pricing template through the production pricing import flow and compare attached rows to the persisted import result.',
749
+ evidence: [
750
+ 'Ticket 004345 reports upload now works but importing/attaching every row is still incomplete.',
751
+ 'Production pricing import view and pricing method are the only files on the verified upload/import path.'
752
+ ]
753
+ },
754
+ rejected_alternatives: ['Route/auth failure rejected because the pricing route loads and upload completes.', 'Frac/one-off pricing views rejected because the complaint is production pricing import.'],
755
+ failing_path: {
756
+ frontend: 'angular/app/widgets/pricing/sale/production/viewcustomer/pricing-sale-production-viewcustomer.component.ts import action',
757
+ backend: 'server/src/methods/pricing.ts import persistence',
758
+ description: 'Production pricing import flow dispatches to backend pricing import persistence and then shows/attaches imported rows.'
759
+ },
760
+ owner_files: [
761
+ 'angular/app/widgets/pricing/sale/production/viewcustomer/pricing-sale-production-viewcustomer.component.ts',
762
+ 'server/src/methods/pricing.ts'
763
+ ],
764
+ proof_plan: {
765
+ before: 'Run the attached production pricing template and observe that imported/attached row count is incomplete.',
766
+ action: 'Upload and import the production pricing template from the pricing view.',
767
+ after: 'Every valid uploaded pricing row is attached and visible/persisted in the pricing import result.',
768
+ business_assertion: 'Production pricing import attaches every valid uploaded row and reports the persisted imported count.',
769
+ route: '/dashboard/manage/pricing',
770
+ data_assertion: 'Mongo persisted pricing rows for the import match the import result row count.',
771
+ business_proof_contract: {
772
+ issue_class: 'upload_import' as const,
773
+ setup_state: 'Production pricing template contains multiple valid rows to import.',
774
+ action_under_test: 'Upload and import the production pricing template.',
775
+ expected_business_state_change: 'Every valid uploaded row is attached/persisted and the import result count matches persisted rows.',
776
+ prohibited_false_pass: 'Upload success, route load, screenshot, or scorecard alone is not acceptance.',
777
+ proof_artifacts: ['004345 import result JSON', '004345 Mongo pricing row delta', '004345 pricing import screenshot'],
778
+ data_or_dom_assertion: 'Imported row count equals persisted pricing row count and the imported rows are visible in the pricing workflow.'
779
+ }
780
+ },
781
+ evidence: [
782
+ { type: 'ticket' as const, summary: 'Ticket 004345 says upload works but not all pricing import data attaches.' },
783
+ {
784
+ type: 'code' as const,
785
+ summary: 'Production pricing view owns the upload/import action.',
786
+ ownerFiles: ['angular/app/widgets/pricing/sale/production/viewcustomer/pricing-sale-production-viewcustomer.component.ts']
787
+ },
788
+ {
789
+ type: 'code' as const,
790
+ summary: 'Pricing method owns import persistence and row count.',
791
+ ownerFiles: ['server/src/methods/pricing.ts']
792
+ },
793
+ { type: 'qa' as const, summary: 'Runtime import proof artifact compares uploaded rows to persisted pricing rows.', artifactPath: 'qa-artifacts/004345-production-pricing-import-proof.json' }
794
+ ],
795
+ status: 'passed' as const
796
+ };
797
+ const pricingProjectRootDiff = buildResolveIOSupportOwnerScopedRepairContract({
798
+ diagnosisGate: pricingDiagnosisWithProjectRootChangedFile,
799
+ changedFiles: [
800
+ 'geochem/angular/app/widgets/pricing/sale/production/viewcustomer/pricing-sale-production-viewcustomer.component.ts',
801
+ 'geochem/server/src/methods/pricing.ts'
802
+ ],
803
+ now: '2026-06-06T00:00:12.500Z'
804
+ });
805
+ assert.equal(pricingProjectRootDiff.status, 'ready');
806
+ assert.deepEqual(pricingProjectRootDiff.outsideOwnerFiles, []);
807
+
808
+ const blockedOwnerScopedRepairContract = buildResolveIOSupportOwnerScopedRepairContract({
809
+ diagnosisGate: validDiagnosis,
810
+ changedFiles: [
811
+ ...validDiagnosis.owner_files,
812
+ 'geochem/server/src/methods/unrelated-driver-cleanup.ts'
813
+ ],
814
+ now: '2026-06-06T00:00:13.000Z'
815
+ });
816
+ assert.equal(blockedOwnerScopedRepairContract.status, 'blocked');
817
+ assert.equal(blockedOwnerScopedRepairContract.sourceEditsAllowed, false);
818
+ assert.ok(blockedOwnerScopedRepairContract.outsideOwnerFiles.includes('geochem/server/src/methods/unrelated-driver-cleanup.ts'));
819
+ assert.ok(blockedOwnerScopedRepairContract.blockers.some((blocker) => /outside owner_files/i.test(blocker)));
820
+
821
+ const blockedDiagnosis = validateResolveIOSupportDiagnosisGate({
822
+ ...validDiagnosis,
823
+ accepted_hypothesis: { statement: '', falsifiable_test: '', evidence: [] },
824
+ owner_files: ['geochem/angular/app']
825
+ });
826
+ assert.equal(blockedDiagnosis.valid, false);
827
+ assert.ok(blockedDiagnosis.blockers.some((blocker) => /accepted_hypothesis\.statement/.test(blocker)));
828
+ assert.ok(blockedDiagnosis.blockers.some((blocker) => /broad or unsafe/.test(blocker)));
829
+
830
+ const placeholderDiagnosis = validateResolveIOSupportDiagnosisGate({
831
+ ...validDiagnosis,
832
+ issue_case: {
833
+ ...validDiagnosis.issue_case,
834
+ expected_result: 'Should work',
835
+ observed_result: 'Not working'
836
+ },
837
+ accepted_hypothesis: {
838
+ statement: 'There is probably a bug.',
839
+ falsifiable_test: 'Test the issue.',
840
+ evidence: ['Something is wrong.']
841
+ },
842
+ rejected_alternatives: ['Not route.'],
843
+ proof_plan: {
844
+ ...validDiagnosis.proof_plan,
845
+ before: 'Before state.',
846
+ action: 'Test the ticket.',
847
+ after: 'It works.',
848
+ business_assertion: 'Issue is fixed.',
849
+ business_proof_contract: {
850
+ ...validDiagnosis.proof_plan.business_proof_contract,
851
+ setup_state: 'Before state.',
852
+ action_under_test: 'Test the issue.',
853
+ expected_business_state_change: 'It works.',
854
+ data_or_dom_assertion: 'Looks right.'
855
+ }
856
+ }
857
+ });
858
+ assert.equal(placeholderDiagnosis.valid, false);
859
+ assert.ok(placeholderDiagnosis.blockers.some((blocker) => /too generic|placeholder-like/i.test(blocker)));
860
+ assert.ok(placeholderDiagnosis.blockers.some((blocker) => /accepted_hypothesis\.statement/i.test(blocker)));
861
+ assert.ok(placeholderDiagnosis.blockers.some((blocker) => /proof_plan\.business_assertion/i.test(blocker)));
862
+
863
+ const nonProductOwnerDiagnosis = validateResolveIOSupportDiagnosisGate({
864
+ ...validDiagnosis,
865
+ owner_files: [
866
+ 'qa-artifacts/disabled-driver-before.png',
867
+ 'geochem/server/tests/drivers.test.ts'
868
+ ]
869
+ });
870
+ assert.equal(nonProductOwnerDiagnosis.valid, false);
871
+ assert.ok(nonProductOwnerDiagnosis.blockers.some((blocker) => /not tests\/docs\/artifacts\/logs\/screenshots/i.test(blocker)));
872
+
873
+ const diagnosisPackOwnerDiagnosis = validateResolveIOSupportDiagnosisGate({
874
+ ...validDiagnosis,
875
+ owner_files: [
876
+ '/var/app/resolveio-ai-workspace/job-1/resolveio-all/geochem/support-diagnosis-pack-abcd1234.json'
877
+ ],
878
+ failing_path: {
879
+ ...validDiagnosis.failing_path,
880
+ backend: '/var/app/resolveio-ai-workspace/job-1/resolveio-all/geochem/support-diagnosis-pack-abcd1234.json'
881
+ },
882
+ evidence: [
883
+ ...validDiagnosis.evidence,
884
+ {
885
+ type: 'other' as const,
886
+ summary: 'Diagnosis pack path is evidence context, not an editable product owner file.',
887
+ artifactPath: '/var/app/resolveio-ai-workspace/job-1/resolveio-all/geochem/support-diagnosis-pack-abcd1234.json'
888
+ }
889
+ ]
890
+ });
891
+ assert.equal(diagnosisPackOwnerDiagnosis.valid, false);
892
+ assert.ok(diagnosisPackOwnerDiagnosis.blockers.some((blocker) => /broad or unsafe path|not tests\/docs\/artifacts\/logs\/screenshots/i.test(blocker)));
893
+
894
+ const vagueArtifactDiagnosis = validateResolveIOSupportDiagnosisGate({
895
+ ...validDiagnosis,
896
+ proof_plan: {
897
+ ...validDiagnosis.proof_plan,
898
+ business_proof_contract: {
899
+ ...validDiagnosis.proof_plan.business_proof_contract,
900
+ proof_artifacts: ['screenshot', 'browser trace']
901
+ }
902
+ }
903
+ });
904
+ assert.equal(vagueArtifactDiagnosis.valid, false);
905
+ assert.ok(vagueArtifactDiagnosis.blockers.some((blocker) => /proof_artifacts are too generic/i.test(blocker)));
906
+
907
+ const routeOnlyDiagnosis = validateResolveIOSupportDiagnosisGate({
908
+ ...validDiagnosis,
909
+ proof_plan: {
910
+ before: 'The affected route is available.',
911
+ action: 'Open the support ticket route.',
912
+ after: 'The page loads and is visible.',
913
+ business_assertion: 'The route loads and the page is visible.',
914
+ route: '/support'
915
+ }
916
+ });
917
+ assert.equal(routeOnlyDiagnosis.valid, false);
918
+ assert.ok(routeOnlyDiagnosis.blockers.some((blocker) => /route-load|screenshot-only|business state/i.test(blocker)));
919
+
920
+ const missingProofContractDiagnosis = validateResolveIOSupportDiagnosisGate({
921
+ ...validDiagnosis,
922
+ proof_plan: {
923
+ ...validDiagnosis.proof_plan,
924
+ business_proof_contract: undefined
925
+ }
926
+ });
927
+ assert.equal(missingProofContractDiagnosis.valid, false);
928
+ assert.ok(missingProofContractDiagnosis.blockers.some((blocker) => /business_proof_contract is required/.test(blocker)));
929
+
930
+ const mismatchedProofContractDiagnosis = validateResolveIOSupportDiagnosisGate({
931
+ ...validDiagnosis,
932
+ proof_plan: {
933
+ ...validDiagnosis.proof_plan,
934
+ business_proof_contract: {
935
+ ...validDiagnosis.proof_plan.business_proof_contract,
936
+ issue_class: 'upload_import' as const
937
+ }
938
+ }
939
+ });
940
+ assert.equal(mismatchedProofContractDiagnosis.valid, false);
941
+ assert.ok(mismatchedProofContractDiagnosis.blockers.some((blocker) => /must match diagnosis issue_class/.test(blocker)));
942
+
943
+ const safeCustomerReplyPolicy = decideResolveIOSupportCustomerReplyPolicy({
944
+ diagnosisGate: validDiagnosis,
945
+ outcomeLabel: 'accepted',
946
+ confidence: { level: 'high', category: 'repo_fix', shouldBlockPr: false },
947
+ businessAssertionStatus: 'pass',
948
+ businessProofArtifacts: ['qa-artifacts/disabled-driver-dropdown.png'],
949
+ releaseStatus: 'completed'
950
+ });
951
+ assert.equal(safeCustomerReplyPolicy.action, 'draft_resolution_reply');
952
+ assert.equal(safeCustomerReplyPolicy.canDraftCustomerReply, true);
953
+ assert.equal(safeCustomerReplyPolicy.canSendCustomerReply, false);
954
+ assert.equal(safeCustomerReplyPolicy.draftBasis?.issueClass, 'filter_query_mismatch');
955
+ assert.ok(safeCustomerReplyPolicy.draftBasis?.issueClassProbePlanId);
956
+ assert.equal(safeCustomerReplyPolicy.draftBasis?.issueClassProbePlanStatus, 'ready');
957
+ assert.equal(safeCustomerReplyPolicy.humanReviewPacket?.reviewType, 'customer_resolution_reply');
958
+ assert.equal(safeCustomerReplyPolicy.humanReviewPacket?.primaryAction, 'review_customer_reply');
959
+ assert.equal(safeCustomerReplyPolicy.humanReviewPacket?.customerFacingDraftAllowed, true);
960
+ assert.equal(safeCustomerReplyPolicy.humanReviewPacket?.customerSendAllowed, false);
961
+ assert.equal(safeCustomerReplyPolicy.humanReviewPacket?.requiresHumanApproval, true);
962
+ assert.equal(safeCustomerReplyPolicy.readinessContract?.status, 'draft_ready');
963
+ assert.equal(safeCustomerReplyPolicy.readinessContract?.primaryCommand, 'review_customer_reply');
964
+ assert.equal(safeCustomerReplyPolicy.readinessContract?.canDraftCustomerReply, true);
965
+ assert.equal(safeCustomerReplyPolicy.readinessContract?.canSendCustomerReply, false);
966
+ assert.equal(safeCustomerReplyPolicy.readinessContract?.requiresHumanApproval, true);
967
+ assert.equal(safeCustomerReplyPolicy.readinessContract?.proofBacked, true);
968
+ assert.equal(validateResolveIOSupportCustomerReplyReadinessContract(safeCustomerReplyPolicy.readinessContract).valid, true);
969
+ const unsafeAutoSendReadiness = validateResolveIOSupportCustomerReplyReadinessContract({
970
+ ...safeCustomerReplyPolicy.readinessContract,
971
+ canSendCustomerReply: true
972
+ });
973
+ assert.equal(unsafeAutoSendReadiness.valid, false);
974
+ assert.ok(unsafeAutoSendReadiness.blockers.some((blocker) => /never allow automatic customer send/i.test(blocker)));
975
+
976
+ const malformedProbeReplyPolicy = decideResolveIOSupportCustomerReplyPolicy({
977
+ diagnosisGate: validDiagnosis,
978
+ issueClassProbePlan: malformedProbePlan.normalized,
979
+ outcomeLabel: 'accepted',
980
+ confidence: { level: 'high', category: 'repo_fix', shouldBlockPr: false },
981
+ businessAssertionStatus: 'pass',
982
+ businessProofArtifacts: ['qa-artifacts/disabled-driver-dropdown.png'],
983
+ releaseStatus: 'completed'
984
+ });
985
+ assert.equal(malformedProbeReplyPolicy.action, 'hold_internal');
986
+ assert.equal(malformedProbeReplyPolicy.canDraftCustomerReply, false);
987
+ assert.equal(malformedProbeReplyPolicy.reason, 'support_reply_requires_valid_issue_class_probe_plan');
988
+ assert.equal(malformedProbeReplyPolicy.humanReviewPacket?.primaryAction, 'repair_support_issue_class_probe_plan');
989
+ assert.ok(malformedProbeReplyPolicy.humanReviewPacket?.blockers.some((blocker) => /Issue-class probe/i.test(blocker)));
990
+
991
+ const missingHotfixProofReplyPolicy = decideResolveIOSupportCustomerReplyPolicy({
992
+ diagnosisGate: validDiagnosis,
993
+ outcomeLabel: 'accepted',
994
+ confidence: { level: 'high', category: 'repo_fix', shouldBlockPr: false },
995
+ businessAssertionStatus: 'pass',
996
+ businessProofArtifacts: ['qa-artifacts/disabled-driver-dropdown.png'],
997
+ releaseStatus: 'completed',
998
+ releaseEvidence: {
999
+ hotfix_commit_proof: {
1000
+ required: true,
1001
+ blocked: true,
1002
+ passed: false,
1003
+ missing_fields: ['gitCommitStatus=passed']
1004
+ },
1005
+ hotfix_evidence: {
1006
+ status: 'incomplete',
1007
+ manager_must_commit_before_hotfix: true,
1008
+ missing_fields: ['gitCommitStatus=passed']
1009
+ }
1010
+ }
1011
+ });
1012
+ assert.equal(missingHotfixProofReplyPolicy.action, 'hold_internal');
1013
+ assert.equal(missingHotfixProofReplyPolicy.canDraftCustomerReply, false);
1014
+ assert.equal(missingHotfixProofReplyPolicy.reason, 'support_reply_blocked_until_release_or_hotfix_gate_finishes');
1015
+ assert.equal(missingHotfixProofReplyPolicy.humanReviewPacket?.primaryAction, 'repair_release_hotfix_first');
1016
+ assert.ok(missingHotfixProofReplyPolicy.humanReviewPacket?.blockers.some((blocker) => /hotfix_commit_proof|gitCommitStatus/i.test(blocker)));
1017
+ assert.ok(missingHotfixProofReplyPolicy.requiredEvidence.some((entry) => /gitCommitStatus=passed/i.test(entry)));
1018
+ assert.equal(missingHotfixProofReplyPolicy.readinessContract?.status, 'blocked_until_release_hotfix');
1019
+ assert.equal(missingHotfixProofReplyPolicy.readinessContract?.primaryCommand, 'repair_release_hotfix_first');
1020
+ assert.equal(missingHotfixProofReplyPolicy.readinessContract?.canDraftCustomerReply, false);
1021
+ assert.equal(missingHotfixProofReplyPolicy.readinessContract?.releaseReady, false);
1022
+ assert.equal(validateResolveIOSupportCustomerReplyReadinessContract(missingHotfixProofReplyPolicy.readinessContract).valid, true);
1023
+
1024
+ const matchedBusinessProofReadiness = evaluateResolveIOSupportBusinessProofReadiness({
1025
+ diagnosisGate: validDiagnosis,
1026
+ businessAssertions: [{
1027
+ assertion: validDiagnosis.proof_plan.business_assertion,
1028
+ status: 'pass',
1029
+ workflow: validDiagnosis.proof_plan.business_proof_contract.action_under_test,
1030
+ action: validDiagnosis.proof_plan.action,
1031
+ expected: validDiagnosis.proof_plan.business_proof_contract.expected_business_state_change,
1032
+ observed: 'The disabled driver option is absent and the active driver remains selected.',
1033
+ dataProof: validDiagnosis.proof_plan.business_proof_contract.data_or_dom_assertion,
1034
+ artifactPaths: ['qa-artifacts/disabled-driver-business-proof.json'],
1035
+ metadata: { supportDiagnosisProof: true }
1036
+ }]
1037
+ });
1038
+ assert.equal(matchedBusinessProofReadiness.ready, true);
1039
+ assert.equal(matchedBusinessProofReadiness.status, 'passed');
1040
+ assert.equal(matchedBusinessProofReadiness.artifactPaths[0], 'qa-artifacts/disabled-driver-business-proof.json');
1041
+ assert.ok(matchedBusinessProofReadiness.proofFingerprint);
1042
+ assert.ok(matchedBusinessProofReadiness.artifactFingerprint);
1043
+ assert.equal(matchedBusinessProofReadiness.proofFreshness, 'fresh');
1044
+
1045
+ const spoofedMetadataBusinessProofReadiness = evaluateResolveIOSupportBusinessProofReadiness({
1046
+ diagnosisGate: validDiagnosis,
1047
+ businessAssertions: [{
1048
+ assertion: 'The route loaded and the page looks correct.',
1049
+ status: 'pass',
1050
+ workflow: 'Open the BOL route.',
1051
+ route: validDiagnosis.proof_plan.route,
1052
+ action: 'Open the route.',
1053
+ expected: 'The page is visible.',
1054
+ observed: 'The page rendered.',
1055
+ dataProof: 'Screenshot captured.',
1056
+ artifactPaths: ['qa-artifacts/generic-route-proof.png'],
1057
+ metadata: { supportDiagnosisProof: true }
1058
+ }]
1059
+ });
1060
+ assert.equal(spoofedMetadataBusinessProofReadiness.ready, false);
1061
+ assert.equal(spoofedMetadataBusinessProofReadiness.status, 'route_only');
1062
+ assert.equal(spoofedMetadataBusinessProofReadiness.reason, 'support_business_proof_route_only_or_acceptance_blocked');
1063
+ assert.ok(spoofedMetadataBusinessProofReadiness.blockers.some((blocker) => /Route probe|shell\/screenshot/i.test(blocker)));
1064
+
1065
+ const staleBusinessProofReadiness = evaluateResolveIOSupportBusinessProofReadiness({
1066
+ diagnosisGate: validDiagnosis,
1067
+ previousProofFingerprint: matchedBusinessProofReadiness.proofFingerprint,
1068
+ businessAssertions: [{
1069
+ assertion: validDiagnosis.proof_plan.business_assertion,
1070
+ status: 'pass',
1071
+ workflow: validDiagnosis.proof_plan.business_proof_contract.action_under_test,
1072
+ action: validDiagnosis.proof_plan.action,
1073
+ expected: validDiagnosis.proof_plan.business_proof_contract.expected_business_state_change,
1074
+ observed: 'The disabled driver option is absent and the active driver remains selected.',
1075
+ dataProof: validDiagnosis.proof_plan.business_proof_contract.data_or_dom_assertion,
1076
+ artifactPaths: ['qa-artifacts/disabled-driver-business-proof.json'],
1077
+ metadata: { supportDiagnosisProof: true }
1078
+ }]
1079
+ });
1080
+ assert.equal(staleBusinessProofReadiness.ready, false);
1081
+ assert.equal(staleBusinessProofReadiness.status, 'stale');
1082
+ assert.equal(staleBusinessProofReadiness.proofFreshness, 'same_as_previous');
1083
+ assert.equal(staleBusinessProofReadiness.reason, 'support_business_proof_same_fingerprint_as_previous');
1084
+
1085
+ const routeOnlyBusinessProofReadiness = evaluateResolveIOSupportBusinessProofReadiness({
1086
+ diagnosisGate: validDiagnosis,
1087
+ outcomeLabel: 'accepted',
1088
+ businessAssertionStatus: 'route_probe_pass',
1089
+ businessProofArtifacts: ['qa-artifacts/route-loaded.png']
1090
+ });
1091
+ assert.equal(routeOnlyBusinessProofReadiness.ready, false);
1092
+ assert.equal(routeOnlyBusinessProofReadiness.status, 'route_only');
1093
+ assert.ok(routeOnlyBusinessProofReadiness.blockers.some((blocker) => /Route probe/i.test(blocker)));
1094
+
1095
+ const blockedAssertionReadiness = evaluateResolveIOSupportBusinessProofReadiness({
1096
+ diagnosisGate: validDiagnosis,
1097
+ businessAssertions: [{
1098
+ assertion: 'The route loaded.',
1099
+ status: 'pass',
1100
+ artifactPaths: ['qa-artifacts/route-loaded.png'],
1101
+ acceptanceBlocked: true,
1102
+ metadata: { routeOnly: true }
1103
+ }]
1104
+ });
1105
+ assert.equal(blockedAssertionReadiness.ready, false);
1106
+ assert.equal(blockedAssertionReadiness.reason, 'support_business_proof_route_only_or_acceptance_blocked');
1107
+
1108
+ const routeOnlyReplyPolicy = decideResolveIOSupportCustomerReplyPolicy({
1109
+ diagnosisGate: validDiagnosis,
1110
+ outcomeLabel: 'accepted',
1111
+ confidence: { level: 'high', category: 'repo_fix', shouldBlockPr: false },
1112
+ businessAssertionStatus: 'route_probe_pass',
1113
+ businessProofArtifacts: ['qa-artifacts/route-loaded.png'],
1114
+ releaseStatus: 'completed'
1115
+ });
1116
+ assert.equal(routeOnlyReplyPolicy.action, 'hold_internal');
1117
+ assert.equal(routeOnlyReplyPolicy.reason, 'support_reply_rejects_route_only_business_proof');
1118
+ assert.equal(routeOnlyReplyPolicy.readinessContract?.status, 'blocked_until_business_proof');
1119
+ assert.equal(routeOnlyReplyPolicy.readinessContract?.primaryCommand, 'run_support_v5_business_proof_qa_row');
1120
+ assert.equal(routeOnlyReplyPolicy.readinessContract?.canDraftCustomerReply, false);
1121
+ assert.equal(validateResolveIOSupportCustomerReplyReadinessContract(routeOnlyReplyPolicy.readinessContract).valid, true);
1122
+
1123
+ const spoofedMetadataReplyPolicy = decideResolveIOSupportCustomerReplyPolicy({
1124
+ diagnosisGate: validDiagnosis,
1125
+ outcomeLabel: 'accepted',
1126
+ confidence: { level: 'high', category: 'business_proof', shouldBlockPr: false },
1127
+ businessAssertions: [{
1128
+ assertion: 'The route loaded and the page looks correct.',
1129
+ status: 'pass',
1130
+ workflow: 'Open the BOL route.',
1131
+ route: validDiagnosis.proof_plan.route,
1132
+ action: 'Open the route.',
1133
+ expected: 'The page is visible.',
1134
+ observed: 'The page rendered.',
1135
+ dataProof: 'Screenshot captured.',
1136
+ artifactPaths: ['qa-artifacts/generic-route-proof.png'],
1137
+ metadata: { supportDiagnosisProof: true }
1138
+ }],
1139
+ releaseStatus: 'completed'
1140
+ });
1141
+ assert.equal(spoofedMetadataReplyPolicy.action, 'hold_internal');
1142
+ assert.equal(spoofedMetadataReplyPolicy.reason, 'support_reply_rejects_route_only_business_proof');
1143
+ assert.equal(spoofedMetadataReplyPolicy.readinessContract?.canDraftCustomerReply, false);
1144
+ assert.equal(spoofedMetadataReplyPolicy.readinessContract?.status, 'blocked_until_business_proof');
1145
+ assert.ok(spoofedMetadataReplyPolicy.humanReviewPacket?.blockers.some((blocker) => /Route probe|shell\/screenshot/i.test(blocker)));
1146
+
1147
+ const staleReplyPolicy = decideResolveIOSupportCustomerReplyPolicy({
1148
+ diagnosisGate: validDiagnosis,
1149
+ outcomeLabel: 'accepted',
1150
+ confidence: { level: 'high', category: 'repo_fix', shouldBlockPr: false },
1151
+ businessAssertions: [{
1152
+ assertion: validDiagnosis.proof_plan.business_assertion,
1153
+ status: 'pass',
1154
+ workflow: validDiagnosis.proof_plan.business_proof_contract.action_under_test,
1155
+ action: validDiagnosis.proof_plan.action,
1156
+ expected: validDiagnosis.proof_plan.business_proof_contract.expected_business_state_change,
1157
+ observed: 'The disabled driver option is absent and the active driver remains selected.',
1158
+ dataProof: validDiagnosis.proof_plan.business_proof_contract.data_or_dom_assertion,
1159
+ artifactPaths: ['qa-artifacts/disabled-driver-business-proof.json'],
1160
+ metadata: { supportDiagnosisProof: true }
1161
+ }],
1162
+ previousProofFingerprint: matchedBusinessProofReadiness.proofFingerprint,
1163
+ releaseStatus: 'completed'
1164
+ });
1165
+ assert.equal(staleReplyPolicy.action, 'hold_internal');
1166
+ assert.equal(staleReplyPolicy.reason, 'support_reply_requires_business_assertion_pass');
1167
+ assert.ok(staleReplyPolicy.humanReviewPacket?.blockers.some((blocker) => /new proof\/artifact fingerprint/i.test(blocker)));
1168
+
1169
+ const lowConfidenceReplyPolicy = decideResolveIOSupportCustomerReplyPolicy({
1170
+ diagnosisGate: validDiagnosis,
1171
+ outcomeLabel: 'accepted',
1172
+ confidence: { level: 'medium', category: 'repo_fix', shouldBlockPr: false },
1173
+ businessAssertionStatus: 'pass',
1174
+ businessProofArtifacts: ['qa-artifacts/disabled-driver-dropdown.png'],
1175
+ releaseStatus: 'completed'
1176
+ });
1177
+ assert.equal(lowConfidenceReplyPolicy.action, 'hold_internal');
1178
+ assert.equal(lowConfidenceReplyPolicy.reason, 'support_reply_requires_high_confidence');
1179
+
1180
+ const clarificationReplyPolicy = decideResolveIOSupportCustomerReplyPolicy({
1181
+ diagnosisGate: {
1182
+ ...validDiagnosis,
1183
+ issue_case: {
1184
+ ...validDiagnosis.issue_case,
1185
+ reproduction_status: 'blocked' as const,
1186
+ reproduction_blocker: 'Need the exact BOL number where the disabled driver appears.'
1187
+ },
1188
+ accepted_hypothesis: { statement: '', falsifiable_test: '', evidence: [] }
1189
+ },
1190
+ confidence: { level: 'low' }
1191
+ });
1192
+ assert.equal(clarificationReplyPolicy.action, 'ask_clarification');
1193
+ assert.equal(clarificationReplyPolicy.canSendCustomerReply, false);
1194
+ assert.match(clarificationReplyPolicy.clarificationQuestion || '', /exact BOL number/);
1195
+ assert.equal(clarificationReplyPolicy.clarificationContract?.status, 'ready');
1196
+ assert.equal(clarificationReplyPolicy.clarificationContract?.missingField, 'reproduction_blocker');
1197
+ assert.equal(clarificationReplyPolicy.clarificationContract?.oneQuestionOnly, true);
1198
+ assert.equal(clarificationReplyPolicy.clarificationContract?.customerSendAllowed, false);
1199
+ assert.equal(clarificationReplyPolicy.clarificationContract?.parksTicketUntilCustomerReply, true);
1200
+ assert.ok(clarificationReplyPolicy.clarificationContract?.nextCommands.includes('park_ticket_until_customer_reply'));
1201
+ assert.equal(clarificationReplyPolicy.humanReviewPacket?.reviewType, 'customer_clarification');
1202
+ assert.equal(clarificationReplyPolicy.humanReviewPacket?.primaryAction, 'review_customer_clarification');
1203
+ assert.match(clarificationReplyPolicy.humanReviewPacket?.question || '', /exact BOL number/);
1204
+ assert.equal(clarificationReplyPolicy.humanReviewPacket?.customerSendAllowed, false);
1205
+ assert.ok(clarificationReplyPolicy.humanReviewPacket?.forbiddenActions.some((action) => /guessed reproduction path/i.test(action)));
1206
+ assert.equal(clarificationReplyPolicy.readinessContract?.status, 'clarification_required');
1207
+ assert.equal(clarificationReplyPolicy.readinessContract?.primaryCommand, 'review_customer_clarification');
1208
+ assert.equal(clarificationReplyPolicy.readinessContract?.clarificationRequired, true);
1209
+ assert.equal(clarificationReplyPolicy.readinessContract?.canSendCustomerReply, false);
1210
+ assert.equal(validateResolveIOSupportCustomerReplyReadinessContract(clarificationReplyPolicy.readinessContract).valid, true);
1211
+
1212
+ const validClarificationContract = validateResolveIOSupportClarificationContract(
1213
+ buildResolveIOSupportClarificationContract(
1214
+ clarificationReplyPolicy.clarificationContract ? {
1215
+ ...validDiagnosis,
1216
+ issue_case: {
1217
+ ...validDiagnosis.issue_case,
1218
+ reproduction_status: 'blocked' as const,
1219
+ reproduction_blocker: 'Need the exact BOL number where the disabled driver appears.'
1220
+ }
1221
+ } : undefined,
1222
+ ['issue_case.reproduction_blocker is required when reproduction_status=blocked']
1223
+ )
1224
+ );
1225
+ assert.equal(validClarificationContract.valid, true);
1226
+ assert.equal(validClarificationContract.normalized.oneQuestionOnly, true);
1227
+
1228
+ const multiQuestionClarificationContract = validateResolveIOSupportClarificationContract({
1229
+ ...validClarificationContract.normalized,
1230
+ question: 'Which BOL number is affected? Which account should we use?'
1231
+ });
1232
+ assert.equal(multiQuestionClarificationContract.valid, false);
1233
+ assert.ok(multiQuestionClarificationContract.blockers.some((blocker) => /exactly one customer question/i.test(blocker)));
1234
+
1235
+ const diagnosisJson = JSON.stringify({ support_diagnosis_gate: validDiagnosis });
1236
+ assert.equal(extractResolveIOSupportDiagnosisGateFromText(`\`\`\`json\n${diagnosisJson}\n\`\`\``)?.issue_class, 'filter_query_mismatch');
1237
+
1238
+ const legacy4433Diagnosis = extractResolveIOSupportDiagnosisGateFromText(JSON.stringify({
1239
+ support_diagnosis_gate: {
1240
+ status: 'passed',
1241
+ issue_case: 'Part inventory was entered with warehouse Kermit, but later checkout/pull history showed warehouse Midland.',
1242
+ issue_class: 'general_inquiry',
1243
+ accepted_hypothesis: 'Repository evidence shows two independent warehouse fields are used by design: manual check-in saves checkin.yard/id_yard, while pull/checkout saves checkout.warehouse_location/id_warehouse_location.',
1244
+ rejected_alternatives: [
1245
+ 'Publication rewrite/default hypothesis rejected: checkoutsWithItemId returns raw checkout docs without warehouse remap.',
1246
+ 'Permission/visibility gate hypothesis rejected: checkout warehouse cell is directly rendered from checkout.warehouse_location with no role/template guard.',
1247
+ 'Missing check-in warehouse persistence hypothesis rejected: addPartInventory stores selected yard into checkin.id_yard and checkin.yard.'
1248
+ ],
1249
+ failing_path: {
1250
+ route: '/part/detail/:id',
1251
+ path_chain: [
1252
+ '/part/checkout submit stores checkout warehouse fields',
1253
+ 'checkoutsWithItemId publishes checkout documents',
1254
+ 'Part Check-out History renders checkout.warehouse_location'
1255
+ ],
1256
+ assessment: 'Observed output follows the checkout warehouse source.'
1257
+ },
1258
+ owner_files: [
1259
+ 'angular/app/part/checkout/part-checkout.component.ts',
1260
+ 'angular/app/part/detail/part-detail.component.ts',
1261
+ 'angular/app/part/detail/part-detail.component.html',
1262
+ 'server/src/publications/checkouts.ts'
1263
+ ],
1264
+ proof_plan: [
1265
+ 'Read-only compare reported checkout document id_warehouse_location/warehouse_location with expected value for the event.',
1266
+ 'Read-only compare corresponding check-in document id_yard/yard for the same part/time window.'
1267
+ ],
1268
+ evidence: [
1269
+ 'Ticket text reports Kermit entry then Midland print outcome.',
1270
+ 'part-detail component subscribes to checkoutsWithItemId for checkout history rows.',
1271
+ 'checkouts publication returns raw Checkouts.find({items.id: id_item}) data.',
1272
+ 'part-detail template renders checkout.warehouse_location directly.',
1273
+ 'part-checkout submit path persists checkout warehouse fields from selected checkout warehouse.'
1274
+ ]
1275
+ }
1276
+ }));
1277
+ assert.ok(legacy4433Diagnosis);
1278
+ assert.equal(legacy4433Diagnosis?.issue_class, 'missing_wrong_data');
1279
+ assert.equal(legacy4433Diagnosis?.issue_case.reproduction_status, 'classified');
1280
+ assert.match(legacy4433Diagnosis?.issue_case.customer_complaint || '', /Kermit/);
1281
+ assert.match(legacy4433Diagnosis?.proof_plan.business_assertion || '', /Kermit|Midland/);
1282
+ assert.ok(legacy4433Diagnosis?.evidence.some((entry) => entry.type === 'ticket'));
1283
+ assert.ok(legacy4433Diagnosis?.evidence.some((entry) => entry.type === 'code' && entry.ownerFiles.includes('server/src/publications/checkouts.ts')));
1284
+ const legacy4433Validation = validateResolveIOSupportDiagnosisGate(legacy4433Diagnosis);
1285
+ assert.equal(legacy4433Validation.valid, true, legacy4433Validation.blockers.join(' | '));
1286
+
1287
+ const workflowBehavior4433Diagnosis = extractResolveIOSupportDiagnosisGateFromText(JSON.stringify({
1288
+ support_diagnosis_gate: {
1289
+ status: 'passed',
1290
+ issue_case: 'General Inquiry: repository evidence indicates existing behavior (not a confirmed runtime failure).',
1291
+ issue_class: 'workflow_behavior',
1292
+ accepted_hypothesis: 'The warehouse shown in later inventory pull/print comes from checkout records (`id_warehouse_location`/`warehouse_location`), while the June 1 entry used check-in fields (`id_yard`/`yard`); these sources are separate and can differ (Kermit vs Midland).',
1293
+ rejected_alternatives: [
1294
+ {
1295
+ hypothesis: 'Midland is hardcoded/defaulted in checkout UI.',
1296
+ reason: 'Checkout requires explicit warehouse selection; no Midland hardcode/default path found in checkout component.'
1297
+ },
1298
+ {
1299
+ hypothesis: 'Print/history warehouse is derived from check-in yard and mapped incorrectly.',
1300
+ reason: 'Pick-ticket/history render checkout warehouse fields directly.'
1301
+ },
1302
+ {
1303
+ hypothesis: 'Checkout publication rewrites warehouse values.',
1304
+ reason: 'checkouts publication returns stored checkout docs without remapping from check-ins.'
1305
+ }
1306
+ ],
1307
+ failing_path: 'Manual part check-in stored with Kermit yard -> later part checkout stored with Midland warehouse -> pull/print and checkout history display checkout warehouse fields (Midland).',
1308
+ owner_files: [
1309
+ 'angular/app/part/checkout/part-checkout.component.ts',
1310
+ 'angular/app/document/pick-ticket/report-doc-pick-ticket.component.html',
1311
+ 'angular/app/part/detail/part-detail.component.ts',
1312
+ 'server/src/publications/checkouts.ts'
1313
+ ],
1314
+ proof_plan: [
1315
+ {
1316
+ action: 'Reproduce with one part: manual check-in at Kermit, then checkout selecting Midland.',
1317
+ proof_required: 'Check-in history shows Kermit while checkout history/pick-ticket show Midland.'
1318
+ },
1319
+ {
1320
+ action: 'If desired behavior is warehouse inheritance from latest check-in, log as behavior change.',
1321
+ proof_required: 'Feature Request approved for auto-default/lock checkout warehouse from latest check-in yard.'
1322
+ }
1323
+ ],
1324
+ evidence: [
1325
+ {
1326
+ type: 'artifact',
1327
+ path: '.resolveio-support-context/manual-ticket.request.txt',
1328
+ detail: 'Reporter states June 1 Kermit entry and later Midland print.'
1329
+ },
1330
+ {
1331
+ type: 'artifact',
1332
+ path: '.resolveio-support-context/attachments/Screenshot_2026-06-09_at_11.16.41_AM.png',
1333
+ detail: 'Screenshot shows Kermit in check-in row and Midland in checkout history rows.'
1334
+ },
1335
+ {
1336
+ type: 'code',
1337
+ path: 'angular/app/part/checkout/part-checkout.component.ts',
1338
+ detail: 'Checkout payload persists selected warehouse fields into checkouts.'
1339
+ },
1340
+ {
1341
+ type: 'code',
1342
+ path: 'angular/app/document/pick-ticket/report-doc-pick-ticket.component.html',
1343
+ detail: 'Rendered warehouse is from checkout data (`data.warehouse_location` / `part.warehouse_location`).'
1344
+ },
1345
+ {
1346
+ type: 'code',
1347
+ path: 'angular/app/part/detail/part-detail.component.ts',
1348
+ detail: 'Checkout history sourced from checkoutsWithItemId.'
1349
+ },
1350
+ {
1351
+ type: 'code',
1352
+ path: 'server/src/publications/checkouts.ts',
1353
+ detail: 'Publication returns checkout docs directly.'
1354
+ }
1355
+ ]
1356
+ }
1357
+ }));
1358
+ assert.ok(workflowBehavior4433Diagnosis);
1359
+ assert.equal(workflowBehavior4433Diagnosis?.issue_class, 'missing_wrong_data');
1360
+ assert.match(workflowBehavior4433Diagnosis?.issue_case.customer_complaint || '', /Kermit/);
1361
+ assert.ok(workflowBehavior4433Diagnosis?.evidence.some((entry) => entry.type === 'ticket' && /Reporter/.test(entry.summary)));
1362
+ assert.ok(workflowBehavior4433Diagnosis?.evidence.some((entry) => entry.type === 'browser' && /Screenshot/.test(entry.summary)));
1363
+ assert.ok(workflowBehavior4433Diagnosis?.evidence.some((entry) => entry.type === 'code' && entry.ownerFiles.includes('server/src/publications/checkouts.ts')));
1364
+ const workflowBehavior4433Validation = validateResolveIOSupportDiagnosisGate(workflowBehavior4433Diagnosis);
1365
+ assert.equal(workflowBehavior4433Validation.valid, true, workflowBehavior4433Validation.blockers.join(' | '));
1366
+
1367
+ const rationale4433Diagnosis = extractResolveIOSupportDiagnosisGateFromText(JSON.stringify({
1368
+ support_diagnosis_gate: {
1369
+ issue_case: {
1370
+ classification: 'General Inquiry',
1371
+ rationale: 'The traced repository path shows the displayed warehouse is sourced from checkout records, while manual inventory entry stores yard on check-in records; this is a cross-flow expectation mismatch rather than a proven defect in the shown code path.',
1372
+ behavior: 'requested_behavior_change'
1373
+ },
1374
+ issue_class: 'general_inquiry',
1375
+ accepted_hypothesis: {
1376
+ summary: 'Part Detail checkout history renders `checkouts.warehouse_location`, and checkout warehouse is set from the warehouse selected during checkout. Manual inventory check-ins persist `checkins.id_yard/yard` separately, so entering inventory at Kermit does not force later checkout/history warehouse away from Midland.',
1377
+ evidence_refs: [
1378
+ 'angular/app/part/detail/part-detail.component.ts:67',
1379
+ 'angular/app/part/detail/part-detail.component.html:196',
1380
+ 'server/src/publications/checkouts.ts:8',
1381
+ 'angular/app/part/checkout/part-checkout.component.ts:275',
1382
+ 'angular/app/part/checkout/part-checkout.component.ts:413',
1383
+ 'server/src/models/checkin.model.ts:10',
1384
+ 'server/src/models/part.model.ts:13'
1385
+ ]
1386
+ },
1387
+ rejected_alternatives: [
1388
+ {
1389
+ hypothesis: 'Checkout publication remaps warehouse values at read time.',
1390
+ rejection_reason: 'checkoutsWithItemId returns raw checkout documents with no warehouse transformation.',
1391
+ evidence: 'server/src/publications/checkouts.ts:8-10'
1392
+ },
1393
+ {
1394
+ hypothesis: 'Checkout form hardcodes/defaults Midland.',
1395
+ rejection_reason: 'Warehouse value is synchronized from selected id_warehouse_location; no hardcoded Midland assignment in traced flow.',
1396
+ evidence: 'angular/app/part/checkout/part-checkout.component.ts:418-427'
1397
+ }
1398
+ ],
1399
+ failing_path: {
1400
+ reported_path: 'User entered part inventory at Kermit and later saw Midland when pulling inventory.',
1401
+ verified_path: 'Part Detail -> Part Check-out History uses checkout publication data and renders checkout.warehouse_location; check-in yard is a separate persisted field path.'
1402
+ },
1403
+ owner_files: [
1404
+ 'angular/app/part/detail/part-detail.component.ts',
1405
+ 'angular/app/part/detail/part-detail.component.html',
1406
+ 'server/src/publications/checkouts.ts',
1407
+ 'angular/app/part/checkout/part-checkout.component.ts',
1408
+ 'server/src/models/checkin.model.ts',
1409
+ 'server/src/models/part.model.ts'
1410
+ ],
1411
+ proof_plan: [
1412
+ 'Verify the warehouse column binding source in Part Check-out History.',
1413
+ 'Verify checkout submit persistence source for id_warehouse_location and warehouse_location.',
1414
+ 'Verify model separation between global part quantity and check-in yard fields.'
1415
+ ],
1416
+ evidence: [
1417
+ {
1418
+ file: '.resolveio-support-context/manual-ticket.request.txt:1-4',
1419
+ fact: 'Ticket states inventory entered at Kermit and later pulled/printed as Midland.'
1420
+ },
1421
+ {
1422
+ file: 'angular/app/part/detail/part-detail.component.ts:67-73',
1423
+ fact: 'History subscribes to publications.checkoutsWithItemId(this.id).'
1424
+ },
1425
+ {
1426
+ file: 'angular/app/part/detail/part-detail.component.html:196',
1427
+ fact: 'Warehouse cell renders checkout.warehouse_location.'
1428
+ },
1429
+ {
1430
+ file: 'server/src/publications/checkouts.ts:8-10',
1431
+ fact: 'Publication returns raw checkouts by item id.'
1432
+ },
1433
+ {
1434
+ file: 'angular/app/part/checkout/part-checkout.component.ts:275-282',
1435
+ fact: 'Checkout payload is built from form values on submit.'
1436
+ },
1437
+ {
1438
+ file: 'angular/app/part/checkout/part-checkout.component.ts:413-427',
1439
+ fact: 'Warehouse value is set from selected yard (id_warehouse_location).'
1440
+ },
1441
+ {
1442
+ file: 'server/src/models/checkin.model.ts:10-11',
1443
+ fact: 'Check-in model persists yard and id_yard.'
1444
+ },
1445
+ {
1446
+ file: 'server/src/models/part.model.ts:13-14',
1447
+ fact: 'Part model tracks global quantity only (quantity, quantity_string).'
1448
+ }
1449
+ ],
1450
+ status: 'passed'
1451
+ }
1452
+ }));
1453
+ assert.ok(rationale4433Diagnosis);
1454
+ assert.equal(rationale4433Diagnosis?.issue_class, 'missing_wrong_data');
1455
+ assert.match(rationale4433Diagnosis?.issue_case.customer_complaint || '', /Kermit/);
1456
+ assert.equal(rationale4433Diagnosis?.issue_case.reproduction_status, 'classified');
1457
+ assert.ok(rationale4433Diagnosis?.evidence.some((entry) => entry.type === 'ticket'));
1458
+ assert.ok(rationale4433Diagnosis?.evidence.some((entry) => entry.type === 'code' && entry.ownerFiles.includes('server/src/publications/checkouts.ts')));
1459
+ assert.ok(rationale4433Diagnosis?.accepted_hypothesis.evidence.some((entry) => /part-detail\.component\.ts/.test(entry)));
1460
+ assert.ok(rationale4433Diagnosis?.rejected_alternatives.some((entry) => /rejected because|Checkout publication remaps/i.test(entry)));
1461
+ const rationale4433Validation = validateResolveIOSupportDiagnosisGate(rationale4433Diagnosis);
1462
+ assert.equal(rationale4433Validation.valid, true, rationale4433Validation.blockers.join(' | '));
1463
+
1464
+ const dataVisibility4433Diagnosis = extractResolveIOSupportDiagnosisGateFromText(JSON.stringify({
1465
+ support_diagnosis_gate: {
1466
+ issue_case: {
1467
+ request_classification: 'General Inquiry',
1468
+ classification_rationale: 'The observed Kermit vs Midland mismatch is explained by existing code paths that read/write different persisted fields (check-in yard vs checkout warehouse), with no evidence of a broken guard or transformation.',
1469
+ behavior_type: 'Requested behavior change, not existing behavior failure'
1470
+ },
1471
+ issue_class: 'data_visibility_mapping',
1472
+ accepted_hypothesis: {
1473
+ statement: 'Part Detail -> Part Check-out History intentionally renders checkout.warehouse_location from checkout documents, while manual inventory entry stores checkin.id_yard/checkin.yard on check-in documents. These are separate flows and are not auto-synchronized.',
1474
+ code_evidence: [
1475
+ 'angular/app/part/detail/part-detail.component.ts:67-73',
1476
+ 'angular/app/part/detail/part-detail.component.html:196',
1477
+ 'server/src/publications/checkouts.ts:8-10',
1478
+ 'angular/app/part/checkout/part-checkout.component.ts:275-282',
1479
+ 'angular/app/part/checkout/part-checkout.component.ts:413-427',
1480
+ 'server/src/models/checkin.model.ts:10-11',
1481
+ 'server/src/models/part.model.ts:13-14'
1482
+ ]
1483
+ },
1484
+ rejected_alternatives: [
1485
+ {
1486
+ hypothesis: 'Warehouse value is remapped during checkout publication read.',
1487
+ why_rejected: 'checkoutsWithItemId returns raw checkout rows via Checkouts.find with no remap logic.',
1488
+ evidence: ['server/src/publications/checkouts.ts:8-10']
1489
+ },
1490
+ {
1491
+ hypothesis: 'Checkout form hardcodes Midland.',
1492
+ why_rejected: 'Checkout warehouse text is set from selected id_warehouse_location yard lookup.',
1493
+ evidence: ['angular/app/part/checkout/part-checkout.component.ts:413-427']
1494
+ }
1495
+ ],
1496
+ failing_path: {
1497
+ reported: 'User entered inventory at Kermit and later saw Midland when pulling/viewing inventory history.',
1498
+ verified: 'Part Detail checkout history subscribes to checkout records and renders checkout.warehouse_location; check-in yard is stored separately on check-in records.'
1499
+ },
1500
+ owner_files: [
1501
+ 'angular/app/part/detail/part-detail.component.ts',
1502
+ 'angular/app/part/detail/part-detail.component.html',
1503
+ 'server/src/publications/checkouts.ts',
1504
+ 'angular/app/part/checkout/part-checkout.component.ts',
1505
+ 'server/src/models/checkin.model.ts',
1506
+ 'server/src/models/part.model.ts'
1507
+ ],
1508
+ proof_plan: [
1509
+ {
1510
+ step: 'Verify the Part Check-out History warehouse column source.',
1511
+ proof_required: 'Confirm subscription source and template binding use checkout warehouse field.'
1512
+ },
1513
+ {
1514
+ step: 'Verify checkout warehouse persistence source.',
1515
+ proof_required: 'Confirm selected id_warehouse_location drives persisted warehouse_location.'
1516
+ }
1517
+ ],
1518
+ evidence: [
1519
+ {
1520
+ file: '.resolveio-support-context/manual-ticket.request.txt:1-4',
1521
+ fact: 'Reporter states inventory entered at Kermit and later displayed as Midland.'
1522
+ },
1523
+ {
1524
+ file: 'angular/app/part/detail/part-detail.component.ts:67-73',
1525
+ fact: 'History source is publications.checkoutsWithItemId(this.id).'
1526
+ },
1527
+ {
1528
+ file: 'angular/app/part/detail/part-detail.component.html:196',
1529
+ fact: 'Warehouse column renders checkout.warehouse_location.'
1530
+ },
1531
+ {
1532
+ file: 'server/src/publications/checkouts.ts:8-10',
1533
+ fact: 'Publication returns raw checkout documents by item id.'
1534
+ },
1535
+ {
1536
+ file: 'angular/app/part/checkout/part-checkout.component.ts:413-427',
1537
+ fact: 'warehouse_location is synchronized from selected yard id.'
1538
+ },
1539
+ {
1540
+ file: 'server/src/models/checkin.model.ts:10-11',
1541
+ fact: 'Check-in model stores yard and id_yard.'
1542
+ },
1543
+ {
1544
+ file: 'server/src/models/part.model.ts:13-14',
1545
+ fact: 'Part model stores global quantity and quantity_string only.'
1546
+ }
1547
+ ],
1548
+ status: 'passed'
1549
+ }
1550
+ }));
1551
+ assert.ok(dataVisibility4433Diagnosis);
1552
+ assert.equal(dataVisibility4433Diagnosis?.issue_class, 'missing_wrong_data');
1553
+ assert.equal(dataVisibility4433Diagnosis?.issue_case.reproduction_status, 'classified');
1554
+ assert.match(dataVisibility4433Diagnosis?.issue_case.customer_complaint || '', /Kermit/);
1555
+ assert.ok(dataVisibility4433Diagnosis?.accepted_hypothesis.evidence.some((entry) => /checkouts\.ts/.test(entry)));
1556
+ const dataVisibility4433Validation = validateResolveIOSupportDiagnosisGate(dataVisibility4433Diagnosis);
1557
+ assert.equal(dataVisibility4433Validation.valid, true, dataVisibility4433Validation.blockers.join(' | '));
1558
+
1559
+ const behaviorInterpretation4433Diagnosis = extractResolveIOSupportDiagnosisGateFromText(JSON.stringify({
1560
+ support_diagnosis_gate: {
1561
+ status: 'passed',
1562
+ issue_case: 'On /part/detail, the customer is comparing two different tables: June 1 Kermit appears in check-in history, while Midland appears in part check-out history.',
1563
+ issue_class: 'general_inquiry',
1564
+ accepted_hypothesis: {
1565
+ summary: 'This is existing intended behavior, not a runtime failure: checkout rows show the warehouse used at checkout time, independent of prior check-in warehouse.',
1566
+ rationale: 'The UI renders separate check-in and check-out sections, and the checkout table binds directly to checkout warehouse fields from checkout records. No permission/template guard hides or rewrites this field for this path.'
1567
+ },
1568
+ rejected_alternatives: [
1569
+ {
1570
+ hypothesis: 'Permission/role gate caused warehouse field mismatch between users.',
1571
+ why_rejected: 'No role/helper gate wraps these history tables; only edit/delete buttons are wrapped with user-role checks.'
1572
+ },
1573
+ {
1574
+ hypothesis: 'A report method overwrote warehouse to Midland on this detail screen.',
1575
+ why_rejected: 'This screen is populated from publications, not reportPartInventory/reportDailyInventorySnapshot.'
1576
+ },
1577
+ {
1578
+ hypothesis: 'Checkout query is incorrectly filtering to Midland.',
1579
+ why_rejected: 'Publication query is items.id equals id_item, so all checkout warehouses for that part are expected.'
1580
+ }
1581
+ ],
1582
+ failing_path: {
1583
+ type: 'behavior_interpretation',
1584
+ route: '/part/detail/:id',
1585
+ module_permission: 'Part module detail route; no table-level permission gate on the warehouse columns in these history sections.',
1586
+ template_chain: 'part-detail template renders separate Other Part Check-in History and Part Check-out History blocks; checkout warehouse column is checkout.warehouse_location.',
1587
+ helper_chain: 'No isUserX/isRoleY helper gates used for these table rows/columns.',
1588
+ publication_chain: 'part-detail subscribes to checkinsWithItemId and checkoutsWithItemId; each publication returns records by item id from checkins/checkouts collections.'
1589
+ },
1590
+ owner_files: [
1591
+ '/var/app/resolveio-ai-workspace/6a35dfb2feca513d5ef50c00/resolveio-all/h2sko/angular/app/part/detail/part-detail.component.html',
1592
+ '/var/app/resolveio-ai-workspace/6a35dfb2feca513d5ef50c00/resolveio-all/h2sko/angular/app/part/detail/part-detail.component.ts',
1593
+ '/var/app/resolveio-ai-workspace/6a35dfb2feca513d5ef50c00/resolveio-all/h2sko/server/src/publications/checkins.ts',
1594
+ '/var/app/resolveio-ai-workspace/6a35dfb2feca513d5ef50c00/resolveio-all/h2sko/server/src/publications/checkouts.ts'
1595
+ ],
1596
+ proof_plan: [
1597
+ 'Validate the screenshot table title/columns against part-detail template sections.',
1598
+ 'Trace part-detail subscriptions to confirm each table data source.',
1599
+ 'Trace publication queries to confirm checkout warehouse semantics.',
1600
+ 'Classify as General Inquiry (existing behavior) unless customer explicitly requests changed display logic.'
1601
+ ],
1602
+ evidence: [
1603
+ {
1604
+ file: '/var/app/resolveio-ai-workspace/6a35dfb2feca513d5ef50c00/resolveio-all/h2sko/.resolveio-support-context/manual-ticket.request.txt',
1605
+ detail: 'Reporter says 6 units entered in Kermit and later inventory pull showed Midland.'
1606
+ },
1607
+ {
1608
+ file: '/var/app/resolveio-ai-workspace/6a35dfb2feca513d5ef50c00/resolveio-all/h2sko/.resolveio-support-context/attachments/Screenshot_2026-06-09_at_11.16.41_AM.png',
1609
+ detail: 'Visible table is Part Check-out History showing Midland rows, while separate check-in context shows Kermit.'
1610
+ },
1611
+ {
1612
+ file: '/var/app/resolveio-ai-workspace/6a35dfb2feca513d5ef50c00/resolveio-all/h2sko/angular/app/part/detail/part-detail.component.html',
1613
+ detail: 'Separate UI blocks exist for check-ins and check-outs; checkout table prints checkout.warehouse_location.'
1614
+ },
1615
+ {
1616
+ file: '/var/app/resolveio-ai-workspace/6a35dfb2feca513d5ef50c00/resolveio-all/h2sko/angular/app/part/detail/part-detail.component.ts',
1617
+ detail: 'Component loads checkins via checkinsWithItemId and checkouts via checkoutsWithItemId.'
1618
+ },
1619
+ {
1620
+ file: '/var/app/resolveio-ai-workspace/6a35dfb2feca513d5ef50c00/resolveio-all/h2sko/server/src/publications/checkouts.ts',
1621
+ detail: 'checkoutsWithItemId returns Checkouts.find items.id equals id_item.'
1622
+ },
1623
+ {
1624
+ file: '/var/app/resolveio-ai-workspace/6a35dfb2feca513d5ef50c00/resolveio-all/h2sko/server/src/publications/checkins.ts',
1625
+ detail: 'checkinsWithItemId returns Checkins.find items.id equals id_item.'
1626
+ }
1627
+ ]
1628
+ }
1629
+ }));
1630
+ assert.ok(behaviorInterpretation4433Diagnosis);
1631
+ assert.equal(behaviorInterpretation4433Diagnosis?.issue_class, 'missing_wrong_data');
1632
+ assert.match(behaviorInterpretation4433Diagnosis?.issue_case.customer_complaint || '', /Kermit|Midland/);
1633
+ assert.ok(behaviorInterpretation4433Diagnosis?.owner_files.every((entry) => !/^var\/app\/resolveio-ai-workspace/.test(entry)));
1634
+ const behaviorInterpretation4433Validation = validateResolveIOSupportDiagnosisGate(behaviorInterpretation4433Diagnosis);
1635
+ assert.equal(behaviorInterpretation4433Validation.valid, true, behaviorInterpretation4433Validation.blockers.join(' | '));
1636
+
1637
+ const diagnosedBundle = recordResolveIOSupportV5Step(initialized, {
1638
+ stepType: 'diagnosis_gate',
1639
+ outcome: 'pass',
1640
+ lane: 'build',
1641
+ summary: diagnosisJson,
1642
+ diagnosisGate: validDiagnosis,
1643
+ now: '2026-06-06T00:00:30.000Z'
1644
+ });
1645
+ assert.equal(diagnosedBundle.supportV5DiagnosisGate?.status, 'passed');
1646
+ assert.deepEqual(diagnosedBundle.supportV5MicrotaskLedger[1].targetFiles, validDiagnosis.owner_files);
1647
+ assert.equal(diagnosedBundle.supportV5IssueClassProbePlan?.status, 'ready');
1648
+ assert.equal(diagnosedBundle.supportV5IssueClassProbePlan?.activeProbe?.issue_class, validDiagnosis.issue_class);
1649
+ assert.equal(diagnosedBundle.supportV5MicrotaskLedger[2].contextRefs.includes('supportV5IssueClassProbePlan'), true);
1650
+ assert.equal(diagnosedBundle.supportV5LaneMemory.qa.activeQaRow?.workflow, validDiagnosis.proof_plan.business_proof_contract.action_under_test);
1651
+ assert.equal(diagnosedBundle.supportV5LaneMemory.qa.activeQaRow?.assertion, validDiagnosis.proof_plan.business_proof_contract.data_or_dom_assertion);
1652
+ assert.equal(diagnosedBundle.supportV5RecoveryPlan?.recoveryClass, 'advance_after_proof');
1653
+ assert.equal(diagnosedBundle.supportV5RecoveryCheckpoint?.recoveryClass, 'advance_after_proof');
1654
+ assert.equal(diagnosedBundle.supportV5RecoveryAction?.checkpointId, diagnosedBundle.supportV5RecoveryCheckpoint?.checkpointId);
1655
+
1656
+ const staleReproductionBundle = initializeResolveIOSupportV5State({
1657
+ jobId: 'job-stale-repro',
1658
+ ticketId: 'ticket-stale-repro',
1659
+ ticketNumber: '004345',
1660
+ title: 'Support ticket #004345',
1661
+ description: 'Pricing upload imports rows but reports wrong verified count.',
1662
+ approvedScopeRequirements: ['Replicate the upload issue with the attached pre-populated and blank templates'],
1663
+ buildThreadKey: 'support:ticket-stale-repro:job:job-stale-repro:build',
1664
+ qaThreadKey: 'support:ticket-stale-repro:job:job-stale-repro:qa',
1665
+ now: '2026-06-06T00:00:31.000Z'
1666
+ });
1667
+ const staleBuildBeforeDiagnosis = staleReproductionBundle.supportV5MicrotaskLedger.find((task) => task.lane === 'build' && task.type === 'build_repair');
1668
+ assert.match(staleBuildBeforeDiagnosis?.objective || '', /Replicate the upload issue/i);
1669
+ const staleReproductionDiagnosed = recordResolveIOSupportV5Step(staleReproductionBundle, {
1670
+ stepType: 'diagnosis_gate',
1671
+ outcome: 'pass',
1672
+ lane: 'build',
1673
+ summary: diagnosisJson,
1674
+ diagnosisGate: validDiagnosis,
1675
+ now: '2026-06-06T00:00:32.000Z'
1676
+ });
1677
+ const staleBuildAfterDiagnosis = staleReproductionDiagnosed.supportV5MicrotaskLedger.find((task) => task.lane === 'build' && task.type === 'build_repair');
1678
+ assert.equal(staleBuildAfterDiagnosis?.status, 'pending');
1679
+ assert.equal(staleBuildAfterDiagnosis?.attempts, 0);
1680
+ assert.equal(staleBuildAfterDiagnosis?.blocker || '', '');
1681
+ assert.deepEqual(staleBuildAfterDiagnosis?.targetFiles, validDiagnosis.owner_files);
1682
+ assert.match(staleBuildAfterDiagnosis?.objective || '', /Patch the accepted root cause/i);
1683
+ assert.match(staleBuildAfterDiagnosis?.selfGate || '', /Do not re-run read-only reproduction as the repair/i);
1684
+ assert.match(staleBuildAfterDiagnosis?.acceptanceProof || '', /disabled driver is excluded|active driver remains selected/i);
1685
+ assert.equal(staleBuildAfterDiagnosis?.contextRefs.includes('accepted_hypothesis'), true);
1686
+
1687
+ const failedPuppeteerPreflight = evaluateResolveIOSupportPreflightGate({
1688
+ required: true,
1689
+ checks: [
1690
+ { name: 'puppeteer_module', status: 'fail', summary: 'Cannot require puppeteer from the QA workspace.', artifactPath: 'qa-artifacts/preflight/puppeteer.log' },
1691
+ { name: 'chrome_executable', status: 'pass', summary: 'Chrome executable exists.' },
1692
+ { name: 'mongo_connection', status: 'pass', summary: 'Mongo ping ok.' }
1693
+ ],
1694
+ recordedAt: '2026-06-06T00:00:35.000Z'
1695
+ });
1696
+ assert.equal(failedPuppeteerPreflight.status, 'infra_failed');
1697
+ assert.equal(failedPuppeteerPreflight.failureClass, 'infra');
1698
+ assert.equal(failedPuppeteerPreflight.blocksProductRepair, true);
1699
+ assert.equal(failedPuppeteerPreflight.nextCommand, 'run_support_v5_infra_repair');
1700
+ assert.ok(failedPuppeteerPreflight.requiredEvidence.some((entry) => /Puppeteer\/Chrome\/server\/client\/Mongo/i.test(entry)));
1701
+ const compilePreflight = evaluateResolveIOSupportPreflightGate({
1702
+ required: true,
1703
+ compileResult: {
1704
+ status: 'fail',
1705
+ summary: 'Angular compile failed on support ticket bundle.',
1706
+ artifactPath: 'qa-artifacts/preflight/compile.log'
1707
+ }
1708
+ });
1709
+ assert.equal(compilePreflight.status, 'compile_failed');
1710
+ assert.equal(compilePreflight.failureClass, 'compile');
1711
+ assert.equal(compilePreflight.nextCommand, 'run_support_v5_compile_repair');
1712
+ const persistedPreflightBundle = recordResolveIOSupportV5Step(diagnosedBundle, {
1713
+ stepType: 'compile_check',
1714
+ outcome: 'needs_repair',
1715
+ lane: 'qa',
1716
+ summary: 'Compile preflight failed before repair loop.',
1717
+ preflightGate: compilePreflight,
1718
+ failureClass: 'compile',
1719
+ now: '2026-06-06T00:00:36.000Z'
1720
+ });
1721
+ assert.equal(persistedPreflightBundle.supportV5PreflightGate?.status, 'compile_failed');
1722
+ assert.equal(persistedPreflightBundle.supportV5StepHistory[persistedPreflightBundle.supportV5StepHistory.length - 1].preflightGate?.nextCommand, 'run_support_v5_compile_repair');
1723
+ const diagnosisMicrotaskId = diagnosedBundle.supportV5MicrotaskLedger.find((task) => task.type === 'diagnosis_gate')?.microtaskId;
1724
+ const buildMicrotasksAfterDiagnosis = diagnosedBundle.supportV5MicrotaskLedger.filter((task) => task.lane === 'build' && task.type !== 'diagnosis_gate');
1725
+ assert.ok(diagnosisMicrotaskId);
1726
+ assert.ok(buildMicrotasksAfterDiagnosis.length > 0);
1727
+ assert.ok(buildMicrotasksAfterDiagnosis.every((task) => task.dependsOn.includes(diagnosisMicrotaskId || '')));
1728
+ assert.ok(buildMicrotasksAfterDiagnosis.every((task) => task.dependsOn.length === new Set(task.dependsOn).size));
1729
+ assert.equal(changedFilesOutsideResolveIOSupportDiagnosisOwnerFiles(validDiagnosis, [
1730
+ 'geochem/angular/app/bol/new/bol-new.component.ts',
1731
+ 'geochem/server/src/methods/unrelated.ts'
1732
+ ]).length, 1);
1733
+ const pricingDiagnosisWithProjectRootOwner = {
1734
+ ...validDiagnosis,
1735
+ owner_files: ['server/src/methods/pricing.ts'],
1736
+ failing_path: {
1737
+ ...validDiagnosis.failing_path,
1738
+ backend: 'server/src/methods/pricing.ts'
1739
+ },
1740
+ evidence: [
1741
+ ...validDiagnosis.evidence,
1742
+ { type: 'code' as const, summary: 'Pricing import method owner file inspected.', ownerFiles: ['server/src/methods/pricing.ts'] }
1743
+ ]
1744
+ };
1745
+ assert.equal(changedFilesOutsideResolveIOSupportDiagnosisOwnerFiles(pricingDiagnosisWithProjectRootOwner, [
1746
+ 'geochem/server/src/methods/pricing.ts'
1747
+ ]).length, 0);
1748
+ assert.deepEqual(changedFilesOutsideResolveIOSupportDiagnosisOwnerFiles(pricingDiagnosisWithProjectRootOwner, [
1749
+ 'geochem/server/src/methods/unrelated-pricing.ts'
1750
+ ]), ['geochem/server/src/methods/unrelated-pricing.ts']);
1751
+ const pricingDiagnosisWithMixedProjectRootOwners = {
1752
+ ...validDiagnosis,
1753
+ issue_case: {
1754
+ ...validDiagnosis.issue_case,
1755
+ customer_complaint: 'Uploading production pricing workbooks leaves rows unimported or unclassified.',
1756
+ expected_result: 'Every supported workbook row imports or receives an explicit duplicate/error classification.',
1757
+ observed_result: 'Rows can be skipped without clear inserted/duplicate/error proof.',
1758
+ route_module: 'Manage Pricing -> Sales -> Production import',
1759
+ account_customer_context: 'Geo Chemicals ticket 004345',
1760
+ reproduction_status: 'classified' as const
1761
+ },
1762
+ issue_class: 'missing_wrong_data' as const,
1763
+ accepted_hypothesis: {
1764
+ statement: 'Production pricing import skips attached workbook rows before insert.',
1765
+ falsifiable_test: 'Upload both workbook variants and compare inserted or classified row count.',
1766
+ evidence: [
1767
+ 'angular/app/widgets/pricing/sale/production/viewcustomer/pricing-sale-production-viewcustomer.component.ts builds the import payload.',
1768
+ 'server/src/methods/pricing.ts validates production pricing rows.'
1769
+ ]
1770
+ },
1771
+ failing_path: {
1772
+ frontend: 'angular/app/widgets/pricing/sale/production/viewcustomer/pricing-sale-production-viewcustomer.component.ts',
1773
+ backend: 'server/src/methods/pricing.ts',
1774
+ description: 'Production pricing import upload path.'
1775
+ },
1776
+ owner_files: [
1777
+ 'angular/app/widgets/pricing/sale/production/viewcustomer/pricing-sale-production-viewcustomer.component.ts',
1778
+ 'server/src/methods/pricing.ts'
1779
+ ],
1780
+ proof_plan: {
1781
+ ...validDiagnosis.proof_plan,
1782
+ business_assertion: 'Uploaded workbook rows are inserted or explicitly classified.',
1783
+ business_proof_contract: {
1784
+ ...validDiagnosis.proof_plan.business_proof_contract,
1785
+ issue_class: 'missing_wrong_data' as const,
1786
+ expected_business_state_change: 'Every uploaded pricing row is inserted, updated, unchanged, or rejected with an explicit reason.',
1787
+ data_or_dom_assertion: 'Inserted plus classified rows equals uploaded candidate row count.'
1788
+ }
1789
+ },
1790
+ evidence: [
1791
+ { type: 'code' as const, summary: 'Angular pricing import component is the frontend owner.', artifactPath: 'angular/app/widgets/pricing/sale/production/viewcustomer/pricing-sale-production-viewcustomer.component.ts', ownerFiles: ['angular/app/widgets/pricing/sale/production/viewcustomer/pricing-sale-production-viewcustomer.component.ts'] },
1792
+ { type: 'code' as const, summary: 'Pricing import method is the backend owner.', artifactPath: 'server/src/methods/pricing.ts', ownerFiles: ['server/src/methods/pricing.ts'] }
1793
+ ]
1794
+ };
1795
+ const mixedProjectRootOwnerValidation = validateResolveIOSupportDiagnosisGate(pricingDiagnosisWithMixedProjectRootOwners);
1796
+ assert.equal(mixedProjectRootOwnerValidation.valid, true, mixedProjectRootOwnerValidation.blockers.join(' | '));
1797
+ assert.deepEqual(changedFilesOutsideResolveIOSupportDiagnosisOwnerFiles(pricingDiagnosisWithMixedProjectRootOwners, [
1798
+ 'geochem/angular/app/widgets/pricing/sale/production/viewcustomer/pricing-sale-production-viewcustomer.component.ts',
1799
+ 'geochem/server/src/methods/pricing.ts',
1800
+ 'resolveio-all/geochem/angular/app/widgets/pricing/sale/production/viewcustomer/pricing-sale-production-viewcustomer.component.ts',
1801
+ '/var/app/resolveio-ai-workspace/run-1/resolveio-all/geochem/server/src/methods/pricing.ts'
1802
+ ]), []);
1803
+ assert.deepEqual(changedFilesOutsideResolveIOSupportDiagnosisOwnerFiles(pricingDiagnosisWithMixedProjectRootOwners, [
1804
+ 'geochem/angular/app/widgets/pricing/sale/production/viewcustomer/pricing-sale-production-viewcustomer.component.ts',
1805
+ 'geochem/angular/app/widgets/pricing/sale/production/viewcustomer/unrelated.component.ts'
1806
+ ]), ['geochem/angular/app/widgets/pricing/sale/production/viewcustomer/unrelated.component.ts']);
1807
+
1808
+ const initialAutonomousDecision = decideResolveIOSupportV5AutonomousNextAction({
1809
+ bundle: initialized
1810
+ });
1811
+ assert.equal(initialAutonomousDecision.action, 'run_diagnosis_gate');
1812
+ assert.equal(initialAutonomousDecision.canRunAutonomously, true);
1813
+ assert.equal(initialAutonomousDecision.canEditProductCode, false);
1814
+ assert.equal(initialAutonomousDecision.rootCauseReadiness.status, 'diagnosis_required');
1815
+ assert.equal(initialAutonomousDecision.rootCauseReadiness.rootCauseFirstSatisfied, false);
1816
+ assert.equal(initialAutonomousDecision.rootCauseReadiness.nextGate, 'diagnosis');
1817
+ assert.equal(initialAutonomousDecision.evidenceFreshness.status, 'missing');
1818
+ assert.equal(initialAutonomousDecision.evidenceFreshness.mustCollectNewEvidence, false);
1819
+ assert.equal(initialAutonomousDecision.continuationProofCheckpoint.action, 'run_diagnosis_gate');
1820
+ assert.equal(initialAutonomousDecision.continuationProofCheckpoint.blocksProductRepairUntilChangedEvidence, true);
1821
+ assert.ok(initialAutonomousDecision.continuationProofCheckpoint.requiredResetEvidence.some((entry) => /SupportDiagnosisGate/i.test(entry)));
1822
+ assert.equal(initialAutonomousDecision.nextActionContract.action, 'run_diagnosis_gate');
1823
+ assert.equal(initialAutonomousDecision.nextActionContract.safeToAutoRun, true);
1824
+ assert.equal(initialAutonomousDecision.nextActionContract.canRunWithoutCodexMonitor, true);
1825
+ assert.equal(initialAutonomousDecision.nextActionContract.codexFallbackRequired, false);
1826
+ assert.equal(initialAutonomousDecision.nextActionContract.rootCauseFirstSatisfied, false);
1827
+ assert.ok(initialAutonomousDecision.nextActionContract.preconditions.some((entry) => /read-only/i.test(entry)));
1828
+ assert.ok(initialAutonomousDecision.nextActionContract.expectedStateTransition.includes('SupportDiagnosisGate changes'));
1829
+ assert.equal(validateResolveIOSupportNextActionContract(initialAutonomousDecision.nextActionContract).valid, true);
1830
+ assert.equal(initialAutonomousDecision.managerExecutionPacket.status, 'auto_ready');
1831
+ assert.equal(initialAutonomousDecision.managerExecutionPacket.executeNow, true);
1832
+ assert.equal(initialAutonomousDecision.managerExecutionPacket.retryScope, 'diagnosis_only');
1833
+ assert.equal(initialAutonomousDecision.managerExecutionPacket.canRunWithoutCodexMonitor, true);
1834
+ assert.ok(initialAutonomousDecision.managerExecutionPacket.proofRequiredBeforeContinuation.some((entry) => /SupportDiagnosisGate changes/i.test(entry)));
1835
+ assert.ok(initialAutonomousDecision.forbiddenActions.some((action) => /No source edits/i.test(action)));
1836
+
1837
+ const blockedReproductionDiagnosis = {
1838
+ ...validDiagnosis,
1839
+ issue_case: {
1840
+ ...validDiagnosis.issue_case,
1841
+ reproduction_status: 'blocked' as const,
1842
+ reproduction_blocker: 'Need the exact BOL number where the disabled driver appears.'
1843
+ },
1844
+ accepted_hypothesis: { statement: '', falsifiable_test: '', evidence: [] }
1845
+ };
1846
+ const clarificationAutonomousDecision = decideResolveIOSupportV5AutonomousNextAction({
1847
+ bundle: {
1848
+ ...initialized,
1849
+ supportV5DiagnosisGate: blockedReproductionDiagnosis
1850
+ }
1851
+ });
1852
+ assert.equal(clarificationAutonomousDecision.action, 'ask_customer_clarification');
1853
+ assert.equal(clarificationAutonomousDecision.primaryCommand, 'review_customer_clarification');
1854
+ assert.equal(clarificationAutonomousDecision.canRunAutonomously, false);
1855
+ assert.equal(clarificationAutonomousDecision.canRunModel, false);
1856
+ assert.equal(clarificationAutonomousDecision.canEditProductCode, false);
1857
+ assert.equal(clarificationAutonomousDecision.canDraftCustomerReply, true);
1858
+ assert.equal(clarificationAutonomousDecision.rootCauseReadiness.status, 'customer_clarification_required');
1859
+ assert.equal(clarificationAutonomousDecision.rootCauseReadiness.nextGate, 'customer_reply');
1860
+ assert.equal(clarificationAutonomousDecision.rootCauseReadiness.requiresHumanDecision, true);
1861
+ assert.equal(clarificationAutonomousDecision.nextActionContract.safeToAutoRun, false);
1862
+ assert.equal(clarificationAutonomousDecision.nextActionContract.canRunWithoutCodexMonitor, false);
1863
+ assert.equal(clarificationAutonomousDecision.nextActionContract.codexFallbackRequired, false);
1864
+ assert.equal(clarificationAutonomousDecision.nextActionContract.codexFallbackReason, 'human_decision_required_not_codex_fallback');
1865
+ assert.match(clarificationAutonomousDecision.humanReviewPacket.question || '', /exact BOL number/);
1866
+ assert.ok(clarificationAutonomousDecision.forbiddenActions.some((action) => /guessed reproduction path/i.test(action)));
1867
+
1868
+ const ownerScopedAutonomousDecision = decideResolveIOSupportV5AutonomousNextAction({
1869
+ bundle: diagnosedBundle,
1870
+ changedFiles: ['geochem/angular/app/bol/new/bol-new.component.ts']
1871
+ });
1872
+ assert.equal(ownerScopedAutonomousDecision.action, 'run_owner_scoped_repair');
1873
+ assert.equal(ownerScopedAutonomousDecision.canEditProductCode, true);
1874
+ assert.equal(ownerScopedAutonomousDecision.rootCauseReadiness.status, 'owner_scoped_repair_ready');
1875
+ assert.equal(ownerScopedAutonomousDecision.rootCauseReadiness.rootCauseFirstSatisfied, true);
1876
+ assert.equal(ownerScopedAutonomousDecision.rootCauseReadiness.canEditProductCode, true);
1877
+ assert.equal(ownerScopedAutonomousDecision.rootCauseReadiness.ownerFilesReady, true);
1878
+ assert.equal(ownerScopedAutonomousDecision.rootCauseReadiness.ownerScopedRepairContractReady, true);
1879
+ assert.equal(ownerScopedAutonomousDecision.repairGate.ownerScopedRepairContract.status, 'ready');
1880
+ assert.deepEqual(ownerScopedAutonomousDecision.ownerFiles, validDiagnosis.owner_files);
1881
+ assert.equal(ownerScopedAutonomousDecision.issueClassProbes[0].acceptance_gate, 'aiqa_business_assertion');
1882
+ assert.equal(ownerScopedAutonomousDecision.rootCauseReadiness.issueClassProbes?.[0].failure_class, 'business');
1883
+ assert.match(ownerScopedAutonomousDecision.primaryCommand, /owner_scoped_repair/);
1884
+ assert.equal(ownerScopedAutonomousDecision.continuationProofCheckpoint.action, 'run_owner_scoped_repair');
1885
+ assert.equal(ownerScopedAutonomousDecision.continuationProofCheckpoint.blocksProductRepairUntilChangedEvidence, false);
1886
+ assert.equal(ownerScopedAutonomousDecision.continuationProofCheckpoint.status, 'waiting_for_proof');
1887
+ assert.equal(ownerScopedAutonomousDecision.nextActionContract.safeToAutoRun, true);
1888
+ assert.equal(ownerScopedAutonomousDecision.nextActionContract.canRunWithoutCodexMonitor, true);
1889
+ assert.equal(ownerScopedAutonomousDecision.nextActionContract.codexFallbackRequired, false);
1890
+ assert.equal(ownerScopedAutonomousDecision.nextActionContract.costRisk, 'expensive_model');
1891
+ assert.equal(ownerScopedAutonomousDecision.nextActionContract.decisionBasis.diagnosisValid, true);
1892
+ assert.equal(ownerScopedAutonomousDecision.nextActionContract.decisionBasis.ownerFilesReady, true);
1893
+ assert.equal(ownerScopedAutonomousDecision.nextActionContract.decisionBasis.ownerScopedRepairContractReady, true);
1894
+ assert.equal(ownerScopedAutonomousDecision.nextActionContract.decisionBasis.ownerScopedRepairContractId, ownerScopedAutonomousDecision.repairGate.ownerScopedRepairContract.contractId);
1895
+ assert.ok(ownerScopedAutonomousDecision.nextActionContract.stopConditions.some((entry) => /outside diagnosis owner_files/i.test(entry)));
1896
+ assert.equal(validateResolveIOSupportNextActionContract(ownerScopedAutonomousDecision.nextActionContract).valid, true);
1897
+ assert.equal(ownerScopedAutonomousDecision.managerExecutionPacket.status, 'auto_ready');
1898
+ assert.equal(ownerScopedAutonomousDecision.managerExecutionPacket.retryScope, 'owner_files_only');
1899
+ assert.equal(ownerScopedAutonomousDecision.managerExecutionPacket.maxAttemptsBeforeFreshEvidence, 1);
1900
+ assert.deepEqual(ownerScopedAutonomousDecision.managerExecutionPacket.ownerFiles, validDiagnosis.owner_files);
1901
+ assert.equal(ownerScopedAutonomousDecision.managerExecutionPacket.proofResetContract.retryScope, 'owner_files_only');
1902
+ assert.equal(ownerScopedAutonomousDecision.managerExecutionPacket.proofResetContract.maxAttemptsBeforeFreshEvidence, 1);
1903
+ assert.ok(ownerScopedAutonomousDecision.managerExecutionPacket.proofResetContract.requiredResetEvidence.length > 0);
1904
+
1905
+ const ownerScopedAutoRetryDecision = decideResolveIOSupportV5AutonomousNextAction({
1906
+ bundle: diagnosedBundle,
1907
+ changedFiles: ['geochem/angular/app/bol/new/bol-new.component.ts'],
1908
+ autonomyPolicy: {
1909
+ mode: 'auto_retry_within_budget',
1910
+ currentSpendUsd: 2,
1911
+ budgetHardUsd: 10,
1912
+ expectedValueScore: 0.4,
1913
+ evidenceStrength: 'material',
1914
+ materialEvidence: true
1915
+ }
1916
+ });
1917
+ assert.equal(ownerScopedAutoRetryDecision.action, 'run_owner_scoped_repair');
1918
+ assert.equal(ownerScopedAutoRetryDecision.autonomyPolicy?.mode, 'auto_retry_within_budget');
1919
+ assert.equal(ownerScopedAutoRetryDecision.autonomyPolicy?.canAutoDispatch, true);
1920
+ assert.equal(ownerScopedAutoRetryDecision.nextActionContract.safeToAutoRun, true);
1921
+ assert.equal(ownerScopedAutoRetryDecision.nextActionContract.decisionBasis.autonomyCanAutoDispatch, true);
1922
+ assert.equal(ownerScopedAutoRetryDecision.nextActionContract.decisionBasis.autonomyExpectedValuePositive, true);
1923
+ assert.equal(ownerScopedAutoRetryDecision.managerExecutionPacket.status, 'auto_ready');
1924
+
1925
+ const approvedUnderSixAutopilotDecision = decideResolveIOSupportV5AutonomousNextAction({
1926
+ bundle: diagnosedBundle,
1927
+ changedFiles: ['geochem/angular/app/bol/new/bol-new.component.ts'],
1928
+ requireAutonomyApprovalForProductRepair: true,
1929
+ autonomyApproval: {
1930
+ contract_id: 'support-autonomy-under-six-approved',
1931
+ status: 'approved_autopilot',
1932
+ approved: true,
1933
+ approved_at: '2026-06-06T00:02:30.000Z',
1934
+ approved_by: 'operator',
1935
+ estimated_hours: 2,
1936
+ max_auto_hours_without_approval: 6,
1937
+ bug_not_bug: 'bug',
1938
+ bug_not_bug_classification_approved: true,
1939
+ after_approval_autopilot_until_a_grade: true,
1940
+ target_grade: 'A',
1941
+ qa_screenshots_required: true,
1942
+ before_action_after_business_proof_required: true,
1943
+ aiqa_business_assertion_required: true,
1944
+ owner_files: validDiagnosis.owner_files,
1945
+ approved_owner_files: validDiagnosis.owner_files,
1946
+ required_completion_evidence: [
1947
+ 'A-grade execution/artifacts/pull_request review',
1948
+ 'QA screenshots with captions',
1949
+ 'AIQaBusinessAssertion before/action/after business proof',
1950
+ 'changed files subset of approved owner_files'
1951
+ ]
1952
+ }
1953
+ });
1954
+ assert.equal(approvedUnderSixAutopilotDecision.action, 'run_owner_scoped_repair');
1955
+ assert.equal(approvedUnderSixAutopilotDecision.autonomyApproval?.autopilotApprovalBoundaryValid, true);
1956
+ assert.equal(approvedUnderSixAutopilotDecision.autonomyApproval?.estimatedHours, 2);
1957
+ assert.equal(approvedUnderSixAutopilotDecision.productRepairDispatchGuard?.status, 'allowed');
1958
+ assert.equal(approvedUnderSixAutopilotDecision.productRepairDispatchGuard?.autoContinueAfterApproval, true);
1959
+ assert.equal(approvedUnderSixAutopilotDecision.productRepairDispatchGuard?.terminalTarget, 'A-grade PR with QA screenshots, AIQaBusinessAssertion before/action/after proof, source commit proof, and changed files inside owner_files');
1960
+ assert.equal(approvedUnderSixAutopilotDecision.nextActionContract.safeToAutoRun, true);
1961
+ assert.equal(approvedUnderSixAutopilotDecision.nextActionContract.canRunWithoutCodexMonitor, true);
1962
+ assert.equal(approvedUnderSixAutopilotDecision.nextActionContract.decisionBasis.autonomyApprovalApproved, true);
1963
+ assert.equal(approvedUnderSixAutopilotDecision.nextActionContract.decisionBasis.approvedScopeHours, 2);
1964
+ assert.equal(approvedUnderSixAutopilotDecision.nextActionContract.decisionBasis.afterApprovalAutopilotUntilAGrade, true);
1965
+ assert.equal(approvedUnderSixAutopilotDecision.nextActionContract.decisionBasis.leSixHourScopeCanAutofixAfterApproval, true);
1966
+ assert.equal(approvedUnderSixAutopilotDecision.nextActionContract.decisionBasis.ownerFilesChangedSinceApproval, false);
1967
+ assert.equal(approvedUnderSixAutopilotDecision.productRepairDispatchGuard?.ownerFilesChangedSinceApproval, false);
1968
+ assert.equal(approvedUnderSixAutopilotDecision.managerExecutionPacket.status, 'auto_ready');
1969
+
1970
+ const approvedScopeOwnerFileDriftDecision = decideResolveIOSupportV5AutonomousNextAction({
1971
+ bundle: diagnosedBundle,
1972
+ changedFiles: ['geochem/angular/app/bol/new/bol-new.component.ts'],
1973
+ requireAutonomyApprovalForProductRepair: true,
1974
+ autonomyApproval: {
1975
+ contract_id: 'support-autonomy-owner-file-drift',
1976
+ status: 'approved_autopilot',
1977
+ approved: true,
1978
+ approved_at: '2026-06-06T00:02:35.000Z',
1979
+ approved_by: 'operator',
1980
+ estimated_hours: 2,
1981
+ max_auto_hours_without_approval: 6,
1982
+ bug_not_bug: 'bug',
1983
+ bug_not_bug_classification_approved: true,
1984
+ after_approval_autopilot_until_a_grade: true,
1985
+ target_grade: 'A',
1986
+ owner_files: [...validDiagnosis.owner_files, 'geochem/server/src/methods/unapproved-scope.ts'],
1987
+ approved_owner_files: validDiagnosis.owner_files,
1988
+ current_owner_files: [...validDiagnosis.owner_files, 'geochem/server/src/methods/unapproved-scope.ts'],
1989
+ required_completion_evidence: [
1990
+ 'A-grade execution/artifacts/pull_request review',
1991
+ 'QA screenshots with captions',
1992
+ 'AIQaBusinessAssertion before/action/after business proof',
1993
+ 'changed files subset of approved owner_files'
1994
+ ]
1995
+ }
1996
+ });
1997
+ assert.equal(approvedScopeOwnerFileDriftDecision.autonomyApproval?.autopilotApprovalBoundaryValid, false);
1998
+ assert.equal(approvedScopeOwnerFileDriftDecision.autonomyApproval?.ownerFilesChangedSinceApproval, true);
1999
+ assert.deepEqual(approvedScopeOwnerFileDriftDecision.autonomyApproval?.ownerFilesAddedSinceApproval, ['geochem/server/src/methods/unapproved-scope.ts']);
2000
+ assert.equal(approvedScopeOwnerFileDriftDecision.productRepairDispatchGuard?.status, 'blocked');
2001
+ assert.equal(approvedScopeOwnerFileDriftDecision.productRepairDispatchGuard?.requiresOwnerFileReapproval, true);
2002
+ assert.equal(approvedScopeOwnerFileDriftDecision.productRepairDispatchGuard?.ownerFilesChangedSinceApproval, true);
2003
+ assert.deepEqual(approvedScopeOwnerFileDriftDecision.productRepairDispatchGuard?.ownerFilesAddedSinceApproval, ['geochem/server/src/methods/unapproved-scope.ts']);
2004
+ assert.equal(approvedScopeOwnerFileDriftDecision.nextActionContract.safeToAutoRun, false);
2005
+ assert.equal(approvedScopeOwnerFileDriftDecision.nextActionContract.requiresHumanApproval, true);
2006
+ assert.equal(approvedScopeOwnerFileDriftDecision.nextActionContract.decisionBasis.ownerFilesChangedSinceApproval, true);
2007
+ assert.equal(approvedScopeOwnerFileDriftDecision.nextActionContract.decisionBasis.requiresOperatorApprovalForAnyScopeChange, true);
2008
+ assert.equal(approvedScopeOwnerFileDriftDecision.managerExecutionPacket.status, 'manual_required');
2009
+
2010
+ const approvedScopeFingerprintDriftDecision = decideResolveIOSupportV5AutonomousNextAction({
2011
+ bundle: diagnosedBundle,
2012
+ changedFiles: ['geochem/angular/app/bol/new/bol-new.component.ts'],
2013
+ requireAutonomyApprovalForProductRepair: true,
2014
+ autonomyApproval: {
2015
+ contract_id: 'support-autonomy-scope-fingerprint-drift',
2016
+ status: 'approved_autopilot',
2017
+ approved: true,
2018
+ approved_at: '2026-06-06T00:02:37.000Z',
2019
+ approved_by: 'operator',
2020
+ estimated_hours: 2,
2021
+ max_auto_hours_without_approval: 6,
2022
+ bug_not_bug: 'bug',
2023
+ bug_not_bug_classification_approved: true,
2024
+ after_approval_autopilot_until_a_grade: true,
2025
+ target_grade: 'A',
2026
+ owner_files: validDiagnosis.owner_files,
2027
+ approved_owner_files: validDiagnosis.owner_files,
2028
+ scope_fingerprint: 'current-scope-fingerprint',
2029
+ previous_scope_fingerprint: 'approved-scope-fingerprint'
2030
+ }
2031
+ });
2032
+ assert.equal(approvedScopeFingerprintDriftDecision.autonomyApproval?.autopilotApprovalBoundaryValid, true);
2033
+ assert.equal(approvedScopeFingerprintDriftDecision.autonomyApproval?.scopeChangedSinceApproval, false);
2034
+ assert.equal(approvedScopeFingerprintDriftDecision.productRepairDispatchGuard?.status, 'allowed');
2035
+ assert.equal(approvedScopeFingerprintDriftDecision.productRepairDispatchGuard?.requiresScopeReapproval, false);
2036
+ assert.equal(approvedScopeFingerprintDriftDecision.nextActionContract.safeToAutoRun, true);
2037
+ assert.equal(approvedScopeFingerprintDriftDecision.nextActionContract.requiresHumanApproval, false);
2038
+ assert.equal(approvedScopeFingerprintDriftDecision.nextActionContract.decisionBasis.scopeChangedSinceApproval, false);
2039
+ assert.notEqual(approvedScopeFingerprintDriftDecision.managerExecutionPacket.status, 'manual_required');
2040
+
2041
+ const approvedDiagnosisFingerprintDriftDecision = decideResolveIOSupportV5AutonomousNextAction({
2042
+ bundle: diagnosedBundle,
2043
+ changedFiles: ['geochem/angular/app/bol/new/bol-new.component.ts'],
2044
+ requireAutonomyApprovalForProductRepair: true,
2045
+ autonomyApproval: {
2046
+ contract_id: 'support-autonomy-diagnosis-fingerprint-drift',
2047
+ status: 'approved_autopilot',
2048
+ approved: true,
2049
+ approved_at: '2026-06-06T00:02:38.000Z',
2050
+ approved_by: 'operator',
2051
+ estimated_hours: 2,
2052
+ max_auto_hours_without_approval: 6,
2053
+ bug_not_bug: 'bug',
2054
+ bug_not_bug_classification_approved: true,
2055
+ after_approval_autopilot_until_a_grade: true,
2056
+ target_grade: 'A',
2057
+ owner_files: validDiagnosis.owner_files,
2058
+ approved_owner_files: validDiagnosis.owner_files,
2059
+ diagnosis_scope_included: true,
2060
+ diagnosis_scope_valid: true,
2061
+ diagnosis_scope_fingerprint: 'current-diagnosis-proof-plan',
2062
+ previous_diagnosis_scope_fingerprint: 'approved-diagnosis-proof-plan',
2063
+ diagnosis_scope_fields: ['issue_case', 'accepted_hypothesis', 'failing_path', 'proof_plan']
2064
+ }
2065
+ });
2066
+ assert.equal(approvedDiagnosisFingerprintDriftDecision.autonomyApproval?.autopilotApprovalBoundaryValid, false);
2067
+ assert.equal(approvedDiagnosisFingerprintDriftDecision.autonomyApproval?.diagnosisScopeChangedSinceApproval, true);
2068
+ assert.equal(approvedDiagnosisFingerprintDriftDecision.autonomyApproval?.diagnosisScopeIncluded, true);
2069
+ assert.equal(approvedDiagnosisFingerprintDriftDecision.productRepairDispatchGuard?.status, 'blocked');
2070
+ assert.equal(approvedDiagnosisFingerprintDriftDecision.productRepairDispatchGuard?.requiresDiagnosisScopeReapproval, true);
2071
+ assert.equal(approvedDiagnosisFingerprintDriftDecision.nextActionContract.safeToAutoRun, false);
2072
+ assert.equal(approvedDiagnosisFingerprintDriftDecision.nextActionContract.requiresHumanApproval, true);
2073
+ assert.equal(approvedDiagnosisFingerprintDriftDecision.nextActionContract.decisionBasis.diagnosisScopeChangedSinceApproval, true);
2074
+ assert.equal(approvedDiagnosisFingerprintDriftDecision.managerExecutionPacket.status, 'manual_required');
2075
+
2076
+ const overSixWithoutExplicitApprovalDecision = decideResolveIOSupportV5AutonomousNextAction({
2077
+ bundle: diagnosedBundle,
2078
+ changedFiles: ['geochem/angular/app/bol/new/bol-new.component.ts'],
2079
+ requireAutonomyApprovalForProductRepair: true,
2080
+ autonomyApproval: {
2081
+ contract_id: 'support-autonomy-over-six-without-explicit',
2082
+ status: 'approved_autopilot',
2083
+ approved: true,
2084
+ approved_at: '2026-06-06T00:02:40.000Z',
2085
+ approved_by: 'operator',
2086
+ estimated_hours: 7,
2087
+ max_auto_hours_without_approval: 6,
2088
+ over_max_auto_hours: true,
2089
+ bug_not_bug: 'bug',
2090
+ bug_not_bug_classification_approved: true,
2091
+ after_approval_autopilot_until_a_grade: true,
2092
+ target_grade: 'A',
2093
+ owner_files: validDiagnosis.owner_files,
2094
+ approved_owner_files: validDiagnosis.owner_files
2095
+ }
2096
+ });
2097
+ assert.equal(overSixWithoutExplicitApprovalDecision.action, 'run_owner_scoped_repair');
2098
+ assert.equal(overSixWithoutExplicitApprovalDecision.autonomyApproval?.autopilotApprovalBoundaryValid, false);
2099
+ assert.equal(overSixWithoutExplicitApprovalDecision.autonomyApproval?.requiresOverLimitApproval, true);
2100
+ assert.equal(overSixWithoutExplicitApprovalDecision.autonomyApproval?.explicitOverLimitApproval, false);
2101
+ assert.equal(overSixWithoutExplicitApprovalDecision.productRepairDispatchGuard?.status, 'blocked');
2102
+ assert.equal(overSixWithoutExplicitApprovalDecision.productRepairDispatchGuard?.nextAction, 'approve_support_autonomy_over_limit');
2103
+ assert.equal(overSixWithoutExplicitApprovalDecision.nextActionContract.safeToAutoRun, false);
2104
+ assert.equal(overSixWithoutExplicitApprovalDecision.nextActionContract.requiresHumanApproval, true);
2105
+ assert.equal(overSixWithoutExplicitApprovalDecision.nextActionContract.decisionBasis.overMaxAutoHours, true);
2106
+ assert.equal(overSixWithoutExplicitApprovalDecision.nextActionContract.decisionBasis.maxAutoHoursWithoutApproval, 6);
2107
+ assert.equal(overSixWithoutExplicitApprovalDecision.managerExecutionPacket.status, 'manual_required');
2108
+ assert.equal(overSixWithoutExplicitApprovalDecision.humanDecisionRequest?.preferredChoiceId, 'approve_over_limit_autonomy');
2109
+ assert.ok(overSixWithoutExplicitApprovalDecision.humanDecisionRequest?.choices.some((choice) => choice.choiceId === 'approve_over_limit_autonomy' && choice.action === 'approve_support_autonomy_over_limit'));
2110
+ assert.ok(overSixWithoutExplicitApprovalDecision.nextActionContract.stopConditions.some((entry) => /over 6 hours/i.test(entry)));
2111
+
2112
+ const malformedOwnerRepairContract = validateResolveIOSupportNextActionContract({
2113
+ ...ownerScopedAutonomousDecision.nextActionContract,
2114
+ rootCauseFirstSatisfied: false,
2115
+ ownerFiles: []
2116
+ });
2117
+ assert.equal(malformedOwnerRepairContract.valid, false);
2118
+ assert.ok(malformedOwnerRepairContract.blockers.some((blocker) => /rootCauseFirstSatisfied|ownerFiles/i.test(blocker)));
2119
+ const blockedOwnerRepairPacket = buildResolveIOSupportManagerExecutionPacket({
2120
+ ...ownerScopedAutonomousDecision.nextActionContract,
2121
+ rootCauseFirstSatisfied: false,
2122
+ ownerFiles: []
2123
+ });
2124
+ assert.equal(blockedOwnerRepairPacket.status, 'blocked');
2125
+ assert.equal(blockedOwnerRepairPacket.executeNow, false);
2126
+
2127
+ const outOfScopeAutonomousDecision = decideResolveIOSupportV5AutonomousNextAction({
2128
+ bundle: diagnosedBundle,
2129
+ changedFiles: ['geochem/server/src/methods/unrelated.ts']
2130
+ });
2131
+ assert.equal(outOfScopeAutonomousDecision.action, 'revise_diagnosis_scope');
2132
+ assert.equal(outOfScopeAutonomousDecision.canEditProductCode, false);
2133
+ assert.equal(outOfScopeAutonomousDecision.rootCauseReadiness.rootCauseFirstSatisfied, true);
2134
+ assert.equal(outOfScopeAutonomousDecision.rootCauseReadiness.ownerScopedRepairContractReady, false);
2135
+ assert.equal(outOfScopeAutonomousDecision.rootCauseReadiness.canEditProductCode, false);
2136
+ assert.equal(outOfScopeAutonomousDecision.nextActionContract.decisionBasis.ownerScopedRepairContractReady, false);
2137
+ assert.equal(outOfScopeAutonomousDecision.nextActionContract.decisionBasis.ownerScopedRepairContractId, outOfScopeAutonomousDecision.repairGate.ownerScopedRepairContract.contractId);
2138
+ assert.ok(outOfScopeAutonomousDecision.blockers.some((blocker) => /outside diagnosis owner_files/i.test(blocker)));
2139
+
2140
+ const infraAutonomousDecision = decideResolveIOSupportV5AutonomousNextAction({
2141
+ bundle: diagnosedBundle,
2142
+ failureClass: 'infra',
2143
+ blocker: 'Puppeteer executable missing.'
2144
+ });
2145
+ assert.equal(infraAutonomousDecision.action, 'repair_infra_only');
2146
+ assert.equal(infraAutonomousDecision.canRunAutonomously, true);
2147
+ assert.equal(infraAutonomousDecision.canEditProductCode, false);
2148
+ assert.equal(infraAutonomousDecision.canRunModel, false);
2149
+ assert.equal(infraAutonomousDecision.rootCauseReadiness.status, 'infra_repair_only');
2150
+ assert.equal(infraAutonomousDecision.rootCauseReadiness.infraOnly, true);
2151
+ assert.equal(infraAutonomousDecision.rootCauseReadiness.canEditProductCode, false);
2152
+ assert.equal(infraAutonomousDecision.evidenceFreshness.status, 'infra_ignored');
2153
+ assert.equal(infraAutonomousDecision.evidenceFreshness.productRepairFailure, false);
2154
+
2155
+ const preflightBlockedAutonomousDecision = decideResolveIOSupportV5AutonomousNextAction({
2156
+ bundle: diagnosedBundle,
2157
+ preflightGate: failedPuppeteerPreflight
2158
+ });
2159
+ assert.equal(preflightBlockedAutonomousDecision.action, 'repair_infra_only');
2160
+ assert.equal(preflightBlockedAutonomousDecision.primaryCommand, 'run_support_v5_infra_repair');
2161
+ assert.equal(preflightBlockedAutonomousDecision.canRunModel, false);
2162
+ assert.equal(preflightBlockedAutonomousDecision.canEditProductCode, false);
2163
+ assert.equal(preflightBlockedAutonomousDecision.preflightGate.status, 'infra_failed');
2164
+ assert.equal(preflightBlockedAutonomousDecision.rootCauseReadiness.preflightReady, false);
2165
+ assert.equal(preflightBlockedAutonomousDecision.rootCauseReadiness.rootCauseFirstSatisfied, false);
2166
+ assert.equal(preflightBlockedAutonomousDecision.nextActionContract.decisionBasis.preflightStatus, 'infra_failed');
2167
+ assert.ok(preflightBlockedAutonomousDecision.nextActionContract.successEvidence.some((entry) => /Puppeteer\/Chrome\/server\/client\/Mongo/i.test(entry)));
2168
+
2169
+ const compilePreflightAutonomousDecision = decideResolveIOSupportV5AutonomousNextAction({
2170
+ bundle: diagnosedBundle,
2171
+ preflightGate: compilePreflight
2172
+ });
2173
+ assert.equal(compilePreflightAutonomousDecision.action, 'repair_infra_only');
2174
+ assert.equal(compilePreflightAutonomousDecision.label, 'Repair Compile Only');
2175
+ assert.equal(compilePreflightAutonomousDecision.primaryCommand, 'run_support_v5_compile_repair');
2176
+ assert.equal(compilePreflightAutonomousDecision.canRunModel, false);
2177
+ assert.equal(compilePreflightAutonomousDecision.nextActionContract.decisionBasis.preflightFailureClass, 'compile');
2178
+ assert.ok(compilePreflightAutonomousDecision.requiredEvidence.some((entry) => /compile command output/i.test(entry)));
2179
+
2180
+ const missingRequiredPreflightAutonomousDecision = decideResolveIOSupportV5AutonomousNextAction({
2181
+ bundle: diagnosedBundle,
2182
+ preflightGate: { required: true }
2183
+ });
2184
+ assert.equal(missingRequiredPreflightAutonomousDecision.action, 'repair_infra_only');
2185
+ assert.equal(missingRequiredPreflightAutonomousDecision.primaryCommand, 'run_support_v5_preflight');
2186
+ assert.equal(missingRequiredPreflightAutonomousDecision.canRunModel, false);
2187
+ assert.ok(missingRequiredPreflightAutonomousDecision.requiredEvidence.some((entry) => /support preflight result/i.test(entry)));
2188
+
2189
+ const pendingReleaseDoesNotPreemptRepairDecision = decideResolveIOSupportV5AutonomousNextAction({
2190
+ bundle: diagnosedBundle,
2191
+ releaseStatus: 'pending_manual'
2192
+ });
2193
+ assert.equal(pendingReleaseDoesNotPreemptRepairDecision.action, 'run_owner_scoped_repair');
2194
+ assert.equal(pendingReleaseDoesNotPreemptRepairDecision.canEditProductCode, true);
2195
+
2196
+ const releaseNeedsCommittedHotfixDecision = decideResolveIOSupportV5AutonomousNextAction({
2197
+ bundle: diagnosedBundle,
2198
+ releaseStatus: 'failed',
2199
+ unresolvedBlockers: ['Duplicate deploy loop after a backend runner hotfix.']
2200
+ });
2201
+ assert.equal(releaseNeedsCommittedHotfixDecision.action, 'repair_release_hotfix_first');
2202
+ assert.equal(releaseNeedsCommittedHotfixDecision.primaryCommand, 'record_hotfix_evidence');
2203
+ assert.equal(releaseNeedsCommittedHotfixDecision.canEditProductCode, false);
2204
+ assert.equal(releaseNeedsCommittedHotfixDecision.canRunModel, false);
2205
+ assert.equal(releaseNeedsCommittedHotfixDecision.canPrepareHotfixPatch, true);
2206
+ assert.equal(releaseNeedsCommittedHotfixDecision.canHotfixBackend, false);
2207
+ assert.equal(releaseNeedsCommittedHotfixDecision.liveHotfixBlockedUntilCommit, true);
2208
+ assert.equal(releaseNeedsCommittedHotfixDecision.rootCauseReadiness.status, 'release_hotfix_required');
2209
+ assert.equal(releaseNeedsCommittedHotfixDecision.rootCauseReadiness.nextGate, 'release');
2210
+ assert.equal(releaseNeedsCommittedHotfixDecision.hotfixContinuation?.action, 'record_hotfix_evidence');
2211
+ assert.ok(releaseNeedsCommittedHotfixDecision.requiredEvidence.some((entry) => /sourceCommitSha.*githubCommitUrl.*gitCommitStatus.*gitPushStatus/i.test(entry)));
2212
+ assert.ok(releaseNeedsCommittedHotfixDecision.blockers.some((blocker) => /Hotfix evidence is missing/i.test(blocker)));
2213
+ assert.ok(releaseNeedsCommittedHotfixDecision.forbiddenActions.some((action) => /committed and pushed to GitHub/i.test(action)));
2214
+ assert.ok(releaseNeedsCommittedHotfixDecision.forbiddenActions.some((action) => /gitPushStatus/i.test(action)));
2215
+ assert.ok(releaseNeedsCommittedHotfixDecision.nextCommands.indexOf('commit_and_push_hotfix_to_github') >= 0);
2216
+ assert.ok(releaseNeedsCommittedHotfixDecision.nextCommands.indexOf('commit_and_push_hotfix_to_github') < releaseNeedsCommittedHotfixDecision.nextCommands.indexOf('apply_backend_hotfix_only_after_commit_proof'));
2217
+ assert.equal(releaseNeedsCommittedHotfixDecision.nextActionContract.action, 'repair_release_hotfix_first');
2218
+ assert.equal(releaseNeedsCommittedHotfixDecision.nextActionContract.safeToAutoRun, true);
2219
+ assert.equal(releaseNeedsCommittedHotfixDecision.nextActionContract.canRunWithoutCodexMonitor, true);
2220
+ assert.equal(releaseNeedsCommittedHotfixDecision.nextActionContract.codexFallbackRequired, false);
2221
+ assert.equal(releaseNeedsCommittedHotfixDecision.nextActionContract.decisionBasis.hotfixCommitRequired, true);
2222
+ assert.equal(releaseNeedsCommittedHotfixDecision.nextActionContract.decisionBasis.liveHotfixBlockedUntilCommit, true);
2223
+ assert.ok(releaseNeedsCommittedHotfixDecision.nextActionContract.stopConditions.some((entry) => /sourceCommitSha.*githubCommitUrl.*gitCommitStatus.*gitPushStatus/i.test(entry)));
2224
+ assert.equal(validateResolveIOSupportNextActionContract(releaseNeedsCommittedHotfixDecision.nextActionContract).valid, true);
2225
+ assert.equal(releaseNeedsCommittedHotfixDecision.managerExecutionPacket.status, 'auto_ready');
2226
+ assert.equal(releaseNeedsCommittedHotfixDecision.managerExecutionPacket.primaryCommand, 'record_hotfix_evidence');
2227
+ assert.equal(releaseNeedsCommittedHotfixDecision.managerExecutionPacket.retryScope, 'release_hotfix_only');
2228
+ assert.ok(releaseNeedsCommittedHotfixDecision.managerExecutionPacket.proofRequiredBeforeContinuation.some((entry) => /sourceCommitSha.*githubCommitUrl.*gitCommitStatus.*gitPushStatus/i.test(entry)));
2229
+ assert.equal(releaseNeedsCommittedHotfixDecision.hotfixDurabilityContract?.status, 'waiting_for_commit_proof');
2230
+ assert.equal(releaseNeedsCommittedHotfixDecision.hotfixDurabilityContract?.liveHotfixBlockedUntilCommit, true);
2231
+ assert.equal(releaseNeedsCommittedHotfixDecision.hotfixDurabilityContract?.canHotfixBackend, false);
2232
+ assert.equal(releaseNeedsCommittedHotfixDecision.hotfixDurabilityContract?.nextSafeAction, 'commit_and_push_hotfix_to_github');
2233
+ assert.ok(releaseNeedsCommittedHotfixDecision.hotfixDurabilityContract?.missingCommitProofFields.includes('sourceCommitSha'));
2234
+
2235
+ const releaseAfterCommittedHotfixDecision = decideResolveIOSupportV5AutonomousNextAction({
2236
+ bundle: diagnosedBundle,
2237
+ releaseStatus: 'failed',
2238
+ unresolvedBlockers: ['Release gate failed before hotfix evidence was recorded.'],
2239
+ releaseGatePassed: true,
2240
+ hotfixEvidence: {
2241
+ channel: 'backend_js',
2242
+ target: {
2243
+ host: 'backend.resolveio.com',
2244
+ path: '/var/app/current/http/support.js'
2245
+ },
2246
+ compiledArtifactPath: 'dist/http/support.js',
2247
+ remoteChecksumBefore: '1111111111111111111111111111111111111111111111111111111111111111',
2248
+ remoteChecksumAfter: '2222222222222222222222222222222222222222222222222222222222222222',
2249
+ sourceCommitSha: 'dd20c124012757ff7d66c682e4b7aad45c402d87',
2250
+ githubCommitUrl: 'https://github.com/resolveio/resolveio-all/commit/dd20c124012757ff7d66c682e4b7aad45c402d87',
2251
+ gitCommitStatus: 'passed',
2252
+ gitPushStatus: 'passed',
2253
+ restartEvidence: 'nodejs and resolveio_support_ticket_codex_manager restarted on backend.resolveio.com and workers',
2254
+ healthCheckStatus: 'passed',
2255
+ selfTestStatus: 'passed',
2256
+ releaseGateStatus: 'passed'
2257
+ }
2258
+ });
2259
+ assert.equal(releaseAfterCommittedHotfixDecision.action, 'repair_release_hotfix_first');
2260
+ assert.equal(releaseAfterCommittedHotfixDecision.primaryCommand, 'continue_support_runner_after_committed_hotfix');
2261
+ assert.equal(releaseAfterCommittedHotfixDecision.hotfixContinuation?.action, 'continue_runner');
2262
+ assert.equal(releaseAfterCommittedHotfixDecision.hotfixContinuation?.canContinueRun, true);
2263
+ assert.equal(releaseAfterCommittedHotfixDecision.canPrepareHotfixPatch, true);
2264
+ assert.equal(releaseAfterCommittedHotfixDecision.canHotfixBackend, true);
2265
+ assert.equal(releaseAfterCommittedHotfixDecision.liveHotfixBlockedUntilCommit, false);
2266
+ assert.equal(releaseAfterCommittedHotfixDecision.nextActionContract.decisionBasis.liveHotfixBlockedUntilCommit, false);
2267
+ assert.equal(releaseAfterCommittedHotfixDecision.nextActionContract.codexFallbackRequired, false);
2268
+ assert.equal(releaseAfterCommittedHotfixDecision.hotfixDurabilityContract?.status, 'ready_for_continuation');
2269
+ assert.equal(releaseAfterCommittedHotfixDecision.hotfixDurabilityContract?.commitProofPassed, true);
2270
+ assert.deepEqual(releaseAfterCommittedHotfixDecision.hotfixDurabilityContract?.missingCommitProofFields, []);
2271
+
2272
+ const completedDiagnosedBundle = {
2273
+ ...diagnosedBundle,
2274
+ supportV5ActiveMicrotaskId: undefined,
2275
+ supportV5MicrotaskLedger: diagnosedBundle.supportV5MicrotaskLedger.map((task) => ({
2276
+ ...task,
2277
+ status: 'pass' as const
2278
+ }))
2279
+ };
2280
+ const approvedAutopilotContract = {
2281
+ contract_id: 'support-autonomy-proof-ready-approved',
2282
+ status: 'approved_autopilot',
2283
+ approved: true,
2284
+ approved_at: '2026-06-06T00:03:00.000Z',
2285
+ approved_by: 'operator',
2286
+ estimated_hours: 2,
2287
+ max_auto_hours_without_approval: 6,
2288
+ bug_not_bug: 'bug',
2289
+ bug_not_bug_classification_approved: true,
2290
+ after_approval_autopilot_until_a_grade: true,
2291
+ target_grade: 'A',
2292
+ qa_screenshots_required: true,
2293
+ before_action_after_business_proof_required: true,
2294
+ aiqa_business_assertion_required: true,
2295
+ owner_files: validDiagnosis.owner_files,
2296
+ approved_owner_files: validDiagnosis.owner_files,
2297
+ required_completion_evidence: [
2298
+ 'A-grade execution/artifacts/pull_request review',
2299
+ 'QA screenshots with captions',
2300
+ 'AIQaBusinessAssertion before/action/after business proof',
2301
+ 'changed files subset of approved owner_files',
2302
+ 'pull_request_url'
2303
+ ]
2304
+ };
2305
+ const approvedAutopilotBusinessAssertions = [{
2306
+ assertion: validDiagnosis.proof_plan.business_assertion,
2307
+ status: 'pass',
2308
+ workflow: validDiagnosis.proof_plan.business_proof_contract.action_under_test,
2309
+ action: validDiagnosis.proof_plan.action,
2310
+ expected: validDiagnosis.proof_plan.business_proof_contract.expected_business_state_change,
2311
+ observed: 'The disabled driver option is absent and the active driver remains selected.',
2312
+ dataProof: validDiagnosis.proof_plan.business_proof_contract.data_or_dom_assertion,
2313
+ artifactPaths: ['qa-artifacts/disabled-driver-before-after-proof.png'],
2314
+ metadata: { supportDiagnosisProof: true }
2315
+ }];
2316
+ const approvedAutopilotCreatePrDecision = decideResolveIOSupportV5AutonomousNextAction({
2317
+ bundle: completedDiagnosedBundle,
2318
+ changedFiles: validDiagnosis.owner_files,
2319
+ businessAssertionStatus: 'pass',
2320
+ businessAssertions: approvedAutopilotBusinessAssertions,
2321
+ businessProofArtifacts: ['qa-artifacts/disabled-driver-before-after-proof.png'],
2322
+ autonomyApproval: approvedAutopilotContract,
2323
+ prReadinessContract: {
2324
+ contract_id: 'support-pr-readiness-proof-ready-no-pr',
2325
+ status: 'ready',
2326
+ ready: true,
2327
+ business_proof_ready: true,
2328
+ diagnosis_valid: true,
2329
+ passed_business_assertion_count: 1,
2330
+ business_proof_artifacts: ['qa-artifacts/disabled-driver-before-after-proof.png'],
2331
+ modified_files: validDiagnosis.owner_files,
2332
+ pull_request_url: '',
2333
+ merge_blocked_until_human_approval: true,
2334
+ customer_reply_blocked_until_release_gate: true,
2335
+ release_blocked_until_pr_review: true
2336
+ }
2337
+ });
2338
+ assert.equal(approvedAutopilotCreatePrDecision.action, 'create_pr');
2339
+ assert.equal(approvedAutopilotCreatePrDecision.primaryCommand, 'createSupportTicketCodexPullRequestManual');
2340
+ assert.equal(approvedAutopilotCreatePrDecision.autopilotCompletionProgress?.activePhase, 'pr_packaging');
2341
+ assert.equal(approvedAutopilotCreatePrDecision.autopilotCompletionProgress?.status, 'ready_for_pr');
2342
+ assert.equal(approvedAutopilotCreatePrDecision.autopilotCompletionProgress?.nextAction, 'create_pr');
2343
+ assert.equal(approvedAutopilotCreatePrDecision.nextActionContract.decisionBasis.autopilotCompletionPhase, 'pr_packaging');
2344
+ assert.equal(approvedAutopilotCreatePrDecision.nextActionContract.decisionBasis.autopilotCompletionNextAction, 'create_pr');
2345
+ assert.equal(approvedAutopilotCreatePrDecision.managerExecutionPacket.retryScope, 'pr_packaging_only');
2346
+ assert.equal(approvedAutopilotCreatePrDecision.managerExecutionPacket.status, 'auto_ready');
2347
+ assert.ok(approvedAutopilotCreatePrDecision.forbiddenActions.some((entry) => /merge|release|deploy|customer/i.test(entry)));
2348
+
2349
+ const approvedAutopilotPrReviewDecision = decideResolveIOSupportV5AutonomousNextAction({
2350
+ bundle: completedDiagnosedBundle,
2351
+ changedFiles: validDiagnosis.owner_files,
2352
+ businessAssertionStatus: 'pass',
2353
+ businessAssertions: approvedAutopilotBusinessAssertions,
2354
+ businessProofArtifacts: ['qa-artifacts/disabled-driver-before-after-proof.png'],
2355
+ autonomyApproval: approvedAutopilotContract,
2356
+ prReadinessContract: {
2357
+ contract_id: 'support-pr-readiness-proof-ready-with-pr',
2358
+ status: 'ready',
2359
+ ready: true,
2360
+ business_proof_ready: true,
2361
+ diagnosis_valid: true,
2362
+ passed_business_assertion_count: 1,
2363
+ business_proof_artifacts: ['qa-artifacts/disabled-driver-before-after-proof.png'],
2364
+ modified_files: validDiagnosis.owner_files,
2365
+ pull_request_url: 'https://github.com/resolveio/resolveio-all/pull/4999',
2366
+ pull_request_status: 'OPEN',
2367
+ merge_blocked_until_human_approval: true,
2368
+ customer_reply_blocked_until_release_gate: true,
2369
+ release_blocked_until_pr_review: true
2370
+ }
2371
+ });
2372
+ assert.equal(approvedAutopilotPrReviewDecision.action, 'run_a_grade_pr_review');
2373
+ assert.equal(approvedAutopilotPrReviewDecision.primaryCommand, 'run_support_a_grade_pr_review');
2374
+ assert.equal(approvedAutopilotPrReviewDecision.autopilotCompletionProgress?.activePhase, 'pr_review');
2375
+ assert.equal(approvedAutopilotPrReviewDecision.autopilotCompletionProgress?.status, 'in_progress');
2376
+ assert.equal(approvedAutopilotPrReviewDecision.autopilotCompletionProgress?.pullRequestUrl, 'https://github.com/resolveio/resolveio-all/pull/4999');
2377
+ assert.equal(approvedAutopilotPrReviewDecision.autopilotCompletionProgress?.prReviewReady, false);
2378
+ assert.equal(approvedAutopilotPrReviewDecision.nextActionContract.decisionBasis.autopilotCompletionReady, false);
2379
+ assert.equal(approvedAutopilotPrReviewDecision.managerExecutionPacket.retryScope, 'pr_review_only');
2380
+ assert.equal(approvedAutopilotPrReviewDecision.managerExecutionPacket.status, 'auto_ready');
2381
+
2382
+ const approvedAutopilotPrBusinessProofReviewDecision = decideResolveIOSupportV5AutonomousNextAction({
2383
+ bundle: completedDiagnosedBundle,
2384
+ changedFiles: validDiagnosis.owner_files,
2385
+ businessAssertionStatus: 'pass',
2386
+ businessAssertions: approvedAutopilotBusinessAssertions,
2387
+ businessProofArtifacts: ['qa-artifacts/disabled-driver-before-after-proof.png'],
2388
+ autonomyApproval: approvedAutopilotContract,
2389
+ prReadinessContract: {
2390
+ contract_id: 'support-pr-readiness-proof-ready-with-pr',
2391
+ status: 'ready',
2392
+ ready: true,
2393
+ business_proof_ready: true,
2394
+ diagnosis_valid: true,
2395
+ passed_business_assertion_count: 1,
2396
+ business_proof_artifacts: ['qa-artifacts/disabled-driver-before-after-proof.png'],
2397
+ modified_files: validDiagnosis.owner_files,
2398
+ pull_request_url: 'https://github.com/resolveio/resolveio-all/pull/4999',
2399
+ pull_request_status: 'OPEN',
2400
+ merge_blocked_until_human_approval: true,
2401
+ customer_reply_blocked_until_release_gate: true,
2402
+ release_blocked_until_pr_review: true
2403
+ },
2404
+ prReviewContract: {
2405
+ contract_id: 'support-pr-review-needs-business-proof',
2406
+ status: 'blocked',
2407
+ ready: false,
2408
+ strict_gate_passed: false,
2409
+ strict_gate_reason: 'missing_before_action_after_business_proof',
2410
+ failure_class: 'business_proof',
2411
+ blocker_fingerprint: 'bf-business-proof',
2412
+ evidence_hash: 'ev-business-proof',
2413
+ next_action: 'run_business_proof_qa',
2414
+ blockers: ['Strict gate failed: missing before/action/after business proof.'],
2415
+ continuation_contract: {
2416
+ action: 'run_business_proof_qa',
2417
+ failure_class: 'business_proof',
2418
+ blocker_fingerprint: 'bf-business-proof',
2419
+ evidence_hash: 'ev-business-proof'
2420
+ }
2421
+ }
2422
+ });
2423
+ assert.equal(approvedAutopilotPrBusinessProofReviewDecision.action, 'run_business_proof_qa');
2424
+ assert.equal(approvedAutopilotPrBusinessProofReviewDecision.managerExecutionPacket.retryScope, 'business_proof_only');
2425
+ assert.equal(approvedAutopilotPrBusinessProofReviewDecision.managerExecutionPacket.proofResetContract.startingFailureClass, 'business_proof');
2426
+ assert.equal(approvedAutopilotPrBusinessProofReviewDecision.managerExecutionPacket.proofResetContract.startingBlockerFingerprint, 'bf-business-proof');
2427
+ assert.equal(approvedAutopilotPrBusinessProofReviewDecision.managerExecutionPacket.proofResetContract.startingEvidenceHash, 'ev-business-proof');
2428
+ assert.ok(approvedAutopilotPrBusinessProofReviewDecision.managerExecutionPacket.proofResetContract.requiredResetEvidence.some((entry) => /changed evidence_hash|AIQaBusinessAssertion|fresh artifact/i.test(entry)));
2429
+ assert.ok(approvedAutopilotPrBusinessProofReviewDecision.blockers.some((entry) => /blocker_fingerprint:bf-business-proof/.test(entry)));
2430
+
2431
+ const approvedAutopilotPrOwnerRepairReviewDecision = decideResolveIOSupportV5AutonomousNextAction({
2432
+ bundle: completedDiagnosedBundle,
2433
+ changedFiles: validDiagnosis.owner_files,
2434
+ businessAssertionStatus: 'pass',
2435
+ businessAssertions: approvedAutopilotBusinessAssertions,
2436
+ businessProofArtifacts: ['qa-artifacts/disabled-driver-before-after-proof.png'],
2437
+ autonomyApproval: approvedAutopilotContract,
2438
+ prReadinessContract: {
2439
+ contract_id: 'support-pr-readiness-proof-ready-with-pr',
2440
+ status: 'ready',
2441
+ ready: true,
2442
+ business_proof_ready: true,
2443
+ diagnosis_valid: true,
2444
+ passed_business_assertion_count: 1,
2445
+ business_proof_artifacts: ['qa-artifacts/disabled-driver-before-after-proof.png'],
2446
+ modified_files: validDiagnosis.owner_files,
2447
+ pull_request_url: 'https://github.com/resolveio/resolveio-all/pull/4999',
2448
+ pull_request_status: 'OPEN',
2449
+ merge_blocked_until_human_approval: true,
2450
+ customer_reply_blocked_until_release_gate: true,
2451
+ release_blocked_until_pr_review: true
2452
+ },
2453
+ prReviewContract: {
2454
+ contract_id: 'support-pr-review-needs-owner-repair',
2455
+ status: 'blocked',
2456
+ ready: false,
2457
+ strict_gate_passed: false,
2458
+ strict_gate_reason: 'grade_below_target',
2459
+ failure_class: 'grade_below_target',
2460
+ blocker_fingerprint: 'bf-owner-repair',
2461
+ evidence_hash: 'ev-owner-repair',
2462
+ next_action: 'run_owner_scoped_repair',
2463
+ blockers: ['Grades are below A threshold.'],
2464
+ continuation_contract: {
2465
+ action: 'run_owner_scoped_repair',
2466
+ failure_class: 'grade_below_target',
2467
+ blocker_fingerprint: 'bf-owner-repair',
2468
+ evidence_hash: 'ev-owner-repair'
2469
+ }
2470
+ }
2471
+ });
2472
+ assert.equal(approvedAutopilotPrOwnerRepairReviewDecision.action, 'run_owner_scoped_repair');
2473
+ assert.equal(approvedAutopilotPrOwnerRepairReviewDecision.managerExecutionPacket.retryScope, 'owner_files_only');
2474
+ assert.equal(approvedAutopilotPrOwnerRepairReviewDecision.managerExecutionPacket.proofResetContract.startingFailureClass, 'grade_below_target');
2475
+ assert.equal(approvedAutopilotPrOwnerRepairReviewDecision.managerExecutionPacket.proofResetContract.startingBlockerFingerprint, 'bf-owner-repair');
2476
+ assert.equal(approvedAutopilotPrOwnerRepairReviewDecision.managerExecutionPacket.proofResetContract.startingEvidenceHash, 'ev-owner-repair');
2477
+ assert.ok(approvedAutopilotPrOwnerRepairReviewDecision.blockers.some((entry) => /blocker_fingerprint:bf-owner-repair/.test(entry)));
2478
+
2479
+ const approvedAutopilotAgradePrDecision = decideResolveIOSupportV5AutonomousNextAction({
2480
+ bundle: completedDiagnosedBundle,
2481
+ changedFiles: validDiagnosis.owner_files,
2482
+ businessAssertionStatus: 'pass',
2483
+ businessAssertions: approvedAutopilotBusinessAssertions,
2484
+ businessProofArtifacts: ['qa-artifacts/disabled-driver-before-after-proof.png'],
2485
+ autonomyApproval: approvedAutopilotContract,
2486
+ prReadinessContract: {
2487
+ contract_id: 'support-pr-readiness-proof-ready-with-pr',
2488
+ status: 'ready',
2489
+ ready: true,
2490
+ business_proof_ready: true,
2491
+ diagnosis_valid: true,
2492
+ passed_business_assertion_count: 1,
2493
+ business_proof_artifacts: ['qa-artifacts/disabled-driver-before-after-proof.png'],
2494
+ modified_files: validDiagnosis.owner_files,
2495
+ pull_request_url: 'https://github.com/resolveio/resolveio-all/pull/4999',
2496
+ pull_request_status: 'OPEN',
2497
+ merge_blocked_until_human_approval: true,
2498
+ customer_reply_blocked_until_release_gate: true,
2499
+ release_blocked_until_pr_review: true
2500
+ },
2501
+ prReviewContract: {
2502
+ contract_id: 'support-pr-review-a-grade',
2503
+ status: 'passed',
2504
+ ready: true,
2505
+ strict_gate_passed: true,
2506
+ execution_grade: 'A',
2507
+ artifacts_grade: 'A',
2508
+ pull_request_grade: 'A',
2509
+ pull_request_url: 'https://github.com/resolveio/resolveio-all/pull/4999',
2510
+ evidence_refs: ['qa-artifacts/disabled-driver-before-after-proof.png']
2511
+ }
2512
+ });
2513
+ assert.equal(approvedAutopilotAgradePrDecision.action, 'park_manual');
2514
+ assert.equal(approvedAutopilotAgradePrDecision.primaryCommand, 'review_a_grade_pr_and_release_gate');
2515
+ assert.equal(approvedAutopilotAgradePrDecision.autopilotCompletionProgress?.activePhase, 'park_before_merge_release_customer');
2516
+ assert.equal(approvedAutopilotAgradePrDecision.autopilotCompletionProgress?.status, 'complete');
2517
+ assert.equal(approvedAutopilotAgradePrDecision.autopilotCompletionProgress?.prReviewReady, true);
2518
+ assert.equal(approvedAutopilotAgradePrDecision.nextActionContract.decisionBasis.autopilotCompletionReady, true);
2519
+ assert.equal(approvedAutopilotAgradePrDecision.managerExecutionPacket.status, 'manual_required');
2520
+
2521
+ const replyAutonomousDecision = decideResolveIOSupportV5AutonomousNextAction({
2522
+ bundle: completedDiagnosedBundle,
2523
+ outcomeLabel: 'accepted',
2524
+ confidence: { level: 'high', category: 'business_proof', shouldBlockPr: false },
2525
+ businessAssertionStatus: 'pass',
2526
+ businessProofArtifacts: ['qa-artifacts/disabled-driver-dropdown.png'],
2527
+ releaseStatus: 'completed'
2528
+ });
2529
+ assert.equal(replyAutonomousDecision.action, 'draft_customer_reply');
2530
+ assert.equal(replyAutonomousDecision.canDraftCustomerReply, true);
2531
+ assert.equal(replyAutonomousDecision.canSendCustomerReply, false);
2532
+ assert.equal(replyAutonomousDecision.businessProofReadiness.ready, true);
2533
+ assert.equal(replyAutonomousDecision.rootCauseReadiness.status, 'customer_reply_draft_ready');
2534
+ assert.equal(replyAutonomousDecision.rootCauseReadiness.canDraftCustomerReply, true);
2535
+ assert.equal(replyAutonomousDecision.rootCauseReadiness.businessProofReady, true);
2536
+ assert.equal(replyAutonomousDecision.humanReviewPacket.reviewType, 'customer_resolution_reply');
2537
+ assert.equal(replyAutonomousDecision.humanReviewPacket.primaryAction, 'review_customer_reply');
2538
+ assert.equal(replyAutonomousDecision.humanReviewPacket.customerFacingDraftAllowed, true);
2539
+ assert.equal(replyAutonomousDecision.humanReviewPacket.customerSendAllowed, false);
2540
+ assert.equal(validateResolveIOSupportNextActionContract(replyAutonomousDecision.nextActionContract).valid, true);
2541
+ assert.equal(replyAutonomousDecision.managerExecutionPacket.status, 'auto_ready');
2542
+ assert.equal(replyAutonomousDecision.managerExecutionPacket.retryScope, 'customer_draft_only');
2543
+ assert.equal(replyAutonomousDecision.canSendCustomerReply, false);
2544
+
2545
+ const hotfixBlockedReplyAutonomousDecision = decideResolveIOSupportV5AutonomousNextAction({
2546
+ bundle: completedDiagnosedBundle,
2547
+ outcomeLabel: 'accepted',
2548
+ confidence: { level: 'high', category: 'business_proof', shouldBlockPr: false },
2549
+ businessAssertionStatus: 'pass',
2550
+ businessProofArtifacts: ['qa-artifacts/disabled-driver-dropdown.png'],
2551
+ releaseStatus: 'completed',
2552
+ hotfixEvidence: {
2553
+ channel: 'backend_js',
2554
+ target: {
2555
+ host: 'backend.resolveio.com',
2556
+ path: '/var/app/current/http/support.js'
2557
+ },
2558
+ compiledArtifactPath: 'dist/http/support.js',
2559
+ remoteChecksumAfter: '2222222222222222222222222222222222222222222222222222222222222222',
2560
+ sourceCommitSha: 'dd20c124012757ff7d66c682e4b7aad45c402d87',
2561
+ githubCommitUrl: 'https://github.com/resolveio/resolveio-all/commit/dd20c124012757ff7d66c682e4b7aad45c402d87',
2562
+ gitPushStatus: 'passed',
2563
+ restartEvidence: 'nodejs and resolveio_support_ticket_codex_manager restarted on backend.resolveio.com and workers',
2564
+ healthCheckStatus: 'passed',
2565
+ selfTestStatus: 'passed'
2566
+ }
2567
+ });
2568
+ assert.equal(hotfixBlockedReplyAutonomousDecision.action, 'repair_release_hotfix_first');
2569
+ assert.equal(hotfixBlockedReplyAutonomousDecision.reason, 'support_v5_reply_blocked_by_hotfix_evidence');
2570
+ assert.equal(hotfixBlockedReplyAutonomousDecision.canDraftCustomerReply, false);
2571
+ assert.equal(hotfixBlockedReplyAutonomousDecision.liveHotfixBlockedUntilCommit, true);
2572
+ assert.ok(hotfixBlockedReplyAutonomousDecision.requiredEvidence.some((entry) => /gitCommitStatus=passed/i.test(entry)));
2573
+ assert.ok(hotfixBlockedReplyAutonomousDecision.forbiddenActions.some((entry) => /customer resolution/i.test(entry)));
2574
+
2575
+ const routeOnlyAutonomousDecision = decideResolveIOSupportV5AutonomousNextAction({
2576
+ bundle: completedDiagnosedBundle,
2577
+ outcomeLabel: 'accepted',
2578
+ confidence: { level: 'high', category: 'route_probe', shouldBlockPr: false },
2579
+ businessAssertionStatus: 'route_probe_pass',
2580
+ businessProofArtifacts: ['qa-artifacts/route-loaded.png'],
2581
+ releaseStatus: 'completed'
2582
+ });
2583
+ assert.equal(routeOnlyAutonomousDecision.action, 'run_business_proof_qa');
2584
+ assert.equal(routeOnlyAutonomousDecision.canRunQa, true);
2585
+ assert.equal(routeOnlyAutonomousDecision.canRunModel, false);
2586
+ assert.equal(routeOnlyAutonomousDecision.businessProofReadiness.status, 'route_only');
2587
+ assert.equal(routeOnlyAutonomousDecision.rootCauseReadiness.status, 'business_proof_required');
2588
+ assert.equal(routeOnlyAutonomousDecision.rootCauseReadiness.canRunBusinessProofQa, true);
2589
+ assert.equal(routeOnlyAutonomousDecision.rootCauseReadiness.businessProofReady, false);
2590
+ assert.ok(routeOnlyAutonomousDecision.requiredEvidence.some((entry) => /filter_query_mismatch/.test(entry)));
2591
+ assert.ok(routeOnlyAutonomousDecision.requiredEvidence.some((entry) => /included\/excluded row proof|dropdown DOM snapshot/i.test(entry)));
2592
+ assert.ok(routeOnlyAutonomousDecision.forbiddenActions.some((action) => /businessProofReadiness\.ready=true/.test(action)));
2593
+ assert.equal(routeOnlyAutonomousDecision.humanReviewPacket.reviewType, 'business_proof_qa');
2594
+ assert.equal(routeOnlyAutonomousDecision.humanReviewPacket.primaryAction, 'run_support_v5_business_proof_qa_row');
2595
+
2596
+ const staleProofAutonomousDecision = decideResolveIOSupportV5AutonomousNextAction({
2597
+ bundle: completedDiagnosedBundle,
2598
+ outcomeLabel: 'accepted',
2599
+ confidence: { level: 'high', category: 'business_proof', shouldBlockPr: false },
2600
+ businessAssertions: [{
2601
+ assertion: validDiagnosis.proof_plan.business_assertion,
2602
+ status: 'pass',
2603
+ workflow: validDiagnosis.proof_plan.business_proof_contract.action_under_test,
2604
+ action: validDiagnosis.proof_plan.action,
2605
+ expected: validDiagnosis.proof_plan.business_proof_contract.expected_business_state_change,
2606
+ observed: 'The disabled driver option is absent and the active driver remains selected.',
2607
+ dataProof: validDiagnosis.proof_plan.business_proof_contract.data_or_dom_assertion,
2608
+ artifactPaths: ['qa-artifacts/disabled-driver-business-proof.json'],
2609
+ metadata: { supportDiagnosisProof: true }
2610
+ }],
2611
+ previousProofFingerprint: matchedBusinessProofReadiness.proofFingerprint,
2612
+ releaseStatus: 'completed'
2613
+ });
2614
+ assert.equal(staleProofAutonomousDecision.action, 'run_business_proof_qa');
2615
+ assert.equal(staleProofAutonomousDecision.businessProofReadiness.status, 'stale');
2616
+ assert.equal(staleProofAutonomousDecision.rootCauseReadiness.businessProofStatus, 'stale');
2617
+ assert.equal(staleProofAutonomousDecision.rootCauseReadiness.proofFreshness, 'same_as_previous');
2618
+ assert.equal(staleProofAutonomousDecision.rootCauseReadiness.canRelease, false);
2619
+ assert.equal(staleProofAutonomousDecision.rootCauseReadiness.canDraftCustomerReply, false);
2620
+ assert.ok(staleProofAutonomousDecision.blockers.some((blocker) => /new proof\/artifact fingerprint/i.test(blocker)));
2621
+
2622
+ const missingDiagnosisRepairGate = decideResolveIOSupportV5RepairGate({
2623
+ diagnosisGate: undefined,
2624
+ activeStepType: 'build_repair'
2625
+ });
2626
+ assert.equal(missingDiagnosisRepairGate.action, 'diagnose_only');
2627
+ assert.equal(missingDiagnosisRepairGate.recoveryPlan.recoveryClass, 'diagnosis_only');
2628
+ assert.equal(missingDiagnosisRepairGate.recoveryPlan.productRepairAllowed, false);
2629
+ assert.ok(missingDiagnosisRepairGate.recoveryPlan.requiredEvidence.includes('accepted_hypothesis'));
2630
+ assert.equal(missingDiagnosisRepairGate.recoveryCheckpoint.recoveryClass, 'diagnosis_only');
2631
+ assert.equal(missingDiagnosisRepairGate.recoveryCheckpoint.productRepairAllowed, false);
2632
+ assert.equal(missingDiagnosisRepairGate.recoveryAction.mode, 'read_only_diagnosis');
2633
+ assert.equal(missingDiagnosisRepairGate.recoveryAction.allowedDispatchAction, 'run_read_only_diagnosis');
2634
+ assert.equal(missingDiagnosisRepairGate.recoveryAction.productRepairAllowed, false);
2635
+ assert.equal(missingDiagnosisRepairGate.recoveryAction.proofRequiredBeforeContinuation, true);
2636
+ const allowedRepairGate = decideResolveIOSupportV5RepairGate({
2637
+ diagnosisGate: validDiagnosis,
2638
+ activeStepType: 'build_repair',
2639
+ changedFiles: ['geochem/angular/app/bol/new/bol-new.component.ts']
2640
+ });
2641
+ assert.equal(allowedRepairGate.action, 'allow_product_repair');
2642
+ assert.equal(allowedRepairGate.recoveryPlan.recoveryClass, 'product_code_repair');
2643
+ assert.equal(allowedRepairGate.recoveryPlan.productRepairAllowed, true);
2644
+ assert.equal(allowedRepairGate.recoveryCheckpoint.recoveryClass, 'product_code_repair');
2645
+ assert.equal(allowedRepairGate.recoveryCheckpoint.productRepairAllowed, true);
2646
+ const outOfScopeRepairGate = decideResolveIOSupportV5RepairGate({
2647
+ diagnosisGate: validDiagnosis,
2648
+ activeStepType: 'build_repair',
2649
+ changedFiles: ['geochem/server/src/methods/unrelated.ts']
2650
+ });
2651
+ assert.equal(outOfScopeRepairGate.action, 'reject_out_of_scope');
2652
+ assert.equal(outOfScopeRepairGate.recoveryPlan.recoveryClass, 'diagnosis_revision');
2653
+ assert.ok(outOfScopeRepairGate.recoveryPlan.forbiddenActions.some((action) => /outside owner_files/.test(action)));
2654
+ const infraRepairGate = decideResolveIOSupportV5RepairGate({
2655
+ diagnosisGate: validDiagnosis,
2656
+ activeStepType: 'build_repair',
2657
+ failureClass: 'infra',
2658
+ blocker: 'Puppeteer failed to launch.'
2659
+ });
2660
+ assert.equal(infraRepairGate.action, 'infra_repair_only');
2661
+ assert.equal(infraRepairGate.recoveryPlan.recoveryClass, 'infra_repair');
2662
+ assert.equal(infraRepairGate.recoveryPlan.expensiveModelAllowed, false);
2663
+ assert.ok(infraRepairGate.recoveryPlan.forbiddenActions.some((action) => /product-code model repair/.test(action)));
2664
+ assert.equal(infraRepairGate.recoveryCheckpoint.recoveryClass, 'infra_repair');
2665
+ assert.equal(infraRepairGate.recoveryCheckpoint.productRepairAllowed, false);
2666
+ const preflightRepairGate = decideResolveIOSupportV5RepairGate({
2667
+ diagnosisGate: validDiagnosis,
2668
+ activeStepType: 'build_repair',
2669
+ preflightGate: compilePreflight
2670
+ });
2671
+ assert.equal(preflightRepairGate.action, 'infra_repair_only');
2672
+ assert.equal(preflightRepairGate.canEditProductCode, false);
2673
+ assert.equal(preflightRepairGate.preflightGate.status, 'compile_failed');
2674
+ assert.equal(preflightRepairGate.recoveryPlan.recoveryClass, 'compile_repair');
2675
+ assert.equal(preflightRepairGate.recoveryPlan.expensiveModelAllowed, false);
2676
+ const repeatedRepairGate = decideResolveIOSupportV5RepairGate({
2677
+ diagnosisGate: validDiagnosis,
2678
+ activeStepType: 'build_repair',
2679
+ failureClass: 'product_code',
2680
+ blocker: 'same blocker',
2681
+ evidenceHash: 'same',
2682
+ history: [
2683
+ { stepType: 'build_repair', outcome: 'needs_repair', lane: 'build', summary: 'same', failureClass: 'product_code', blocker: 'same blocker', evidenceHash: 'same', recordedAt: '1' },
2684
+ { stepType: 'build_repair', outcome: 'needs_repair', lane: 'build', summary: 'same', failureClass: 'product_code', blocker: 'same blocker', evidenceHash: 'same', recordedAt: '2' }
2685
+ ],
2686
+ maxRepeatedNoProgress: 2
2687
+ });
2688
+ assert.equal(repeatedRepairGate.action, 'park_repeated_failure');
2689
+ assert.equal(repeatedRepairGate.recoveryPlan.recoveryClass, 'blocked_until_new_evidence');
2690
+ assert.ok(repeatedRepairGate.recoveryPlan.loopResetEvidence.some((evidence) => /changed evidence hash/.test(evidence)));
2691
+ assert.equal(repeatedRepairGate.recoveryCheckpoint.status, 'parked');
2692
+ assert.equal(repeatedRepairGate.recoveryCheckpoint.allowedAction, 'collect_new_evidence_only');
2693
+ assert.equal(repeatedRepairGate.recoveryEvidenceProbe.evidenceOnly, true);
2694
+ assert.equal(repeatedRepairGate.recoveryEvidenceProbe.productRepairAllowed, false);
2695
+ assert.ok(repeatedRepairGate.recoveryEvidenceProbe.steps.some((step) => step.kind === 'rerun_same_gate'));
2696
+ assert.equal(repeatedRepairGate.recoveryAction.mode, 'collect_evidence');
2697
+ assert.equal(repeatedRepairGate.recoveryAction.productRepairAllowed, false);
2698
+ assert.equal(repeatedRepairGate.recoveryAction.retryPolicy.requireNewEvidence, true);
2699
+
2700
+ const staleEvidenceFreshness = evaluateResolveIOSupportEvidenceFreshness({
2701
+ history: [
2702
+ { stepType: 'build_repair', outcome: 'needs_repair', lane: 'build', summary: 'same blocker', failureClass: 'product_code', blocker: 'same blocker', evidenceHash: 'same', recordedAt: '1' },
2703
+ { stepType: 'build_repair', outcome: 'needs_repair', lane: 'build', summary: 'same blocker', failureClass: 'product_code', blocker: 'same blocker', evidenceHash: 'same', recordedAt: '2' }
2704
+ ],
2705
+ failureClass: 'product_code',
2706
+ blocker: 'same blocker',
2707
+ evidenceHash: 'same',
2708
+ limit: 2
2709
+ });
2710
+ assert.equal(staleEvidenceFreshness.status, 'stale_repeated');
2711
+ assert.equal(staleEvidenceFreshness.mustCollectNewEvidence, true);
2712
+ assert.equal(staleEvidenceFreshness.canRetry, false);
2713
+ assert.ok(staleEvidenceFreshness.requiredResetEvidence.some((evidence) => /changed evidence hash|new artifact path|business\/compile\/infra\/release proof/i.test(evidence)));
2714
+
2715
+ const staleContinuationProofCheckpoint = buildResolveIOSupportContinuationProofCheckpoint({
2716
+ action: 'collect_new_evidence',
2717
+ reason: staleEvidenceFreshness.reason,
2718
+ evidenceFreshness: staleEvidenceFreshness,
2719
+ requiredEvidence: staleEvidenceFreshness.requiredResetEvidence
2720
+ });
2721
+ assert.equal(staleContinuationProofCheckpoint.status, 'waiting_for_new_evidence');
2722
+ assert.equal(staleContinuationProofCheckpoint.successRequiresNewEvidence, true);
2723
+ assert.equal(staleContinuationProofCheckpoint.blocksProductRepairUntilChangedEvidence, true);
2724
+ assert.equal(staleContinuationProofCheckpoint.startingEvidenceHash, 'same');
2725
+ assert.ok(staleContinuationProofCheckpoint.requiredResetEvidence.some((evidence) => /changed evidence hash|new artifact path|business\/compile\/infra\/release proof/i.test(evidence)));
2726
+
2727
+ const staleEvidenceProbeContract = buildResolveIOSupportEvidenceProbeContract({
2728
+ diagnosisGate: validDiagnosis,
2729
+ issueClassProbePlan: validProbePlan,
2730
+ evidenceFreshness: staleEvidenceFreshness,
2731
+ requiredEvidence: staleEvidenceFreshness.requiredResetEvidence,
2732
+ now: '2026-06-06T00:00:40.000Z'
2733
+ });
2734
+ assert.equal(staleEvidenceProbeContract.status, 'ready');
2735
+ assert.equal(staleEvidenceProbeContract.evidenceOnly, true);
2736
+ assert.equal(staleEvidenceProbeContract.productRepairAllowed, false);
2737
+ assert.equal(staleEvidenceProbeContract.canRunWithoutCodexMonitor, true);
2738
+ assert.equal(staleEvidenceProbeContract.probeType, 'issue_class_probe');
2739
+ assert.equal(staleEvidenceProbeContract.issueClass, validDiagnosis.issue_class);
2740
+ assert.equal(staleEvidenceProbeContract.activeRoute, validDiagnosis.proof_plan.route);
2741
+ assert.equal(staleEvidenceProbeContract.startingEvidenceHash, 'same');
2742
+ assert.equal(staleEvidenceProbeContract.sameFailureCount, 2);
2743
+ assert.ok(staleEvidenceProbeContract.requiredNewSignals.some((signal) => /changed evidenceHash/i.test(signal)));
2744
+ assert.ok(staleEvidenceProbeContract.requiredArtifacts.some((artifact) => /AIQaBusinessAssertion|dropdown DOM snapshot/i.test(artifact)));
2745
+ assert.ok(staleEvidenceProbeContract.acceptableEvidence.some((entry) => /AIQaBusinessAssertion|before\/action\/after|DOM|Mongo|data/i.test(entry)));
2746
+ assert.ok(staleEvidenceProbeContract.forbiddenActions.some((entry) => /product-code|repair loop/i.test(entry)));
2747
+ assert.equal(validateResolveIOSupportEvidenceProbeContract(staleEvidenceProbeContract).valid, true);
2748
+
2749
+ const malformedEvidenceProbeContract = validateResolveIOSupportEvidenceProbeContract({
2750
+ ...staleEvidenceProbeContract,
2751
+ requiredNewSignals: [],
2752
+ requiredArtifacts: [],
2753
+ forbiddenActions: []
2754
+ });
2755
+ assert.equal(malformedEvidenceProbeContract.valid, false);
2756
+ assert.ok(malformedEvidenceProbeContract.blockers.some((blocker) => /new signal|artifact|forbid product-code/i.test(blocker)));
2757
+
2758
+ const repeatedAutonomousDecision = decideResolveIOSupportV5AutonomousNextAction({
2759
+ bundle: {
2760
+ ...diagnosedBundle,
2761
+ supportV5StepHistory: [
2762
+ { stepType: 'build_repair', outcome: 'needs_repair', lane: 'build', summary: 'same blocker', failureClass: 'product_code', blocker: 'same blocker', evidenceHash: 'same', recordedAt: '1' },
2763
+ { stepType: 'build_repair', outcome: 'needs_repair', lane: 'build', summary: 'same blocker', failureClass: 'product_code', blocker: 'same blocker', evidenceHash: 'same', recordedAt: '2' }
2764
+ ]
2765
+ },
2766
+ failureClass: 'product_code',
2767
+ blocker: 'same blocker',
2768
+ evidenceHash: 'same'
2769
+ });
2770
+ assert.equal(repeatedAutonomousDecision.action, 'collect_new_evidence');
2771
+ assert.equal(repeatedAutonomousDecision.canEditProductCode, false);
2772
+ assert.equal(repeatedAutonomousDecision.evidenceFreshness.mustCollectNewEvidence, true);
2773
+ assert.equal(repeatedAutonomousDecision.continuationProofCheckpoint.status, 'waiting_for_new_evidence');
2774
+ assert.equal(repeatedAutonomousDecision.continuationProofCheckpoint.blocksProductRepairUntilChangedEvidence, true);
2775
+ assert.equal(repeatedAutonomousDecision.nextActionContract.action, 'collect_new_evidence');
2776
+ assert.equal(repeatedAutonomousDecision.nextActionContract.safeToAutoRun, true);
2777
+ assert.equal(repeatedAutonomousDecision.nextActionContract.canRunWithoutCodexMonitor, true);
2778
+ assert.equal(repeatedAutonomousDecision.nextActionContract.decisionBasis.sameFailureCount, 2);
2779
+ assert.equal(repeatedAutonomousDecision.nextActionContract.decisionBasis.evidenceProbeContractReady, true);
2780
+ assert.equal(repeatedAutonomousDecision.nextActionContract.decisionBasis.evidenceProbeContractId, repeatedAutonomousDecision.evidenceProbeContract?.contractId);
2781
+ assert.equal(repeatedAutonomousDecision.evidenceProbeContract?.status, 'ready');
2782
+ assert.equal(repeatedAutonomousDecision.evidenceProbeContract?.productRepairAllowed, false);
2783
+ assert.ok(repeatedAutonomousDecision.evidenceProbeContract?.nextCommands.includes('execute_active_issue_class_probe_evidence_only'));
2784
+ assert.equal(repeatedAutonomousDecision.humanDecisionRequest?.required, true);
2785
+ assert.equal(repeatedAutonomousDecision.humanDecisionRequest?.status, 'auto_dispatch_allowed');
2786
+ assert.equal(repeatedAutonomousDecision.humanDecisionRequest?.preferredChoiceId, 'run_bounded_evidence_probe');
2787
+ assert.equal(repeatedAutonomousDecision.humanDecisionRequest?.autoDispatchAllowed, true);
2788
+ assert.equal(repeatedAutonomousDecision.humanDecisionRequest?.evidence.sameFailureCount, 2);
2789
+ assert.ok(repeatedAutonomousDecision.humanDecisionRequest?.choices.some((choice) => choice.choiceId === 'run_bounded_evidence_probe' && choice.action === 'collect_new_evidence' && choice.allowedWithoutCodexMonitor === true));
2790
+ assert.ok(repeatedAutonomousDecision.humanDecisionRequest?.blockedUntil.some((entry) => /blockerFingerprint|evidenceHash|business|compile|infra|release proof/i.test(entry)));
2791
+ assert.ok(repeatedAutonomousDecision.nextActionContract.stopConditions.some((entry) => /Reset requires:.*changed evidence hash|Reset requires:.*new artifact path|same failure class/i.test(entry)));
2792
+ assert.equal(validateResolveIOSupportNextActionContract(repeatedAutonomousDecision.nextActionContract).valid, true);
2793
+ assert.equal(repeatedAutonomousDecision.managerExecutionPacket.status, 'auto_ready');
2794
+ assert.equal(repeatedAutonomousDecision.managerExecutionPacket.retryScope, 'evidence_only');
2795
+ assert.equal(repeatedAutonomousDecision.managerExecutionPacket.maxAttemptsBeforeFreshEvidence, 1);
2796
+ assert.ok(repeatedAutonomousDecision.managerExecutionPacket.blockers.some((blocker) => /same_failure|changed_evidence|support_v5/i.test(blocker)));
2797
+ assert.ok(repeatedAutonomousDecision.forbiddenActions.some((action) => /product-code repair|another repair loop|blockerFingerprint|evidenceHash/i.test(action)));
2798
+
2799
+ const monitorOnlyRepeatedDecision = decideResolveIOSupportV5AutonomousNextAction({
2800
+ bundle: {
2801
+ ...diagnosedBundle,
2802
+ supportV5StepHistory: [
2803
+ { stepType: 'build_repair', outcome: 'needs_repair', lane: 'build', summary: 'same blocker', failureClass: 'product_code', blocker: 'same blocker', evidenceHash: 'same', recordedAt: '1' },
2804
+ { stepType: 'build_repair', outcome: 'needs_repair', lane: 'build', summary: 'same blocker', failureClass: 'product_code', blocker: 'same blocker', evidenceHash: 'same', recordedAt: '2' }
2805
+ ]
2806
+ },
2807
+ failureClass: 'product_code',
2808
+ blocker: 'same blocker',
2809
+ evidenceHash: 'same',
2810
+ autonomyPolicy: {
2811
+ mode: 'monitor_only',
2812
+ currentSpendUsd: 2,
2813
+ budgetHardUsd: 10
2814
+ }
2815
+ });
2816
+ assert.equal(monitorOnlyRepeatedDecision.action, 'collect_new_evidence');
2817
+ assert.equal(monitorOnlyRepeatedDecision.autonomyPolicy?.mode, 'monitor_only');
2818
+ assert.equal(monitorOnlyRepeatedDecision.autonomyPolicy?.canAutoDispatch, false);
2819
+ assert.equal(monitorOnlyRepeatedDecision.nextActionContract.safeToAutoRun, false);
2820
+ assert.equal(monitorOnlyRepeatedDecision.nextActionContract.canRunWithoutCodexMonitor, false);
2821
+ assert.equal(monitorOnlyRepeatedDecision.nextActionContract.requiresHumanApproval, true);
2822
+ assert.equal(monitorOnlyRepeatedDecision.nextActionContract.codexFallbackRequired, false);
2823
+ assert.equal(monitorOnlyRepeatedDecision.nextActionContract.decisionBasis.autonomyCanAutoDispatch, false);
2824
+ assert.equal(monitorOnlyRepeatedDecision.nextActionContract.decisionBasis.autonomyMode, 'monitor_only');
2825
+ assert.equal(monitorOnlyRepeatedDecision.managerExecutionPacket.status, 'manual_required');
2826
+ assert.equal(monitorOnlyRepeatedDecision.humanDecisionRequest?.status, 'waiting_for_operator');
2827
+ assert.equal(monitorOnlyRepeatedDecision.humanDecisionRequest?.autoDispatchAllowed, false);
2828
+
2829
+ const materialEvidenceFreshness = evaluateResolveIOSupportEvidenceFreshness({
2830
+ history: [
2831
+ { stepType: 'build_repair', outcome: 'needs_repair', lane: 'build', summary: 'same blocker', failureClass: 'product_code', blocker: 'same blocker', evidenceHash: 'same', artifactPaths: ['qa-artifacts/old.log'], recordedAt: '1' }
2832
+ ],
2833
+ failureClass: 'product_code',
2834
+ blocker: 'same blocker',
2835
+ evidenceHash: 'after-business-proof',
2836
+ artifactPaths: ['qa-artifacts/old.log', 'qa-artifacts/business-proof-after.png'],
2837
+ limit: 2
2838
+ });
2839
+ assert.equal(materialEvidenceFreshness.status, 'material_evidence');
2840
+ assert.equal(materialEvidenceFreshness.materialEvidence, true);
2841
+ assert.equal(materialEvidenceFreshness.canRetry, true);
2842
+ assert.equal(materialEvidenceFreshness.loopBudgetShouldReset, true);
2843
+
2844
+ const withFailure = recordResolveIOSupportV5Step(initialized, {
2845
+ stepType: 'qa_row',
2846
+ outcome: 'needs_repair',
2847
+ lane: 'qa',
2848
+ model: 'gpt-5.4-mini',
2849
+ threadKey: 'support:ticket-1:job:job-1:qa',
2850
+ promptTokenEstimate: 1200,
2851
+ runtimeMs: 45000,
2852
+ summary: 'Row 2 failed.',
2853
+ blocker: 'Screenshot shell-only for row 2',
2854
+ artifactPaths: ['qa-artifacts/row-2.jpg'],
2855
+ activeQaRow: {
2856
+ index: 2,
2857
+ workflow: 'Disabled driver cannot replace selected active driver',
2858
+ route: '/dashboard/driver/truck-treating/bol-detail/bol/route',
2859
+ status: 'failed'
2860
+ },
2861
+ now: '2026-06-06T00:01:00.000Z'
2862
+ });
2863
+
2864
+ assert.equal(withFailure.supportV5StepHistory.length, 1);
2865
+ assert.equal(withFailure.supportV5Budget.loopCount, 1);
2866
+ assert.equal(withFailure.supportV5LaneMemory.qa.activeBlocker, 'Screenshot shell-only for row 2');
2867
+ assert.equal(withFailure.supportV5SupervisorState.currentQaRow?.index, 2);
2868
+ assert.deepEqual(decideResolveIOSupportV5Continuation(withFailure).action, 'continue');
2869
+
2870
+ let repeated = withFailure;
2871
+ repeated = recordResolveIOSupportV5Step(repeated, {
2872
+ stepType: 'qa_row',
2873
+ outcome: 'needs_repair',
2874
+ lane: 'qa',
2875
+ blocker: 'Screenshot shell-only for row 2',
2876
+ summary: 'Still shell-only.',
2877
+ now: '2026-06-06T00:02:00.000Z'
2878
+ });
2879
+ repeated = recordResolveIOSupportV5Step(repeated, {
2880
+ stepType: 'qa_row',
2881
+ outcome: 'needs_repair',
2882
+ lane: 'qa',
2883
+ blocker: 'Screenshot shell-only for row 2',
2884
+ summary: 'Still shell-only again.',
2885
+ now: '2026-06-06T00:03:00.000Z'
2886
+ });
2887
+
2888
+ const repeatedDecision = decideResolveIOSupportV5Continuation(repeated);
2889
+ assert.equal(repeatedDecision.action, 'park');
2890
+ assert.equal(repeatedDecision.reason, 'support_v5_same_failure_with_weak_evidence');
2891
+ assert.equal(repeatedDecision.recoveryPlan.recoveryClass, 'blocked_until_new_evidence');
2892
+ assert.equal(repeatedDecision.recoveryPlan.allowedAction, 'collect_new_evidence_only');
2893
+ assert.equal(repeatedDecision.recoveryCheckpoint.status, 'parked');
2894
+ assert.equal(repeatedDecision.recoveryCheckpoint.allowedAction, 'collect_new_evidence_only');
2895
+ assert.equal(repeatedDecision.recoveryEvidenceProbe.evidenceOnly, true);
2896
+ assert.equal(repeatedDecision.recoveryEvidenceProbe.productRepairAllowed, false);
2897
+ assert.ok(repeatedDecision.recoveryEvidenceProbe.acceptanceEvidence.some((evidence) => /changed evidence hash|new artifact path/i.test(evidence)));
2898
+ assert.equal(repeatedDecision.recoveryAction.mode, 'collect_evidence');
2899
+ assert.equal(repeatedDecision.recoveryAction.retryPolicy.requireNewEvidence, true);
2900
+ assert.ok(repeatedDecision.recoveryAction.requiredArtifacts.length > 0);
2901
+
2902
+ let materialEvidenceRetryBundle = withFailure;
2903
+ materialEvidenceRetryBundle = recordResolveIOSupportV5Step(materialEvidenceRetryBundle, {
2904
+ stepType: 'qa_row',
2905
+ outcome: 'needs_repair',
2906
+ lane: 'qa',
2907
+ failureClass: 'product_code',
2908
+ blocker: 'Save action still throws TypeError.',
2909
+ summary: 'Save action still throws TypeError.',
2910
+ evidenceHash: 'save-typeerror-before',
2911
+ artifactPaths: ['qa-artifacts/save-before.log'],
2912
+ now: '2026-06-06T00:03:10.000Z'
2913
+ });
2914
+ materialEvidenceRetryBundle = recordResolveIOSupportV5Step(materialEvidenceRetryBundle, {
2915
+ stepType: 'qa_row',
2916
+ outcome: 'needs_repair',
2917
+ lane: 'qa',
2918
+ failureClass: 'product_code',
2919
+ blocker: 'Save action still throws TypeError.',
2920
+ summary: 'New business DOM trace proves the save click reaches the method with an undefined id.',
2921
+ evidenceHash: 'save-typeerror-after-dom-trace',
2922
+ artifactPaths: ['qa-artifacts/save-before.log', 'qa-artifacts/save-after-dom-trace.json'],
2923
+ now: '2026-06-06T00:03:20.000Z'
2924
+ });
2925
+ const materialEvidenceRetryDecision = decideResolveIOSupportV5Continuation(materialEvidenceRetryBundle);
2926
+ assert.equal(materialEvidenceRetryDecision.action, 'continue');
2927
+ assert.equal(materialEvidenceRetryDecision.reason, 'support_v5_continue');
2928
+
2929
+ const repeatedProductFailure = decideResolveIOSupportV5RepeatedFailureStop({
2930
+ history: [
2931
+ { stepType: 'build_repair', outcome: 'needs_repair', lane: 'build', summary: 'same', failureClass: 'product_code', blocker: 'same blocker', evidenceHash: 'same', recordedAt: '1' },
2932
+ { stepType: 'build_repair', outcome: 'needs_repair', lane: 'build', summary: 'same', failureClass: 'product_code', blocker: 'same blocker', evidenceHash: 'same', recordedAt: '2' }
2933
+ ],
2934
+ failureClass: 'product_code',
2935
+ blocker: 'same blocker',
2936
+ evidenceHash: 'same',
2937
+ limit: 2
2938
+ });
2939
+ assert.equal(repeatedProductFailure.shouldStop, true);
2940
+ const repeatedInfraFailure = decideResolveIOSupportV5RepeatedFailureStop({
2941
+ history: [
2942
+ { stepType: 'compile_check', outcome: 'needs_repair', lane: 'build', summary: 'same', failureClass: 'infra', blocker: 'puppeteer missing', evidenceHash: 'same', recordedAt: '1' }
2943
+ ],
2944
+ failureClass: 'infra',
2945
+ blocker: 'puppeteer missing',
2946
+ evidenceHash: 'same',
2947
+ limit: 1
2948
+ });
2949
+ assert.equal(repeatedInfraFailure.shouldStop, false);
2950
+
2951
+ let repeatedInfraBundle = diagnosedBundle;
2952
+ repeatedInfraBundle = recordResolveIOSupportV5Step(repeatedInfraBundle, {
2953
+ stepType: 'compile_check',
2954
+ outcome: 'needs_repair',
2955
+ lane: 'build',
2956
+ failureClass: 'infra',
2957
+ blocker: 'Puppeteer executable missing.',
2958
+ evidenceHash: 'same-infra',
2959
+ now: '2026-06-06T00:03:10.000Z'
2960
+ });
2961
+ repeatedInfraBundle = recordResolveIOSupportV5Step(repeatedInfraBundle, {
2962
+ stepType: 'compile_check',
2963
+ outcome: 'needs_repair',
2964
+ lane: 'build',
2965
+ failureClass: 'infra',
2966
+ blocker: 'Puppeteer executable missing.',
2967
+ evidenceHash: 'same-infra',
2968
+ now: '2026-06-06T00:03:20.000Z'
2969
+ });
2970
+ repeatedInfraBundle = recordResolveIOSupportV5Step(repeatedInfraBundle, {
2971
+ stepType: 'compile_check',
2972
+ outcome: 'needs_repair',
2973
+ lane: 'build',
2974
+ failureClass: 'infra',
2975
+ blocker: 'Puppeteer executable missing.',
2976
+ evidenceHash: 'same-infra',
2977
+ now: '2026-06-06T00:03:30.000Z'
2978
+ });
2979
+ const repeatedInfraContinuation = decideResolveIOSupportV5Continuation(repeatedInfraBundle);
2980
+ assert.equal(repeatedInfraContinuation.action, 'continue');
2981
+ assert.equal(repeatedInfraContinuation.reason, 'support_v5_infra_or_compile_repair_required');
2982
+ assert.equal(repeatedInfraContinuation.recoveryPlan.recoveryClass, 'infra_repair');
2983
+ assert.equal(repeatedInfraContinuation.recoveryPlan.productRepairAllowed, false);
2984
+
2985
+ const sameBlockerWeakHashOnlyEvidence = decideResolveIOSupportV5RepeatedFailureStop({
2986
+ history: [
2987
+ { stepType: 'build_repair', outcome: 'needs_repair', lane: 'build', summary: 'same root issue', failureClass: 'product_code', blocker: 'same blocker', evidenceHash: 'before-fix-log', recordedAt: '1' },
2988
+ { stepType: 'build_repair', outcome: 'needs_repair', lane: 'build', summary: 'same root issue', failureClass: 'product_code', blocker: 'same blocker', evidenceHash: 'renamed-hash-only', recordedAt: '2' }
2989
+ ],
2990
+ failureClass: 'product_code',
2991
+ blocker: 'same blocker',
2992
+ evidenceHash: 'renamed-hash-only',
2993
+ limit: 2
2994
+ });
2995
+ assert.equal(sameBlockerWeakHashOnlyEvidence.shouldStop, true);
2996
+ assert.equal(sameBlockerWeakHashOnlyEvidence.reason, 'support_v5_same_failure_with_weak_evidence');
2997
+ assert.equal(sameBlockerWeakHashOnlyEvidence.newEvidence, true);
2998
+ assert.equal(sameBlockerWeakHashOnlyEvidence.materialEvidence, false);
2999
+ assert.equal(sameBlockerWeakHashOnlyEvidence.evidenceStrength, 'weak');
3000
+
3001
+ const sameBlockerMaterialEvidence = decideResolveIOSupportV5RepeatedFailureStop({
3002
+ history: [
3003
+ { stepType: 'build_repair', outcome: 'needs_repair', lane: 'build', summary: 'same root issue', failureClass: 'product_code', blocker: 'same blocker', evidenceHash: 'before-fix-log', artifactPaths: ['qa-artifacts/before.log'], recordedAt: '1' },
3004
+ { stepType: 'build_repair', outcome: 'needs_repair', lane: 'build', summary: 'New business DOM trace proves the disabled driver option still persists after save.', failureClass: 'product_code', blocker: 'same blocker', evidenceHash: 'after-business-proof', artifactPaths: ['qa-artifacts/before.log', 'qa-artifacts/business-proof-after.json'], recordedAt: '2' }
3005
+ ],
3006
+ failureClass: 'product_code',
3007
+ blocker: 'same blocker',
3008
+ evidenceHash: 'after-business-proof',
3009
+ artifactPaths: ['qa-artifacts/before.log', 'qa-artifacts/business-proof-after.json'],
3010
+ limit: 2
3011
+ });
3012
+ assert.equal(sameBlockerMaterialEvidence.shouldStop, false);
3013
+ assert.equal(sameBlockerMaterialEvidence.reason, 'support_v5_retry_allowed_material_evidence');
3014
+ assert.equal(sameBlockerMaterialEvidence.materialEvidence, true);
3015
+ assert.equal(sameBlockerMaterialEvidence.evidenceStrength, 'material');
3016
+ assert.ok(sameBlockerMaterialEvidence.evidenceSignals?.includes('artifact_paths_added'));
3017
+
3018
+ const pingPongFailure = decideResolveIOSupportV5RepeatedFailureStop({
3019
+ history: [
3020
+ { stepType: 'qa_row', outcome: 'needs_repair', lane: 'qa', summary: 'Filter excludes active customers', failureClass: 'business', blocker: 'Filter excludes active customers', evidenceHash: 'a', recordedAt: '1' },
3021
+ { stepType: 'qa_row', outcome: 'needs_repair', lane: 'qa', summary: 'Filter includes inactive customers', failureClass: 'business', blocker: 'Filter includes inactive customers', evidenceHash: 'b', recordedAt: '2' },
3022
+ { stepType: 'qa_row', outcome: 'needs_repair', lane: 'qa', summary: 'Filter excludes active customers', failureClass: 'business', blocker: 'Filter excludes active customers', evidenceHash: 'a', recordedAt: '3' },
3023
+ { stepType: 'qa_row', outcome: 'needs_repair', lane: 'qa', summary: 'Filter includes inactive customers', failureClass: 'business', blocker: 'Filter includes inactive customers', evidenceHash: 'b', recordedAt: '4' }
3024
+ ],
3025
+ failureClass: 'business',
3026
+ blocker: 'Filter includes inactive customers',
3027
+ evidenceHash: 'b',
3028
+ limit: 3
3029
+ });
3030
+ assert.equal(pingPongFailure.shouldStop, true);
3031
+ assert.equal(pingPongFailure.reason, 'support_v5_ping_pong_failure_loop');
3032
+
3033
+ const budgeted = recordResolveIOSupportV5Step(initialized, {
3034
+ stepType: 'build_repair',
3035
+ outcome: 'needs_repair',
3036
+ lane: 'build',
3037
+ promptTokenEstimate: 1900,
3038
+ blocker: 'Prompt was too broad for a repair step.',
3039
+ now: '2026-06-06T00:04:00.000Z'
3040
+ });
3041
+ assert.equal(decideResolveIOSupportV5Continuation(budgeted).reason, 'support_v5_budget_guard');
3042
+ assert.equal(decideResolveIOSupportV5Continuation(budgeted).recoveryPlan.recoveryClass, 'manual_handoff');
3043
+
3044
+ const prompt = buildResolveIOSupportV5DiagnoseFirstPrompt({
3045
+ goal: initialized.supportV5SupervisorState.currentGoal,
3046
+ activeStep: 'qa_row',
3047
+ activeBlocker: 'Route failed to hydrate.',
3048
+ lane: 'qa',
3049
+ currentQaRow: withFailure.supportV5SupervisorState.currentQaRow,
3050
+ artifactPaths: ['qa-artifacts/client.log'],
3051
+ changedFiles: ['geochem/angular/app/bol/new/bol-new.component.ts']
3052
+ }).join('\n');
3053
+
3054
+ assert.match(prompt, /Diagnose First/);
3055
+ assert.match(prompt, /Do not send customer email/);
3056
+ assert.match(prompt, /Current QA row/);
3057
+ assert.match(prompt, /geochem\/angular\/app\/bol\/new\/bol-new\.component\.ts/);
3058
+
3059
+ const diagnosisPromptWithHints = buildResolveIOSupportV5MicrotaskPrompt({
3060
+ bundle: initialized,
3061
+ lane: 'build',
3062
+ stage: 'support-diagnosis-gate',
3063
+ similarCaseHints,
3064
+ contextSnippets: ['Ticket number: 004999']
3065
+ });
3066
+ assert.equal(diagnosisPromptWithHints.activeMicrotask?.type, 'diagnosis_gate');
3067
+ assert.equal(diagnosisPromptWithHints.diagnosisEvidencePack.status, 'needs_diagnosis');
3068
+ assert.equal(diagnosisPromptWithHints.diagnosisEvidencePack.sourceEditsAllowed, false);
3069
+ assert.equal(diagnosisPromptWithHints.diagnosisEvidencePack.similarCaseSelection.ranked[0].ticketNumber, '004333');
3070
+ assert.match(diagnosisPromptWithHints.prompt, /Similar accepted fix hints/);
3071
+ assert.match(diagnosisPromptWithHints.prompt, /diagnosis_evidence_pack|support-diagnosis-pack/);
3072
+ assert.match(diagnosisPromptWithHints.prompt, /advisory only/i);
3073
+ assert.match(diagnosisPromptWithHints.prompt, /004333/);
3074
+ assert.match(diagnosisPromptWithHints.prompt, /owner_file_overlap/);
3075
+
3076
+ const inventoryDiagnosisPrompt = buildResolveIOSupportV5MicrotaskPrompt({
3077
+ bundle: initializeResolveIOSupportV5State({
3078
+ jobId: 'job-004433',
3079
+ ticketId: 'ticket-004433',
3080
+ ticketNumber: '004433',
3081
+ title: 'Support ticket #004433',
3082
+ description: 'Inventory issue. On June 1 Danny entered pad inventory as 6 units in Kermit warehouse. When inventory was pulled later, it printed at Midland warehouse.',
3083
+ approvedScopeRequirements: ['Inventory pad quantity entered for Kermit warehouse is printing under Midland warehouse.'],
3084
+ existing: {
3085
+ supportV5DiagnosisGate: {
3086
+ issue_case: {
3087
+ customer_complaint: '',
3088
+ expected_result: '',
3089
+ observed_result: '',
3090
+ route_module: '',
3091
+ account_customer_context: '',
3092
+ reproduction_status: 'reproduced'
3093
+ },
3094
+ issue_class: 'missing_wrong_data',
3095
+ accepted_hypothesis: {
3096
+ statement: 'Existing check-in and checkout warehouse fields may be crossed in the inventory print path.',
3097
+ falsifiable_test: '',
3098
+ evidence: []
3099
+ },
3100
+ rejected_alternatives: [],
3101
+ failing_path: {
3102
+ description: 'Inventory print path for the part detail workflow.'
3103
+ },
3104
+ owner_files: [
3105
+ 'var/app/resolveio-ai-workspace/6a35ce5d939a82c734da7e67/resolveio-all/h2sko/angular/app/part/detail/part-detail.component.html',
3106
+ 'var/app/resolveio-ai-workspace/6a35ce5d939a82c734da7e67/resolveio-all/h2sko/angular/app/document/pick-ticket/report-doc-pick-ticket.component.html'
3107
+ ],
3108
+ proof_plan: {},
3109
+ evidence: [],
3110
+ status: 'passed'
3111
+ }
3112
+ } as any,
3113
+ now: '2026-06-06T00:06:00.000Z'
3114
+ }),
3115
+ lane: 'build',
3116
+ stage: 'support-diagnosis-gate'
3117
+ });
3118
+ assert.match(inventoryDiagnosisPrompt.prompt, /Starter only/);
3119
+ assert.match(inventoryDiagnosisPrompt.prompt, /Inventory print path for the part detail workflow/);
3120
+ assert.match(inventoryDiagnosisPrompt.prompt, /h2sko\/angular\/app\/part\/detail\/part-detail\.component\.html/);
3121
+ assert.doesNotMatch(inventoryDiagnosisPrompt.prompt, /customer_complaint":""/);
3122
+ assert.doesNotMatch(inventoryDiagnosisPrompt.prompt, /var\/app\/resolveio-ai-workspace/);
3123
+
3124
+ assert.equal(
3125
+ fingerprintResolveIOSupportV5Blocker('Line 123 failed for 6a239cd7d67a2b24b36f3dfe'),
3126
+ fingerprintResolveIOSupportV5Blocker('Line 456 failed for abcdefabcdefabcdef')
3127
+ );
3128
+
3129
+ const incident = buildResolveIOSupportV5Incident({
3130
+ incidentClass: 'false_screenshot_rejection',
3131
+ summary: 'Sparse but valid BOL detail screen was rejected by file-size gate.',
3132
+ stepType: 'artifact_package',
3133
+ blocker: 'screenshot shell-only',
3134
+ artifactPaths: ['qa-artifacts/row-2.jpg'],
3135
+ now: '2026-06-06T00:05:00.000Z'
3136
+ });
3137
+ assert.equal(incident.incidentClass, 'false_screenshot_rejection');
3138
+ assert.ok(incident.blockerFingerprint);
3139
+
3140
+ const microtaskPrompt = buildResolveIOSupportV5MicrotaskPrompt({
3141
+ bundle: diagnosedBundle,
3142
+ lane: 'build',
3143
+ stage: 'support-autonomous-owner',
3144
+ failureText: 'QA row failed because the driver dropdown still includes disabled drivers.',
3145
+ changedFiles: ['geochem/angular/app/bol/new/bol-new.component.ts'],
3146
+ contextSnippets: ['Workspace root: /var/ai-workspace/job-1', 'Project root: geochem']
3147
+ });
3148
+ assert.equal(microtaskPrompt.activeMicrotask?.lane, 'build');
3149
+ assert.equal(microtaskPrompt.withinHardCap, true);
3150
+ assert.ok(microtaskPrompt.promptTokenEstimate <= buildResolveIOSupportV5PromptBudget().buildMicrotaskHardCap);
3151
+ assert.match(microtaskPrompt.prompt, /Microtask id:/);
3152
+ assert.match(microtaskPrompt.prompt, /persistent lane thread/);
3153
+ assert.match(microtaskPrompt.prompt, /Diagnosis issue class: filter_query_mismatch/);
3154
+ assert.doesNotMatch(microtaskPrompt.prompt, /Initial ticket context:/);
3155
+ assert.doesNotMatch(microtaskPrompt.prompt, /Staged support context:/);
3156
+ assert.doesNotMatch(microtaskPrompt.prompt, /BUILD_PLAN/);
3157
+
3158
+ const qaPrompt = buildResolveIOSupportV5MicrotaskPrompt({
3159
+ bundle: {
3160
+ ...diagnosedBundle,
3161
+ supportV5ActiveMicrotaskId: diagnosedBundle.supportV5MicrotaskLedger.find((task) => task.lane === 'qa')?.microtaskId
3162
+ },
3163
+ lane: 'qa',
3164
+ stage: 'support-qa-validation',
3165
+ activeQaRow: {
3166
+ index: 0,
3167
+ workflow: 'BOL driver display',
3168
+ route: '/dashboard/driver/truck-treating/bol-detail/bol/route',
3169
+ assertion: 'Disabled driver is rejected',
3170
+ status: 'pending'
3171
+ }
3172
+ });
3173
+ assert.equal(qaPrompt.activeMicrotask?.lane, 'qa');
3174
+ assert.ok(qaPrompt.promptTokenEstimate <= buildResolveIOSupportV5PromptBudget().qaMicrotaskHardCap);
3175
+ assert.match(qaPrompt.prompt, /Return strict JSON only/);
3176
+ assert.match(qaPrompt.prompt, /IssueClassProbePlan|support-probe-plan|filter_query_mismatch/);
3177
+
3178
+ const usageBundle = recordResolveIOSupportV5MicrotaskUsage(initialized, {
3179
+ microtaskId: initialized.supportV5MicrotaskLedger[0].microtaskId,
3180
+ lane: 'build',
3181
+ model: 'gpt-5.3-codex',
3182
+ threadKey: initialized.supportV5LaneMemory.build.threadKey,
3183
+ reuseThread: true,
3184
+ promptTokenEstimate: microtaskPrompt.promptTokenEstimate,
3185
+ promptSections: microtaskPrompt.promptSections,
3186
+ outcome: 'pass'
3187
+ });
3188
+ const usageSummary = summarizeResolveIOSupportV5MicrotaskUsage(usageBundle);
3189
+ assert.equal(usageSummary.byMicrotask.length, 1);
3190
+ assert.ok(usageSummary.bySection.some((section) => section.name === 'active_microtask'));
3191
+ assert.equal(usageSummary.broadPromptViolations.length, 0);
3192
+
3193
+ const digest = buildResolveIOSupportV5ScopeDigest({
3194
+ goal: 'Resolve support ticket 004999',
3195
+ approvedScope: ['A'.repeat(2000), 'B'.repeat(2000)],
3196
+ prBranch: 'support-004999',
3197
+ maxTokens: 200
3198
+ });
3199
+ assert.ok(digest.length <= 800);
3200
+
3201
+ console.log('support runner v5 tests passed');