@resolveio/server-lib 22.3.174 → 22.3.176

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 +23825 -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 +3292 -0
  177. package/src/util/ai-run-evidence-dashboard.ts +313 -0
  178. package/src/util/ai-run-evidence-eval.ts +1054 -0
  179. package/src/util/ai-run-evidence.ts +1124 -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 +4193 -0
  183. package/src/util/ai-runner-qa-auth.ts +821 -0
  184. package/src/util/ai-runner-qa-tools.ts +3045 -0
  185. package/src/util/aicoder-runner-v6.ts +2510 -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 +4785 -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 +560 -0
  208. package/tests/ai-assistant-snf-live-eval.ts +975 -0
  209. package/tests/ai-assistant-utils.test.ts +3057 -0
  210. package/tests/ai-manager-autopilot-snapshot.test.ts +193 -0
  211. package/tests/ai-manager-recovery-checkpoint.test.ts +1136 -0
  212. package/tests/ai-run-eval.test.ts +112 -0
  213. package/tests/ai-run-evidence.test.ts +1403 -0
  214. package/tests/ai-runner-contract.test.ts +488 -0
  215. package/tests/aicoder-runner-v6.test.ts +676 -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 +1166 -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 -338
  425. package/methods/ai-terminal.js +0 -23454
  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 -73
  672. package/util/ai-run-evidence-adapters.js +0 -2986
  673. package/util/ai-run-evidence-adapters.js.map +0 -1
  674. package/util/ai-run-evidence-dashboard.d.ts +0 -84
  675. package/util/ai-run-evidence-dashboard.js +0 -336
  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 -854
  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 -778
  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 -653
  690. package/util/ai-runner-manager-policy.js +0 -2880
  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 -822
  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 -3029
  697. package/util/ai-runner-qa-tools.js.map +0 -1
  698. package/util/aicoder-runner-v6.d.ts +0 -367
  699. package/util/aicoder-runner-v6.js +0 -1925
  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 -810
  732. package/util/support-runner-v5.js +0 -3497
  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,3292 @@
1
+ import {
2
+ AIRun,
3
+ AIRunCost,
4
+ AIRunEvent,
5
+ AIRunGateResult,
6
+ AIQaArtifact,
7
+ AIQaBusinessAssertion,
8
+ AIQaCompileResult,
9
+ AIQaInfraCheck,
10
+ AIQaRun,
11
+ AIQaRouteProbe,
12
+ buildAIRun,
13
+ buildAIRunCost,
14
+ buildAIQaRun,
15
+ normalizeOpenAIUsageRowsForCosting
16
+ } from './ai-run-evidence';
17
+ import {
18
+ evaluateResolveIOAICoderWorkflowProofReadiness,
19
+ ResolveIOAICoderJourneyContractValidationResult,
20
+ ResolveIOAICoderWorkflowProofReadiness,
21
+ validateResolveIOAICoderJourneyContract
22
+ } from './aicoder-runner-v6';
23
+ import {
24
+ decideResolveIOAIManagerPolicy,
25
+ ResolveIOAIManagerFailureRecord,
26
+ validateResolveIOAIManagerHotfixEvidence
27
+ } from './ai-runner-manager-policy';
28
+ import {
29
+ buildResolveIOSupportDiagnosisEvidencePack,
30
+ validateResolveIOSupportDiagnosisGate
31
+ } from './support-runner-v5';
32
+
33
+ export interface SupportAIRunAdapterInput {
34
+ ticket?: Record<string, any>;
35
+ job?: Record<string, any>;
36
+ versions?: Array<Record<string, any>>;
37
+ buildPlans?: Array<Record<string, any>>;
38
+ aiJobs?: Array<Record<string, any>>;
39
+ taskEvents?: Array<Record<string, any>>;
40
+ usageLedger?: Array<Record<string, any>>;
41
+ commits?: Array<Record<string, any>>;
42
+ qaEvidence?: Record<string, any>;
43
+ now?: Date | string;
44
+ }
45
+
46
+ export interface AICoderAIRunAdapterInput {
47
+ app?: Record<string, any>;
48
+ job?: Record<string, any>;
49
+ logs?: Array<Record<string, any>>;
50
+ usageLedger?: Array<Record<string, any>>;
51
+ commits?: Array<Record<string, any>>;
52
+ qaEvidence?: Record<string, any>;
53
+ now?: Date | string;
54
+ }
55
+
56
+ export interface AssistantAIRunAdapterInput {
57
+ runId?: string;
58
+ requestId?: string;
59
+ messageId?: string;
60
+ conversation?: Record<string, any>;
61
+ messages?: Array<Record<string, any>>;
62
+ issueReports?: Array<Record<string, any>>;
63
+ usageLedger?: Array<Record<string, any>>;
64
+ correctnessChecks?: Array<Record<string, any>>;
65
+ answerQuality?: Record<string, any>;
66
+ now?: Date | string;
67
+ }
68
+
69
+ export type AssistantAnswerQualityStatus =
70
+ | 'ready'
71
+ | 'missing_data_source'
72
+ | 'date_incorrect'
73
+ | 'missing_date_window'
74
+ | 'illegal_query_shape'
75
+ | 'missing_query_proof'
76
+ | 'query_error'
77
+ | 'permission_error'
78
+ | 'no_data_unverified'
79
+ | 'missing_citations'
80
+ | 'low_confidence'
81
+ | 'missing_next_action'
82
+ | 'incorrect'
83
+ | 'blocked';
84
+
85
+ export interface AssistantAnswerQualityInput {
86
+ answerQuality?: Record<string, any>;
87
+ correctnessChecks?: Array<Record<string, any>>;
88
+ now?: Date | string;
89
+ }
90
+
91
+ export interface AssistantAnswerQualityDecision {
92
+ ready: boolean;
93
+ status: AssistantAnswerQualityStatus;
94
+ reason: string;
95
+ blockers: string[];
96
+ queryStatus: 'ok' | 'no_data' | 'query_error' | 'permission_error' | 'unknown';
97
+ confidenceLevel: 'high' | 'medium' | 'low' | 'unknown';
98
+ dateBasis?: string;
99
+ expectedCurrentDate?: string;
100
+ dateWindowRequired: boolean;
101
+ dateWindowPresent: boolean;
102
+ dateWindow?: {
103
+ startDate?: string;
104
+ endDate?: string;
105
+ mode?: string;
106
+ months?: number;
107
+ };
108
+ noDataConfirmed: boolean;
109
+ legalQueryShape: boolean;
110
+ citationRefs: string[];
111
+ queryEvidenceRequired: boolean;
112
+ queryEvidencePresent: boolean;
113
+ queryEvidenceRefs: string[];
114
+ queryExecutionCount: number;
115
+ nextActions: string[];
116
+ evidenceRefs: string[];
117
+ failedChecks: string[];
118
+ recordedAt: string;
119
+ }
120
+
121
+ function cleanText(value: any, max = 1000): string {
122
+ return String(value || '').replace(/\s+/g, ' ').trim().slice(0, max);
123
+ }
124
+
125
+ function idText(value: any, max = 160): string | undefined {
126
+ const normalized = cleanText(value, max);
127
+ return normalized || undefined;
128
+ }
129
+
130
+ function asArray<T = any>(value: any): T[] {
131
+ return Array.isArray(value) ? value : [];
132
+ }
133
+
134
+ function cleanStringList(value: any, limit = 40, max = 500): string[] {
135
+ return asArray<any>(value)
136
+ .map((entry) => cleanText(entry, max))
137
+ .filter(Boolean)
138
+ .slice(0, limit);
139
+ }
140
+
141
+ function plainObject(value: any): Record<string, any> {
142
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
143
+ }
144
+
145
+ function firstText(source: Record<string, any> | undefined, fields: string[], max = 1000): string | undefined {
146
+ for (const field of fields) {
147
+ const value = source?.[field];
148
+ const normalized = idText(value, max);
149
+ if (normalized) {
150
+ return normalized;
151
+ }
152
+ }
153
+ return undefined;
154
+ }
155
+
156
+ function dateValue(source: Record<string, any> | undefined, fields: string[]): Date | string | undefined {
157
+ for (const field of fields) {
158
+ const value = source?.[field];
159
+ if (value) {
160
+ return value;
161
+ }
162
+ }
163
+ return undefined;
164
+ }
165
+
166
+ function addSourceId(sourceIds: Record<string, string>, key: string, value: any): void {
167
+ const normalized = idText(value);
168
+ if (normalized) {
169
+ sourceIds[key] = normalized;
170
+ }
171
+ }
172
+
173
+ function pushEvent(events: AIRunEvent[], event: AIRunEvent): void {
174
+ const message = cleanText(event.message, 1800);
175
+ const metadata = event.metadata || {};
176
+ if (!message && !Object.keys(metadata).length && !event.artifactPaths?.length) {
177
+ return;
178
+ }
179
+ events.push({
180
+ ...event,
181
+ message,
182
+ artifactPaths: asArray<string>(event.artifactPaths).filter(Boolean).slice(0, 20)
183
+ });
184
+ }
185
+
186
+ function eventDate(record: Record<string, any>): Date | string | undefined {
187
+ return dateValue(record, ['recordedAt', 'createdAt', 'updatedAt', 'timestamp', 'time', 'date']);
188
+ }
189
+
190
+ function normalizeManagerFailureRecord(value: any, fallback: Partial<ResolveIOAIManagerFailureRecord> = {}): ResolveIOAIManagerFailureRecord | undefined {
191
+ const entry = plainObject(value);
192
+ if (!Object.keys(entry).length && !Object.keys(fallback).length) {
193
+ return undefined;
194
+ }
195
+ const blocker = cleanText(entry.blocker || entry.reason || entry.summary || entry.message || fallback.blocker || fallback.summary, 1200);
196
+ const failureClass = cleanText(entry.failureClass || entry.failure_class || entry.class || fallback.failureClass, 120);
197
+ const evidenceHash = cleanText(entry.evidenceHash || entry.evidence_hash || entry.hash || fallback.evidenceHash, 180);
198
+ const blockerFingerprint = cleanText(entry.blockerFingerprint || entry.blocker_fingerprint || fallback.blockerFingerprint, 180);
199
+ const lane = cleanText(entry.lane || fallback.lane || 'support', 80);
200
+ const stepType = cleanText(entry.stepType || entry.step_type || entry.type || fallback.stepType || 'support_step', 80);
201
+ const outcome = cleanText(entry.outcome || entry.status || entry.result || fallback.outcome || 'needs_repair', 80);
202
+ if (!blocker && !failureClass && !evidenceHash && !blockerFingerprint) {
203
+ return undefined;
204
+ }
205
+ return {
206
+ lane,
207
+ stepType,
208
+ outcome,
209
+ failureClass: failureClass || undefined,
210
+ blocker: blocker || undefined,
211
+ summary: cleanText(entry.summary || entry.message || fallback.summary, 1200) || undefined,
212
+ blockerFingerprint: blockerFingerprint || undefined,
213
+ evidenceHash: evidenceHash || undefined,
214
+ changedFiles: cleanStringList(entry.changedFiles || entry.changed_files || fallback.changedFiles, 80, 500),
215
+ artifactPaths: cleanStringList(entry.artifactPaths || entry.artifact_paths || entry.artifacts || fallback.artifactPaths, 80, 500)
216
+ };
217
+ }
218
+
219
+ function supportManagerFailureRecords(job: Record<string, any>, evidence: Record<string, any>): ResolveIOAIManagerFailureRecord[] {
220
+ const records: ResolveIOAIManagerFailureRecord[] = [];
221
+ const pushRecord = (value: any, fallback: Partial<ResolveIOAIManagerFailureRecord> = {}): void => {
222
+ const record = normalizeManagerFailureRecord(value, fallback);
223
+ if (record) {
224
+ records.push(record);
225
+ }
226
+ };
227
+ for (const entry of asArray<any>(job.supportV5StepHistory || job.support_v5_step_history || evidence.supportV5StepHistory || evidence.support_v5_step_history)) {
228
+ pushRecord(entry);
229
+ }
230
+ for (const entry of asArray<any>(job.supportV5FailureFingerprints || job.support_v5_failure_fingerprints || evidence.supportV5FailureFingerprints || evidence.support_v5_failure_fingerprints)) {
231
+ const fingerprint = plainObject(entry);
232
+ const count = Math.max(1, Math.min(8, Number(fingerprint.count || fingerprint.repeatedCount || fingerprint.repeated_count || 1) || 1));
233
+ const fallback = {
234
+ lane: cleanText(fingerprint.lane || 'support', 80),
235
+ stepType: cleanText(fingerprint.stepType || fingerprint.step_type || 'support_step', 80),
236
+ outcome: cleanText(fingerprint.outcome || 'needs_repair', 80),
237
+ failureClass: cleanText(fingerprint.failureClass || fingerprint.failure_class || fingerprint.class, 120),
238
+ blocker: cleanText(fingerprint.blocker || fingerprint.reason || fingerprint.summary, 1200),
239
+ blockerFingerprint: cleanText(fingerprint.blockerFingerprint || fingerprint.blocker_fingerprint, 180),
240
+ evidenceHash: cleanText(fingerprint.evidenceHash || fingerprint.evidence_hash, 180)
241
+ };
242
+ for (let index = 0; index < count; index += 1) {
243
+ pushRecord(fingerprint, fallback);
244
+ }
245
+ }
246
+ const currentFailure = evidenceObject(
247
+ job.supportV5CurrentFailure,
248
+ job.support_v5_current_failure,
249
+ job.supportV5RecoveryCurrent,
250
+ job.support_v5_recovery_current,
251
+ evidence.currentFailure,
252
+ evidence.current_failure
253
+ );
254
+ pushRecord(currentFailure);
255
+ return records.slice(-80);
256
+ }
257
+
258
+ function managerNoBlindLoopGateFromHistory(
259
+ history: ResolveIOAIManagerFailureRecord[],
260
+ budgetSource: Record<string, any>,
261
+ now?: Date | string
262
+ ): AIRunGateResult | undefined {
263
+ if (!history.length) {
264
+ return undefined;
265
+ }
266
+ const budget = plainObject(budgetSource);
267
+ const maxSameFailureRepeats = Math.max(1, Number(budget.maxRepeatedNoProgress || budget.max_repeated_no_progress || budget.maxSameFailureRepeats || budget.max_same_failure_repeats || 3) || 3);
268
+ const decision = decideResolveIOAIManagerPolicy({
269
+ history,
270
+ maxSameFailureRepeats
271
+ });
272
+ const parksRun = /^(park_repeated_failure|park_ping_pong|manual_handoff)$/.test(decision.action);
273
+ const status: AIRunGateResult['status'] = parksRun
274
+ ? 'blocked'
275
+ : decision.action === 'retry_infra'
276
+ ? 'warn'
277
+ : 'pass';
278
+ return {
279
+ key: 'manager_no_blind_loop_policy',
280
+ label: 'No-blind-loop manager policy',
281
+ status,
282
+ reason: decision.reason,
283
+ evidenceRefs: cleanStringList(decision.recoveryAction.requiredArtifacts, 20, 500),
284
+ recordedAt: isoNow(now),
285
+ metadata: {
286
+ action: decision.action,
287
+ failureClass: decision.failureClass,
288
+ blockerFingerprint: decision.blockerFingerprint,
289
+ evidenceHash: decision.evidenceHash,
290
+ sameFailureCount: decision.sameFailureCount,
291
+ pingPongCount: decision.pingPongCount,
292
+ newEvidence: decision.newEvidence,
293
+ materialEvidence: decision.materialEvidence,
294
+ evidenceStrength: decision.evidenceStrength,
295
+ evidenceSignals: decision.evidenceSignals,
296
+ loopBudgetShouldReset: decision.loopBudgetShouldReset,
297
+ productRepairFailure: decision.productRepairFailure,
298
+ recoveryClass: decision.recoveryPlan.recoveryClass,
299
+ allowedAction: decision.recoveryPlan.allowedAction,
300
+ dispatchAction: decision.recoveryAction.allowedDispatchAction,
301
+ evidenceOnly: decision.recoveryAction.evidenceOnly,
302
+ productRepairAllowed: decision.recoveryAction.productRepairAllowed,
303
+ expensiveModelAllowed: decision.recoveryAction.expensiveModelAllowed,
304
+ costCeilingUsd: decision.recoveryAction.costCeilingUsd,
305
+ requiresNewEvidence: decision.recoveryAction.retryPolicy?.requireNewEvidence === true,
306
+ nextCommands: decision.recoveryAction.nextCommands,
307
+ requiredEvidence: decision.recoveryAction.requiredArtifacts,
308
+ forbiddenActions: decision.recoveryPlan.forbiddenActions
309
+ }
310
+ };
311
+ }
312
+
313
+ function supportManagerNoBlindLoopGate(
314
+ job: Record<string, any>,
315
+ evidence: Record<string, any>,
316
+ now?: Date | string
317
+ ): AIRunGateResult | undefined {
318
+ return managerNoBlindLoopGateFromHistory(
319
+ supportManagerFailureRecords(job, evidence),
320
+ job.supportV5Budget || job.support_v5_budget || evidence.supportV5Budget || evidence.support_v5_budget,
321
+ now
322
+ );
323
+ }
324
+
325
+ function aicoderManagerFailureRecords(
326
+ app: Record<string, any>,
327
+ job: Record<string, any>,
328
+ evidence: Record<string, any>
329
+ ): ResolveIOAIManagerFailureRecord[] {
330
+ const records: ResolveIOAIManagerFailureRecord[] = [];
331
+ const pushRecord = (value: any, fallback: Partial<ResolveIOAIManagerFailureRecord> = {}): void => {
332
+ const record = normalizeManagerFailureRecord(value, {
333
+ lane: 'aicoder',
334
+ stepType: 'workflow_step',
335
+ ...fallback
336
+ });
337
+ if (record) {
338
+ records.push(record);
339
+ }
340
+ };
341
+ const historySources = [
342
+ job.aicoderV6StepHistory,
343
+ job.aiCoderV6StepHistory,
344
+ job.aicoder_v6_step_history,
345
+ job.ai_coder_v6_step_history,
346
+ job.workflowStepHistory,
347
+ job.workflow_step_history,
348
+ job.stepHistory,
349
+ job.step_history,
350
+ job.managerFailureHistory,
351
+ job.manager_failure_history,
352
+ app.aicoderV6StepHistory,
353
+ app.aiCoderV6StepHistory,
354
+ app.aicoder_v6_step_history,
355
+ app.ai_coder_v6_step_history,
356
+ app.workflowStepHistory,
357
+ app.workflow_step_history,
358
+ evidence.aicoderV6StepHistory,
359
+ evidence.aiCoderV6StepHistory,
360
+ evidence.aicoder_v6_step_history,
361
+ evidence.ai_coder_v6_step_history,
362
+ evidence.workflowStepHistory,
363
+ evidence.workflow_step_history,
364
+ evidence.stepHistory,
365
+ evidence.step_history,
366
+ evidence.managerFailureHistory,
367
+ evidence.manager_failure_history
368
+ ];
369
+ for (const source of historySources) {
370
+ for (const entry of asArray<any>(source)) {
371
+ pushRecord(entry);
372
+ }
373
+ }
374
+ const fingerprintSources = [
375
+ job.aicoderV6FailureFingerprints,
376
+ job.aiCoderV6FailureFingerprints,
377
+ job.aicoder_v6_failure_fingerprints,
378
+ job.ai_coder_v6_failure_fingerprints,
379
+ job.workflowFailureFingerprints,
380
+ job.workflow_failure_fingerprints,
381
+ job.failureFingerprints,
382
+ job.failure_fingerprints,
383
+ app.aicoderV6FailureFingerprints,
384
+ app.aiCoderV6FailureFingerprints,
385
+ app.aicoder_v6_failure_fingerprints,
386
+ app.ai_coder_v6_failure_fingerprints,
387
+ evidence.aicoderV6FailureFingerprints,
388
+ evidence.aiCoderV6FailureFingerprints,
389
+ evidence.aicoder_v6_failure_fingerprints,
390
+ evidence.ai_coder_v6_failure_fingerprints,
391
+ evidence.workflowFailureFingerprints,
392
+ evidence.workflow_failure_fingerprints,
393
+ evidence.failureFingerprints,
394
+ evidence.failure_fingerprints
395
+ ];
396
+ for (const source of fingerprintSources) {
397
+ for (const entry of asArray<any>(source)) {
398
+ const fingerprint = plainObject(entry);
399
+ const count = Math.max(1, Math.min(8, Number(fingerprint.count || fingerprint.repeatedCount || fingerprint.repeated_count || 1) || 1));
400
+ const fallback = {
401
+ lane: cleanText(fingerprint.lane || 'aicoder', 80),
402
+ stepType: cleanText(fingerprint.stepType || fingerprint.step_type || 'workflow_step', 80),
403
+ outcome: cleanText(fingerprint.outcome || 'needs_repair', 80),
404
+ failureClass: cleanText(fingerprint.failureClass || fingerprint.failure_class || fingerprint.class, 120),
405
+ blocker: cleanText(fingerprint.blocker || fingerprint.reason || fingerprint.summary, 1200),
406
+ blockerFingerprint: cleanText(fingerprint.blockerFingerprint || fingerprint.blocker_fingerprint, 180),
407
+ evidenceHash: cleanText(fingerprint.evidenceHash || fingerprint.evidence_hash, 180)
408
+ };
409
+ for (let index = 0; index < count; index += 1) {
410
+ pushRecord(fingerprint, fallback);
411
+ }
412
+ }
413
+ }
414
+ const currentFailure = evidenceObject(
415
+ job.aicoderV6CurrentFailure,
416
+ job.aiCoderV6CurrentFailure,
417
+ job.aicoder_v6_current_failure,
418
+ job.ai_coder_v6_current_failure,
419
+ job.workflowCurrentFailure,
420
+ job.workflow_current_failure,
421
+ job.currentFailure,
422
+ job.current_failure,
423
+ evidence.aicoderV6CurrentFailure,
424
+ evidence.aiCoderV6CurrentFailure,
425
+ evidence.aicoder_v6_current_failure,
426
+ evidence.ai_coder_v6_current_failure,
427
+ evidence.workflowCurrentFailure,
428
+ evidence.workflow_current_failure,
429
+ evidence.currentFailure,
430
+ evidence.current_failure
431
+ );
432
+ pushRecord(currentFailure);
433
+ return records.slice(-80);
434
+ }
435
+
436
+ function aicoderManagerNoBlindLoopGate(
437
+ app: Record<string, any>,
438
+ job: Record<string, any>,
439
+ evidence: Record<string, any>,
440
+ now?: Date | string
441
+ ): AIRunGateResult | undefined {
442
+ return managerNoBlindLoopGateFromHistory(
443
+ aicoderManagerFailureRecords(app, job, evidence),
444
+ job.aicoderV6Budget
445
+ || job.aiCoderV6Budget
446
+ || job.aicoder_v6_budget
447
+ || job.ai_coder_v6_budget
448
+ || evidence.aicoderV6Budget
449
+ || evidence.aiCoderV6Budget
450
+ || evidence.aicoder_v6_budget
451
+ || evidence.ai_coder_v6_budget
452
+ || app.aicoderV6Budget
453
+ || app.aiCoderV6Budget
454
+ || app.aicoder_v6_budget
455
+ || app.ai_coder_v6_budget
456
+ || job.budget
457
+ || evidence.budget,
458
+ now
459
+ );
460
+ }
461
+
462
+ function managerNoBlindLoopMetadata(gate: AIRunGateResult | undefined): Record<string, any> | undefined {
463
+ if (!gate) {
464
+ return undefined;
465
+ }
466
+ return {
467
+ action: cleanText(gate.metadata?.action, 120),
468
+ status: gate.status,
469
+ failureClass: cleanText(gate.metadata?.failureClass, 120),
470
+ sameFailureCount: Number(gate.metadata?.sameFailureCount || 0),
471
+ pingPongCount: Number(gate.metadata?.pingPongCount || 0),
472
+ newEvidence: gate.metadata?.newEvidence === true,
473
+ materialEvidence: gate.metadata?.materialEvidence === true,
474
+ evidenceStrength: cleanText(gate.metadata?.evidenceStrength, 120),
475
+ loopBudgetShouldReset: gate.metadata?.loopBudgetShouldReset === true,
476
+ dispatchAction: cleanText(gate.metadata?.dispatchAction, 160),
477
+ productRepairAllowed: gate.metadata?.productRepairAllowed === true,
478
+ expensiveModelAllowed: gate.metadata?.expensiveModelAllowed === true,
479
+ productRepairFailure: gate.metadata?.productRepairFailure === true,
480
+ requiresNewEvidence: gate.metadata?.requiresNewEvidence === true
481
+ };
482
+ }
483
+
484
+ function hotfixEvidenceCandidates(...sources: any[]): Record<string, any>[] {
485
+ const result: Record<string, any>[] = [];
486
+ const seen = new Set<string>();
487
+ const pushCandidate = (value: any): void => {
488
+ if (Array.isArray(value)) {
489
+ for (const entry of value) {
490
+ pushCandidate(entry);
491
+ }
492
+ return;
493
+ }
494
+ if (!value || typeof value !== 'object') {
495
+ return;
496
+ }
497
+ const nestedValues = [
498
+ value.hotfixEvidence,
499
+ value.hotfix_evidence,
500
+ value.releaseHotfixEvidence,
501
+ value.release_hotfix_evidence,
502
+ value.managerHotfixEvidence,
503
+ value.manager_hotfix_evidence,
504
+ value.backendHotfixEvidence,
505
+ value.backend_hotfix_evidence
506
+ ].filter(Boolean);
507
+ if (nestedValues.length) {
508
+ for (const nestedValue of nestedValues) {
509
+ pushCandidate(nestedValue);
510
+ }
511
+ return;
512
+ }
513
+ if (!(value.channel || value.hotfixChannel || value.hotfix_channel)) {
514
+ return;
515
+ }
516
+ const key = JSON.stringify({
517
+ channel: value.channel || value.hotfixChannel || value.hotfix_channel,
518
+ host: value.target?.host || value.host,
519
+ path: value.target?.path || value.path || value.remotePath || value.remote_path,
520
+ checksum: value.remoteChecksumAfter || value.remote_checksum_after || value.remoteChecksum || value.remote_checksum
521
+ });
522
+ if (seen.has(key)) {
523
+ return;
524
+ }
525
+ seen.add(key);
526
+ result.push(value);
527
+ };
528
+ for (const source of sources) {
529
+ pushCandidate(source);
530
+ }
531
+ return result;
532
+ }
533
+
534
+ function hotfixEvidenceArtifactPaths(evidence: Record<string, any>, normalized: Record<string, any> | undefined): string[] {
535
+ return [
536
+ normalized?.compiledArtifactPath,
537
+ normalized?.builtDistPath,
538
+ normalized?.target?.artifactPath,
539
+ evidence.compiledArtifactPath,
540
+ evidence.compiled_artifact_path,
541
+ evidence.builtDistPath,
542
+ evidence.built_dist_path,
543
+ evidence.artifactPath,
544
+ evidence.artifact_path
545
+ ].map((entry) => cleanText(entry, 500)).filter(Boolean).slice(0, 8);
546
+ }
547
+
548
+ function hotfixEvidenceGate(
549
+ evidence: Record<string, any>,
550
+ now?: Date | string
551
+ ): AIRunGateResult | undefined {
552
+ const validation = validateResolveIOAIManagerHotfixEvidence(evidence);
553
+ if (!validation.normalized && validation.status === 'missing') {
554
+ return undefined;
555
+ }
556
+ const status: AIRunGateResult['status'] = validation.valid
557
+ ? (validation.fullDeployAllowed ? 'warn' : 'pass')
558
+ : validation.status === 'blocked'
559
+ ? 'blocked'
560
+ : 'fail';
561
+ const reason = validation.valid
562
+ ? validation.fullDeployAllowed
563
+ ? 'Hotfix evidence allows exactly one full deploy after force/new-artifact proof.'
564
+ : 'Hotfix evidence is sufficient; rerun the smallest release gate before continuing.'
565
+ : validation.blockers.join('; ');
566
+ return {
567
+ key: 'hotfix_evidence',
568
+ label: 'Hotfix evidence',
569
+ status,
570
+ reason,
571
+ evidenceRefs: hotfixEvidenceArtifactPaths(evidence, validation.normalized as any),
572
+ recordedAt: isoNow(now || validation.normalized?.recordedAt),
573
+ metadata: {
574
+ channel: validation.channel,
575
+ nextAction: validation.nextAction,
576
+ fullDeployAllowed: validation.fullDeployAllowed,
577
+ fullDeployBlocked: validation.fullDeployBlocked,
578
+ hotfixSatisfied: validation.hotfixSatisfied,
579
+ blockers: validation.blockers,
580
+ githubCommitGuard: validation.githubCommitGuard,
581
+ target: validation.normalized?.target
582
+ }
583
+ };
584
+ }
585
+
586
+ function hotfixCommitProofGate(
587
+ evidence: Record<string, any>,
588
+ now?: Date | string
589
+ ): AIRunGateResult | undefined {
590
+ const validation = validateResolveIOAIManagerHotfixEvidence(evidence);
591
+ const guard = validation.githubCommitGuard;
592
+ if (!guard.required) {
593
+ return undefined;
594
+ }
595
+ const status: AIRunGateResult['status'] = guard.passed
596
+ ? 'pass'
597
+ : guard.status === 'invalid'
598
+ ? 'fail'
599
+ : 'blocked';
600
+ return {
601
+ key: 'hotfix_commit_proof',
602
+ label: 'Hotfix GitHub commit proof',
603
+ status,
604
+ reason: guard.passed
605
+ ? 'Hotfix has matching sourceCommitSha, GitHub commit URL, and passed gitPushStatus/gitCommitStatus.'
606
+ : (guard.blockers.join('; ') || 'Manager hotfix is blocked until sourceCommitSha, githubCommitUrl, and passed gitPushStatus prove the hotfix is pushed to GitHub.'),
607
+ evidenceRefs: cleanStringList([guard.githubCommitUrl], 1, 500),
608
+ recordedAt: isoNow(now || validation.normalized?.recordedAt),
609
+ metadata: {
610
+ channel: guard.channel || validation.channel,
611
+ status: guard.status,
612
+ passed: guard.passed,
613
+ managerMustCommitBeforeHotfix: guard.managerMustCommitBeforeHotfix,
614
+ sourceCommitSha: guard.sourceCommitSha,
615
+ githubCommitUrl: guard.githubCommitUrl,
616
+ githubCommitSha: guard.githubCommitSha,
617
+ gitCommitStatus: guard.gitCommitStatus,
618
+ gitPushStatus: guard.gitPushStatus,
619
+ blockers: guard.blockers,
620
+ nextCommands: guard.nextCommands,
621
+ requiredEvidence: guard.requiredEvidence
622
+ }
623
+ };
624
+ }
625
+
626
+ function collectHotfixEvidenceEvents(
627
+ sources: any[],
628
+ events: AIRunEvent[],
629
+ gates: AIRunGateResult[],
630
+ now?: Date | string
631
+ ): void {
632
+ for (const evidence of hotfixEvidenceCandidates(...sources)) {
633
+ const validation = validateResolveIOAIManagerHotfixEvidence(evidence);
634
+ if (!validation.normalized && validation.status === 'missing') {
635
+ continue;
636
+ }
637
+ const artifactPaths = hotfixEvidenceArtifactPaths(evidence, validation.normalized as any);
638
+ pushEvent(events, {
639
+ type: 'hotfix',
640
+ category: validation.channel || 'hotfix',
641
+ message: validation.valid
642
+ ? `Hotfix evidence recorded for ${validation.channel}.`
643
+ : `Incomplete hotfix evidence for ${validation.channel || 'unknown channel'}.`,
644
+ artifactPaths,
645
+ recordedAt: validation.normalized?.recordedAt || eventDate(evidence),
646
+ metadata: {
647
+ status: validation.status,
648
+ nextAction: validation.nextAction,
649
+ fullDeployAllowed: validation.fullDeployAllowed,
650
+ fullDeployBlocked: validation.fullDeployBlocked,
651
+ hotfixSatisfied: validation.hotfixSatisfied,
652
+ blockers: validation.blockers,
653
+ githubCommitGuard: validation.githubCommitGuard,
654
+ target: validation.normalized?.target
655
+ }
656
+ });
657
+ const gate = hotfixEvidenceGate(evidence, now);
658
+ if (gate) {
659
+ gates.push(gate);
660
+ }
661
+ const commitProofGate = hotfixCommitProofGate(evidence, now);
662
+ if (commitProofGate) {
663
+ gates.push(commitProofGate);
664
+ pushEvent(events, {
665
+ type: 'log',
666
+ category: 'hotfix_commit_proof',
667
+ message: commitProofGate.reason,
668
+ artifactPaths: commitProofGate.evidenceRefs,
669
+ recordedAt: commitProofGate.recordedAt,
670
+ metadata: commitProofGate.metadata
671
+ });
672
+ }
673
+ }
674
+ }
675
+
676
+ function collectUsageEvents(entries: Array<Record<string, any>>, events: AIRunEvent[]): AIRunCost | undefined {
677
+ if (!entries.length) {
678
+ return undefined;
679
+ }
680
+ const normalizedEntries = normalizeOpenAIUsageRowsForCosting(entries);
681
+ for (const entry of normalizedEntries) {
682
+ pushEvent(events, {
683
+ type: 'usage',
684
+ category: cleanText(entry.category || entry.kind || entry.phase, 160) || 'usage',
685
+ message: cleanText(entry.summary || entry.description || entry.model || 'Model usage recorded.', 800),
686
+ recordedAt: eventDate(entry),
687
+ metadata: {
688
+ model: entry.model,
689
+ inputTokens: entry.inputTokens ?? entry.input_tokens,
690
+ outputTokens: entry.outputTokens ?? entry.output_tokens,
691
+ totalTokens: entry.totalTokens ?? entry.total_tokens,
692
+ estimatedUsd: entry.estimatedUsd ?? entry.cost_estimate,
693
+ usageSource: entry.usage_source ?? entry.usageSource,
694
+ usageSurface: entry.usage_surface ?? entry.usageSurface,
695
+ usagePhase: entry.usage_phase ?? entry.usagePhase,
696
+ costBasis: entry.cost_basis ?? entry.costBasis,
697
+ isManual: entry.is_manual ?? entry.isManual,
698
+ isUntracked: entry.is_untracked ?? entry.isUntracked,
699
+ untrackedReason: entry.untracked_reason ?? entry.untrackedReason
700
+ }
701
+ });
702
+ }
703
+ return buildAIRunCost(entries);
704
+ }
705
+
706
+ function collectCommitEvents(commits: Array<Record<string, any>>, events: AIRunEvent[]): void {
707
+ for (const commit of commits.slice(0, 80)) {
708
+ pushEvent(events, {
709
+ type: 'git_commit',
710
+ message: cleanText(commit.message || commit.subject || commit.title || commit.hash || commit.sha, 1200),
711
+ recordedAt: eventDate(commit),
712
+ metadata: {
713
+ sha: commit.sha || commit.hash,
714
+ branch: commit.branch,
715
+ author: commit.author
716
+ }
717
+ });
718
+ }
719
+ }
720
+
721
+ function statusFromBoolean(value: any, fallback?: string): string | undefined {
722
+ if (value === true) {
723
+ return 'pass';
724
+ }
725
+ if (value === false) {
726
+ return 'fail';
727
+ }
728
+ return fallback;
729
+ }
730
+
731
+ function collectArtifacts(evidence: Record<string, any> | undefined): AIQaArtifact[] {
732
+ const artifacts: AIQaArtifact[] = [];
733
+ for (const entry of asArray<any>(evidence?.artifacts)) {
734
+ artifacts.push({
735
+ type: entry.type || 'other',
736
+ path: cleanText(entry.path || entry.file || entry.artifactPath, 500),
737
+ url: cleanText(entry.url, 500),
738
+ title: cleanText(entry.title || entry.name, 300),
739
+ caption: cleanText(entry.caption || entry.message, 800),
740
+ metadata: entry.metadata
741
+ });
742
+ }
743
+ const screenshots = asArray<any>(evidence?.screenshots || evidence?.qaScreenshots || evidence?.browserScreenshots);
744
+ for (const screenshot of screenshots.slice(0, 40)) {
745
+ if (typeof screenshot === 'string') {
746
+ artifacts.push({ type: 'screenshot', path: screenshot });
747
+ } else {
748
+ artifacts.push({
749
+ type: 'screenshot',
750
+ path: cleanText(screenshot.path || screenshot.file, 500),
751
+ url: cleanText(screenshot.url, 500),
752
+ title: cleanText(screenshot.title || screenshot.route, 300),
753
+ caption: cleanText(screenshot.caption || screenshot.message, 800),
754
+ metadata: screenshot.metadata
755
+ });
756
+ }
757
+ }
758
+ const artifactRefs = [
759
+ ...asArray<any>(evidence?.artifactPaths),
760
+ ...asArray<any>(evidence?.artifact_paths),
761
+ ...asArray<any>(evidence?.runnerEvidenceArtifacts),
762
+ ...asArray<any>(evidence?.runner_evidence_artifacts),
763
+ ...asArray<any>(evidence?.supportQaArtifacts),
764
+ ...asArray<any>(evidence?.support_qa_artifacts),
765
+ ...asArray<any>(evidence?.qa_artifacts)
766
+ ];
767
+ for (const entry of artifactRefs.slice(0, 60)) {
768
+ if (typeof entry === 'string') {
769
+ artifacts.push({ type: 'other', path: cleanText(entry, 500) });
770
+ }
771
+ else if (entry && typeof entry === 'object') {
772
+ artifacts.push({
773
+ type: entry.type || 'other',
774
+ path: cleanText(entry.path || entry.file || entry.artifactPath || entry.artifact_path, 500),
775
+ url: cleanText(entry.url, 500),
776
+ title: cleanText(entry.title || entry.name || entry.label, 300),
777
+ caption: cleanText(entry.caption || entry.message || entry.summary, 800),
778
+ metadata: entry.metadata
779
+ });
780
+ }
781
+ }
782
+ return artifacts;
783
+ }
784
+
785
+ function collectInfraChecks(evidence: Record<string, any> | undefined): AIQaInfraCheck[] {
786
+ const checks: AIQaInfraCheck[] = [];
787
+ for (const entry of asArray<any>(evidence?.infraChecks || evidence?.preflightChecks)) {
788
+ checks.push({
789
+ name: cleanText(entry.name || entry.check || entry.key, 160) || 'infra_check',
790
+ status: entry.status || statusFromBoolean(entry.passed),
791
+ message: cleanText(entry.message || entry.error || entry.reason, 1000),
792
+ path: cleanText(entry.path, 500),
793
+ durationMs: Number(entry.durationMs ?? entry.duration_ms) || undefined,
794
+ metadata: entry.metadata,
795
+ checkedAt: eventDate(entry)
796
+ });
797
+ }
798
+ if (evidence?.preflightStatus) {
799
+ checks.push({
800
+ name: 'qa_preflight',
801
+ status: evidence.preflightStatus,
802
+ message: cleanText(evidence.preflightMessage || evidence.preflightReason, 1000),
803
+ path: cleanText(evidence.preflightArtifactPath || evidence.preflightPath, 500)
804
+ });
805
+ }
806
+ return checks;
807
+ }
808
+
809
+ function collectCompileResult(evidence: Record<string, any> | undefined): AIQaCompileResult | undefined {
810
+ const build = evidence?.compile || evidence?.build || evidence?.buildEvidence || evidence?.buildResult;
811
+ if (!build && evidence?.compileStatus === undefined && evidence?.buildStatus === undefined) {
812
+ return undefined;
813
+ }
814
+ const status = build?.status
815
+ || evidence?.compileStatus
816
+ || evidence?.buildStatus
817
+ || statusFromBoolean(build?.passed ?? build?.allExpectedPassed ?? evidence?.buildPassed);
818
+ return {
819
+ status,
820
+ command: cleanText(build?.command || evidence?.buildCommand, 500),
821
+ artifactPath: cleanText(build?.artifactPath || build?.logPath || evidence?.buildLogPath, 500),
822
+ message: cleanText(build?.message || build?.error || evidence?.buildMessage, 1000),
823
+ durationMs: Number(build?.durationMs ?? build?.duration_ms) || undefined,
824
+ staleEvidence: build?.staleEvidence === true || evidence?.staleBuildEvidence === true,
825
+ recordedAt: eventDate(build || evidence || {})
826
+ };
827
+ }
828
+
829
+ function collectRouteProbes(evidence: Record<string, any> | undefined): AIQaRouteProbe[] {
830
+ const probes: AIQaRouteProbe[] = [];
831
+ const rows = [
832
+ ...asArray<any>(evidence?.routeProbes),
833
+ ...asArray<any>(evidence?.route_probes),
834
+ ...asArray<any>(evidence?.routes),
835
+ ...asArray<any>(evidence?.browserRoutes),
836
+ ...asArray<any>(evidence?.browser_routes),
837
+ ...asArray<any>(evidence?.workflowProbes),
838
+ ...asArray<any>(evidence?.workflow_probes)
839
+ ];
840
+ const directProbe = plainObject(evidence?.routeProbe || evidence?.route_probe || evidence?.workflowProbe || evidence?.workflow_probe);
841
+ if (Object.keys(directProbe).length) {
842
+ rows.push(directProbe);
843
+ }
844
+ for (const entry of rows) {
845
+ probes.push({
846
+ route: cleanText(entry.route || entry.path || entry.url, 500) || '/',
847
+ status: entry.status || statusFromBoolean(entry.passed),
848
+ screenshot: cleanText(entry.screenshot || entry.screenshotPath, 500),
849
+ caption: cleanText(entry.caption || entry.message, 1000),
850
+ shellOnly: entry.shellOnly === true,
851
+ authenticated: entry.authenticated === true,
852
+ consoleErrors: asArray<string>(entry.consoleErrors),
853
+ networkErrors: asArray<string>(entry.networkErrors),
854
+ message: cleanText(entry.message || entry.error, 1000),
855
+ recordedAt: eventDate(entry)
856
+ });
857
+ }
858
+ const screenshots = asArray<any>(evidence?.screenshots || evidence?.qaScreenshots || evidence?.browserScreenshots);
859
+ for (const screenshot of screenshots.slice(0, 20)) {
860
+ if (typeof screenshot === 'string') {
861
+ probes.push({ route: '/', status: 'pass', screenshot, caption: 'Historical browser screenshot recorded.' });
862
+ } else if (screenshot.route || screenshot.path || screenshot.url || screenshot.screenshot || screenshot.file) {
863
+ probes.push({
864
+ route: cleanText(screenshot.route || screenshot.url || screenshot.path, 500) || '/',
865
+ status: screenshot.status || statusFromBoolean(screenshot.passed, 'pass'),
866
+ screenshot: cleanText(screenshot.screenshot || screenshot.screenshotPath || screenshot.file || screenshot.path, 500),
867
+ caption: cleanText(screenshot.caption || screenshot.message, 1000),
868
+ shellOnly: screenshot.shellOnly === true,
869
+ authenticated: screenshot.authenticated === true,
870
+ recordedAt: eventDate(screenshot)
871
+ });
872
+ }
873
+ }
874
+ return probes;
875
+ }
876
+
877
+ function collectBusinessAssertions(evidence: Record<string, any> | undefined): AIQaBusinessAssertion[] {
878
+ const assertions: AIQaBusinessAssertion[] = [];
879
+ const rows = [
880
+ ...asArray<any>(evidence?.businessAssertions),
881
+ ...asArray<any>(evidence?.business_assertions),
882
+ ...asArray<any>(evidence?.workflowAssertions),
883
+ ...asArray<any>(evidence?.workflow_assertions),
884
+ ...asArray<any>(evidence?.assertions),
885
+ ...asArray<any>(evidence?.qaRows),
886
+ ...asArray<any>(evidence?.qa_rows),
887
+ ...asArray<any>(evidence?.rows),
888
+ ...asArray<any>(evidence?.supportQaAssertions),
889
+ ...asArray<any>(evidence?.support_qa_assertions),
890
+ ...asArray<any>(evidence?.aiQaBusinessAssertions),
891
+ ...asArray<any>(evidence?.ai_qa_business_assertions)
892
+ ];
893
+ for (const entry of rows) {
894
+ assertions.push({
895
+ assertion: cleanText(entry.assertion || entry.name || entry.label || entry.workflow || entry.expected, 1000) || 'business assertion',
896
+ status: entry.status || entry.outcome || entry.result || statusFromBoolean(entry.passed),
897
+ workflow: cleanText(entry.workflow || entry.workflowName || entry.workflow_name, 400),
898
+ route: cleanText(entry.route || entry.url, 500),
899
+ action: cleanText(entry.action || entry.action_under_test || entry.actionUnderTest, 500),
900
+ expected: cleanText(entry.expected || entry.expected_business_state_change || entry.expectedBusinessStateChange, 1000),
901
+ observed: cleanText(entry.observed || entry.actual || entry.after || entry.after_state || entry.afterState, 1000),
902
+ dataProof: cleanText(entry.dataProof || entry.data_proof || entry.domProof || entry.dom_proof || entry.proof || entry.summary, 1400),
903
+ mongoDelta: plainObject(entry.mongoDelta || entry.mongo_delta),
904
+ artifactPaths: cleanStringList(entry.artifactPaths || entry.artifact_paths || entry.artifacts || entry.screenshots || (entry.screenshot ? [entry.screenshot] : []), 30, 500),
905
+ message: cleanText(entry.message || entry.error || entry.reason, 1000),
906
+ recordedAt: eventDate(entry),
907
+ metadata: {
908
+ ...plainObject(entry.metadata),
909
+ ...(entry.acceptanceBlocked === true || entry.acceptance_blocked === true ? { acceptanceBlocked: true } : {}),
910
+ ...(entry.routeOnly === true || entry.route_only === true ? { routeOnly: true } : {})
911
+ }
912
+ });
913
+ }
914
+ if (!assertions.length && evidence?.qaRunOutcome === 'business_assertion_passed') {
915
+ assertions.push({
916
+ assertion: cleanText(evidence.aiRunOutcomeReason || evidence.reason || 'Historical business assertion passed.', 1000),
917
+ status: 'pass',
918
+ dataProof: cleanText(evidence.aiRunOutcomeReason || evidence.reason, 1400)
919
+ });
920
+ }
921
+ if (!assertions.length && evidence?.qaRunOutcome === 'business_assertion_failed') {
922
+ assertions.push({
923
+ assertion: cleanText(evidence.aiRunOutcomeReason || evidence.reason || 'Historical business assertion failed.', 1000),
924
+ status: 'fail',
925
+ observed: cleanText(evidence.aiRunOutcomeReason || evidence.reason, 1000)
926
+ });
927
+ }
928
+ return assertions;
929
+ }
930
+
931
+ function buildQaFromEvidence(evidence: Record<string, any> | undefined, now?: Date | string): AIQaRun {
932
+ return buildAIQaRun({
933
+ infraChecks: collectInfraChecks(evidence),
934
+ compile: collectCompileResult(evidence),
935
+ routeProbes: collectRouteProbes(evidence),
936
+ businessAssertions: collectBusinessAssertions(evidence),
937
+ artifacts: collectArtifacts(evidence),
938
+ now
939
+ });
940
+ }
941
+
942
+ function isoNow(value?: Date | string): string {
943
+ if (value instanceof Date) {
944
+ return value.toISOString();
945
+ }
946
+ if (value) {
947
+ const parsed = new Date(value);
948
+ if (!Number.isNaN(parsed.getTime())) {
949
+ return parsed.toISOString();
950
+ }
951
+ }
952
+ return new Date().toISOString();
953
+ }
954
+
955
+ function normalizeMatchText(value: any): string {
956
+ return cleanText(value, 3000)
957
+ .toLowerCase()
958
+ .replace(/[^a-z0-9]+/g, ' ')
959
+ .replace(/\s+/g, ' ')
960
+ .trim();
961
+ }
962
+
963
+ function importantTokens(value: any): Set<string> {
964
+ const stopWords = new Set([
965
+ 'the', 'and', 'that', 'with', 'from', 'after', 'before', 'when', 'then', 'this', 'must',
966
+ 'should', 'will', 'only', 'into', 'have', 'has', 'are', 'was', 'were', 'been'
967
+ ]);
968
+ return new Set(normalizeMatchText(value).split(' ')
969
+ .filter((token) => token.length >= 4 && !stopWords.has(token)));
970
+ }
971
+
972
+ function businessAssertionPassed(assertion: AIQaBusinessAssertion): boolean {
973
+ return /^(pass|passed|success|ok|done)$/i.test(cleanText(assertion.status, 40));
974
+ }
975
+
976
+ function supportAssertionMatchesDiagnosisProof(assertion: AIQaBusinessAssertion, proofPlan: Record<string, any>): boolean {
977
+ const metadata = assertion.metadata || {};
978
+ if (metadata.diagnosisProofPlanMatched === true || metadata.proofPlanMatched === true || metadata.supportDiagnosisProof === true) {
979
+ return true;
980
+ }
981
+ const requiredBusinessAssertion = proofPlan.business_assertion || proofPlan.businessAssertion;
982
+ const proofParts = [
983
+ requiredBusinessAssertion,
984
+ proofPlan.after,
985
+ proofPlan.data_assertion || proofPlan.dataAssertion,
986
+ proofPlan.artifact_expectation || proofPlan.artifactExpectation
987
+ ].map((part) => normalizeMatchText(part)).filter((part) => part.length >= 16);
988
+ if (!proofParts.length) {
989
+ return true;
990
+ }
991
+ const assertionText = normalizeMatchText([
992
+ assertion.assertion,
993
+ assertion.workflow,
994
+ assertion.action,
995
+ assertion.expected,
996
+ assertion.observed,
997
+ assertion.dataProof,
998
+ assertion.message
999
+ ].filter(Boolean).join(' '));
1000
+ if (proofParts.some((part) => assertionText.includes(part))) {
1001
+ return true;
1002
+ }
1003
+ const proofTokens = importantTokens([
1004
+ requiredBusinessAssertion,
1005
+ proofPlan.after,
1006
+ proofPlan.data_assertion || proofPlan.dataAssertion
1007
+ ].filter(Boolean).join(' '));
1008
+ const assertionTokens = importantTokens(assertionText);
1009
+ if (!proofTokens.size) {
1010
+ return true;
1011
+ }
1012
+ let overlap = 0;
1013
+ for (const token of proofTokens) {
1014
+ if (assertionTokens.has(token)) {
1015
+ overlap += 1;
1016
+ }
1017
+ }
1018
+ return overlap >= Math.min(4, Math.max(3, Math.ceil(proofTokens.size * 0.45)));
1019
+ }
1020
+
1021
+ function supportDiagnosisProofGateResult(diagnosisGate: Record<string, any>, now?: Date | string): AIRunGateResult {
1022
+ const proofPlan = diagnosisGate.proof_plan || diagnosisGate.proofPlan || {};
1023
+ return {
1024
+ key: 'support_diagnosis_business_proof',
1025
+ label: 'Diagnosis proof plan',
1026
+ status: 'blocked',
1027
+ reason: 'Support business assertion did not map to the diagnosis proof_plan. Route proof or generic workflow proof cannot accept the ticket.',
1028
+ evidenceRefs: [],
1029
+ recordedAt: isoNow(now),
1030
+ metadata: {
1031
+ issueClass: cleanText(diagnosisGate.issue_class || diagnosisGate.issueClass, 120),
1032
+ requiredBusinessAssertion: cleanText(proofPlan.business_assertion || proofPlan.businessAssertion, 1000),
1033
+ requiredAfter: cleanText(proofPlan.after, 1000),
1034
+ requiredDataAssertion: cleanText(proofPlan.data_assertion || proofPlan.dataAssertion, 1000)
1035
+ }
1036
+ };
1037
+ }
1038
+
1039
+ const SUPPORT_ROOT_CAUSE_ENTRY_REQUIRED_FIELDS = [
1040
+ 'issue_case',
1041
+ 'issue_class',
1042
+ 'accepted_hypothesis',
1043
+ 'rejected_alternatives',
1044
+ 'failing_path',
1045
+ 'owner_files',
1046
+ 'proof_plan',
1047
+ 'evidence'
1048
+ ];
1049
+
1050
+ const SUPPORT_ROOT_CAUSE_ENTRY_ISSUE_CLASSES = [
1051
+ 'no_op_submit',
1052
+ 'missing_wrong_data',
1053
+ 'filter_query_mismatch',
1054
+ 'invoice_pdf_export',
1055
+ 'upload_import',
1056
+ 'route_auth_hydration',
1057
+ 'slow_query_performance'
1058
+ ];
1059
+
1060
+ function supportRootCauseEntryContractGate(contract: Record<string, any>, now?: Date | string): AIRunGateResult | undefined {
1061
+ if (!contract || !Object.keys(contract).length) {
1062
+ return undefined;
1063
+ }
1064
+ const requiredOutput = plainObject(contract.required_output || contract.requiredOutput);
1065
+ const ownerPolicy = plainObject(contract.owner_file_policy || contract.ownerFilePolicy);
1066
+ const businessProofPolicy = plainObject(contract.business_proof_policy || contract.businessProofPolicy);
1067
+ const failurePolicy = plainObject(contract.failure_policy || contract.failurePolicy);
1068
+ const probes = asArray<Record<string, any>>(contract.issue_class_probes || contract.issueClassProbes);
1069
+ const requiredFields = cleanStringList(requiredOutput.required_fields || requiredOutput.requiredFields, 30, 160);
1070
+ const issueClasses = probes.map((probe) => cleanText(probe.issue_class || probe.issueClass, 120)).filter(Boolean);
1071
+ const missingRequiredFields = SUPPORT_ROOT_CAUSE_ENTRY_REQUIRED_FIELDS.filter((field) => !requiredFields.includes(field));
1072
+ const missingIssueClasses = SUPPORT_ROOT_CAUSE_ENTRY_ISSUE_CLASSES.filter((issueClass) => !issueClasses.includes(issueClass));
1073
+ const blockers: string[] = [];
1074
+ if (!cleanText(contract.contract_id || contract.contractId, 160)) {
1075
+ blockers.push('Root-cause entry contract is missing contract_id.');
1076
+ }
1077
+ if (cleanText(requiredOutput.object_key || requiredOutput.objectKey, 120) !== 'support_diagnosis_gate') {
1078
+ blockers.push('Root-cause entry contract must require support_diagnosis_gate output.');
1079
+ }
1080
+ if (missingRequiredFields.length) {
1081
+ blockers.push(`Root-cause entry contract missing required output fields: ${missingRequiredFields.join(', ')}.`);
1082
+ }
1083
+ const ownerFileMax = Number(ownerPolicy.max_files || ownerPolicy.maxFiles || 0);
1084
+ if (!Number.isFinite(ownerFileMax) || ownerFileMax <= 0 || ownerFileMax > 12) {
1085
+ blockers.push('Root-cause entry contract must cap owner_files to a small exact file set.');
1086
+ }
1087
+ if (ownerPolicy.edits_outside_owner_files_require_revised_diagnosis !== true && ownerPolicy.editsOutsideOwnerFilesRequireRevisedDiagnosis !== true) {
1088
+ blockers.push('Root-cause entry contract must require revised diagnosis before edits outside owner_files.');
1089
+ }
1090
+ if (businessProofPolicy.requires_aiqa_business_assertion !== true && businessProofPolicy.requiresAiqaBusinessAssertion !== true) {
1091
+ blockers.push('Root-cause entry contract must require AIQaBusinessAssertion business proof.');
1092
+ }
1093
+ if (businessProofPolicy.route_load_screenshot_scorecard_model_claim_not_acceptance !== true && businessProofPolicy.routeLoadScreenshotScorecardModelClaimNotAcceptance !== true) {
1094
+ blockers.push('Root-cause entry contract must reject route-load/screenshot/scorecard/model-claim acceptance.');
1095
+ }
1096
+ if (failurePolicy.repeated_failure_without_new_evidence_parks_run !== true && failurePolicy.repeatedFailureWithoutNewEvidenceParksRun !== true) {
1097
+ blockers.push('Root-cause entry contract must park repeated failures without new evidence.');
1098
+ }
1099
+ if (missingIssueClasses.length) {
1100
+ blockers.push(`Root-cause entry contract missing issue-class probes: ${missingIssueClasses.join(', ')}.`);
1101
+ }
1102
+ const status: AIRunGateResult['status'] = blockers.length ? 'fail' : 'pass';
1103
+ return {
1104
+ key: 'support_root_cause_entry_contract',
1105
+ label: 'Root-cause entry contract',
1106
+ status,
1107
+ reason: blockers.length
1108
+ ? blockers.join(' ')
1109
+ : 'Support run has a structured root-cause-first entry contract with issue-class probes, owner-file policy, and business-proof policy.',
1110
+ evidenceRefs: cleanStringList(businessProofPolicy.required_artifacts || businessProofPolicy.requiredArtifacts, 20, 500),
1111
+ recordedAt: isoNow(now),
1112
+ metadata: {
1113
+ contractId: cleanText(contract.contract_id || contract.contractId, 160),
1114
+ version: cleanText(contract.version, 120),
1115
+ status: cleanText(contract.status, 120),
1116
+ requiredOutputKey: cleanText(requiredOutput.object_key || requiredOutput.objectKey, 120),
1117
+ requiredFields,
1118
+ missingRequiredFields,
1119
+ ownerFileMax,
1120
+ requiresRevisedDiagnosisForOutOfScopeEdits: ownerPolicy.edits_outside_owner_files_require_revised_diagnosis === true || ownerPolicy.editsOutsideOwnerFilesRequireRevisedDiagnosis === true,
1121
+ requiresBusinessAssertion: businessProofPolicy.requires_aiqa_business_assertion === true || businessProofPolicy.requiresAiqaBusinessAssertion === true,
1122
+ routeOnlyAcceptanceRejected: businessProofPolicy.route_load_screenshot_scorecard_model_claim_not_acceptance === true || businessProofPolicy.routeLoadScreenshotScorecardModelClaimNotAcceptance === true,
1123
+ issueClassProbeCount: issueClasses.length,
1124
+ issueClasses,
1125
+ missingIssueClasses
1126
+ }
1127
+ };
1128
+ }
1129
+
1130
+ function applySupportDiagnosisProofGate(qa: AIQaRun, diagnosisGate: Record<string, any>, now?: Date | string): AIQaRun {
1131
+ const proofPlan = diagnosisGate.proof_plan || diagnosisGate.proofPlan || {};
1132
+ const requiredBusinessAssertion = cleanText(proofPlan.business_assertion || proofPlan.businessAssertion, 1000);
1133
+ const requiredAfter = cleanText(proofPlan.after, 1000);
1134
+ if (!Object.keys(proofPlan).length || (!requiredBusinessAssertion && !requiredAfter)) {
1135
+ return qa;
1136
+ }
1137
+ const passedAssertions = qa.businessAssertions.filter(businessAssertionPassed);
1138
+ if (!passedAssertions.length) {
1139
+ return qa;
1140
+ }
1141
+ if (passedAssertions.some((assertion) => supportAssertionMatchesDiagnosisProof(assertion, proofPlan))) {
1142
+ return qa;
1143
+ }
1144
+ const gate = supportDiagnosisProofGateResult(diagnosisGate, now);
1145
+ return {
1146
+ ...qa,
1147
+ outcome: qa.routeProbes.some((probe) => /^(pass|passed|success|ok|done)$/i.test(cleanText(probe.status, 40)))
1148
+ ? 'route_only_pass'
1149
+ : 'incomplete',
1150
+ gateResults: [
1151
+ ...qa.gateResults.filter((existing) => !(existing.key === 'qa_business_assertion' && existing.status === 'pass')),
1152
+ gate
1153
+ ]
1154
+ };
1155
+ }
1156
+
1157
+ const SUPPORT_PRODUCT_REPAIR_STEP_TYPES = new Set([
1158
+ 'build_repair',
1159
+ 'owner_scoped_repair',
1160
+ 'product_repair',
1161
+ 'code_repair'
1162
+ ]);
1163
+
1164
+ const SUPPORT_PRODUCT_REPAIR_ACTIONS = new Set([
1165
+ 'run_owner_scoped_repair',
1166
+ 'run_targeted_product_repair',
1167
+ 'allow_product_repair'
1168
+ ]);
1169
+
1170
+ function supportSourceEditFiles(value: any): string[] {
1171
+ return cleanStringList(value, 80, 500)
1172
+ .filter((filePath) => {
1173
+ const normalized = filePath.replace(/\\/g, '/').toLowerCase();
1174
+ if (!normalized || /(^|\/)(qa-artifacts|runner-evidence|logs?|tmp|dist|node_modules)(\/|$)/.test(normalized)) {
1175
+ return false;
1176
+ }
1177
+ return /\.(?:ts|tsx|js|jsx|html|scss|css|json|mjs|cjs|vue|svelte|md)$/i.test(normalized);
1178
+ });
1179
+ }
1180
+
1181
+ function supportRepairActivityFromRecord(
1182
+ value: any,
1183
+ source: string,
1184
+ index: number
1185
+ ): { changedFiles: string[]; activityRef?: string } | undefined {
1186
+ const record = plainObject(value);
1187
+ if (!Object.keys(record).length) {
1188
+ return undefined;
1189
+ }
1190
+ const stepType = cleanText(record.stepType || record.step_type || record.type, 120);
1191
+ const action = cleanText(record.action || record.primaryAction || record.primary_action || record.allowedDispatchAction || record.allowed_dispatch_action, 120);
1192
+ const lane = cleanText(record.lane || record.phase || record.category, 120).toLowerCase();
1193
+ const changedFiles = supportSourceEditFiles(
1194
+ record.changedFiles
1195
+ || record.changed_files
1196
+ || record.modifiedFiles
1197
+ || record.modified_files
1198
+ || record.editedFiles
1199
+ || record.edited_files
1200
+ || record.files
1201
+ );
1202
+ const explicitProductRepair = SUPPORT_PRODUCT_REPAIR_STEP_TYPES.has(stepType)
1203
+ || SUPPORT_PRODUCT_REPAIR_ACTIONS.has(action)
1204
+ || (lane === 'build' && changedFiles.length > 0);
1205
+ if (!explicitProductRepair) {
1206
+ return undefined;
1207
+ }
1208
+ const activityRef = cleanText(
1209
+ record.microtaskId
1210
+ || record.microtask_id
1211
+ || record.taskId
1212
+ || record.task_id
1213
+ || record.jobId
1214
+ || record.job_id
1215
+ || record._id
1216
+ || record.id
1217
+ || `${source}:${index}`,
1218
+ 240
1219
+ );
1220
+ return { changedFiles, activityRef };
1221
+ }
1222
+
1223
+ function supportProductRepairEvidence(input: SupportAIRunAdapterInput, evidence: Record<string, any>): {
1224
+ attempted: boolean;
1225
+ changedFiles: string[];
1226
+ activityRefs: string[];
1227
+ activityCount: number;
1228
+ } {
1229
+ const job = input.job || {};
1230
+ const records = [
1231
+ ...asArray<any>(job.supportV5StepHistory),
1232
+ ...asArray<any>(job.support_v5_step_history),
1233
+ ...asArray<any>(job.stepHistory),
1234
+ ...asArray<any>(job.step_history),
1235
+ ...asArray<any>(evidence.supportV5StepHistory),
1236
+ ...asArray<any>(evidence.support_v5_step_history),
1237
+ ...asArray<any>(evidence.stepHistory),
1238
+ ...asArray<any>(evidence.step_history),
1239
+ ...asArray<any>(input.taskEvents),
1240
+ ...asArray<any>(input.aiJobs),
1241
+ ...asArray<any>(input.buildPlans)
1242
+ ];
1243
+ const activityRefs: string[] = [];
1244
+ const changedFiles: string[] = [];
1245
+ let activityCount = 0;
1246
+ records.forEach((record, index) => {
1247
+ const activity = supportRepairActivityFromRecord(record, 'support_repair_activity', index);
1248
+ if (!activity) {
1249
+ return;
1250
+ }
1251
+ activityCount += 1;
1252
+ if (activity.activityRef) {
1253
+ activityRefs.push(activity.activityRef);
1254
+ }
1255
+ changedFiles.push(...activity.changedFiles);
1256
+ });
1257
+ const directChangedFiles = supportSourceEditFiles([
1258
+ ...asArray<any>(job.changedFiles),
1259
+ ...asArray<any>(job.changed_files),
1260
+ ...asArray<any>(job.modifiedFiles),
1261
+ ...asArray<any>(job.modified_files),
1262
+ ...asArray<any>(job.editedFiles),
1263
+ ...asArray<any>(job.edited_files),
1264
+ ...asArray<any>(evidence.changedFiles),
1265
+ ...asArray<any>(evidence.changed_files),
1266
+ ...asArray<any>(evidence.modifiedFiles),
1267
+ ...asArray<any>(evidence.modified_files),
1268
+ ...asArray<any>(evidence.editedFiles),
1269
+ ...asArray<any>(evidence.edited_files),
1270
+ ...asArray<any>(input.commits).flatMap((commit) => asArray<any>(commit.changedFiles || commit.changed_files || commit.files))
1271
+ ]);
1272
+ if (directChangedFiles.length) {
1273
+ activityCount += 1;
1274
+ activityRefs.push('changed_files');
1275
+ changedFiles.push(...directChangedFiles);
1276
+ }
1277
+ const uniqueChangedFiles = Array.from(new Set(changedFiles.map((filePath) => cleanText(filePath, 500)).filter(Boolean))).slice(0, 80);
1278
+ return {
1279
+ attempted: activityCount > 0,
1280
+ changedFiles: uniqueChangedFiles,
1281
+ activityRefs: Array.from(new Set(activityRefs.map((ref) => cleanText(ref, 240)).filter(Boolean))).slice(0, 40),
1282
+ activityCount
1283
+ };
1284
+ }
1285
+
1286
+ function supportDiagnosisBeforeRepairGate(
1287
+ diagnosisGate: Record<string, any>,
1288
+ repairEvidence: ReturnType<typeof supportProductRepairEvidence>,
1289
+ now?: Date | string
1290
+ ): AIRunGateResult | undefined {
1291
+ if (!repairEvidence.attempted) {
1292
+ return undefined;
1293
+ }
1294
+ const validation = validateResolveIOSupportDiagnosisGate(diagnosisGate);
1295
+ const status: AIRunGateResult['status'] = validation.valid ? 'pass' : 'blocked';
1296
+ return {
1297
+ key: 'support_diagnosis_before_repair',
1298
+ label: 'Support diagnosis before repair',
1299
+ status,
1300
+ reason: validation.valid
1301
+ ? 'Support product repair has a valid root-cause diagnosis gate and owner-file scope.'
1302
+ : 'Support product repair is blocked until a valid SupportDiagnosisGate records reproduction/classification, accepted hypothesis, rejected alternatives, owner_files, and before/action/after proof.',
1303
+ evidenceRefs: repairEvidence.changedFiles,
1304
+ recordedAt: isoNow(now),
1305
+ metadata: {
1306
+ repairAttempted: true,
1307
+ activityCount: repairEvidence.activityCount,
1308
+ activityRefs: repairEvidence.activityRefs,
1309
+ changedFiles: repairEvidence.changedFiles,
1310
+ diagnosisValid: validation.valid,
1311
+ diagnosisStatus: validation.status,
1312
+ blockers: validation.blockers,
1313
+ ownerFiles: validation.normalized?.owner_files || [],
1314
+ issueClass: validation.normalized?.issue_class,
1315
+ productRepairAllowed: validation.valid,
1316
+ allowedDispatchAction: validation.valid ? 'run_owner_scoped_repair' : 'run_read_only_diagnosis',
1317
+ requiredEvidence: validation.valid
1318
+ ? ['AIQaBusinessAssertion mapped to diagnosis proof_plan before acceptance']
1319
+ : ['support_diagnosis_gate JSON', 'reproduction or blocked-reproduction reason', 'accepted_hypothesis with evidence', 'owner_files', 'proof_plan before/action/after']
1320
+ }
1321
+ };
1322
+ }
1323
+
1324
+ function evidenceObject(...values: any[]): Record<string, any> {
1325
+ for (const value of values) {
1326
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
1327
+ return value;
1328
+ }
1329
+ }
1330
+ return {};
1331
+ }
1332
+
1333
+ function mergeEvidenceRecords(...values: any[]): Record<string, any> {
1334
+ const merged: Record<string, any> = {};
1335
+ for (const value of values) {
1336
+ const source = plainObject(value);
1337
+ for (const [key, entry] of Object.entries(source)) {
1338
+ if (entry === undefined || entry === null || entry === '') {
1339
+ continue;
1340
+ }
1341
+ if (Array.isArray(entry)) {
1342
+ merged[key] = [...asArray(merged[key]), ...entry];
1343
+ continue;
1344
+ }
1345
+ if (plainObject(entry) === entry && plainObject(merged[key]) === merged[key]) {
1346
+ merged[key] = { ...merged[key], ...entry };
1347
+ continue;
1348
+ }
1349
+ merged[key] = entry;
1350
+ }
1351
+ }
1352
+ return merged;
1353
+ }
1354
+
1355
+ function buildSupportQaEvidence(input: SupportAIRunAdapterInput): Record<string, any> {
1356
+ const job = input.job || {};
1357
+ const ticket = input.ticket || {};
1358
+ const qaCursor = plainObject(job.supportQaValidationCursor || job.support_qa_validation_cursor);
1359
+ const autonomousDecision = plainObject(job.supportV5AutonomousDecision || job.support_v5_autonomous_decision);
1360
+ const businessProofReadiness = plainObject(
1361
+ autonomousDecision.businessProofReadiness
1362
+ || autonomousDecision.business_proof_readiness
1363
+ || job.businessProofReadiness
1364
+ || job.business_proof_readiness
1365
+ );
1366
+ const customerReplyPolicy = plainObject(
1367
+ autonomousDecision.customerReplyPolicy
1368
+ || autonomousDecision.customer_reply_policy
1369
+ || job.customerReplyPolicy
1370
+ || job.customer_reply_policy
1371
+ );
1372
+ const humanReviewPacket = plainObject(
1373
+ autonomousDecision.humanReviewPacket
1374
+ || autonomousDecision.human_review_packet
1375
+ || customerReplyPolicy.humanReviewPacket
1376
+ || customerReplyPolicy.human_review_packet
1377
+ || job.humanReviewPacket
1378
+ || job.human_review_packet
1379
+ );
1380
+ const evidence = mergeEvidenceRecords(
1381
+ qaCursor,
1382
+ job.qaEvidence,
1383
+ job.qa_evidence,
1384
+ ticket.qaEvidence,
1385
+ ticket.qa_evidence,
1386
+ input.qaEvidence
1387
+ );
1388
+ evidence.supportQaAssertions = [
1389
+ ...asArray(job.supportQaAssertions),
1390
+ ...asArray(job.support_qa_assertions),
1391
+ ...asArray(evidence.supportQaAssertions)
1392
+ ];
1393
+ evidence.aiQaBusinessAssertions = [
1394
+ ...asArray(job.aiQaBusinessAssertions),
1395
+ ...asArray(job.ai_qa_business_assertions),
1396
+ ...asArray(evidence.aiQaBusinessAssertions)
1397
+ ];
1398
+ evidence.runnerEvidenceArtifacts = [
1399
+ ...asArray(job.runnerEvidenceArtifacts),
1400
+ ...asArray(job.runner_evidence_artifacts),
1401
+ ...asArray(evidence.runnerEvidenceArtifacts)
1402
+ ];
1403
+ evidence.supportQaArtifacts = [
1404
+ ...asArray(job.supportQaArtifacts),
1405
+ ...asArray(job.support_qa_artifacts),
1406
+ ...asArray(job.qa_artifacts),
1407
+ ...asArray(evidence.supportQaArtifacts)
1408
+ ];
1409
+ if (Object.keys(businessProofReadiness).length) {
1410
+ evidence.businessProofReadiness = businessProofReadiness;
1411
+ }
1412
+ if (Object.keys(customerReplyPolicy).length) {
1413
+ evidence.customerReplyPolicy = {
1414
+ ...customerReplyPolicy,
1415
+ ...(Object.keys(humanReviewPacket).length ? { humanReviewPacket } : {})
1416
+ };
1417
+ }
1418
+ else if (Object.keys(humanReviewPacket).length) {
1419
+ evidence.customerReplyPolicy = { humanReviewPacket };
1420
+ }
1421
+ return evidence;
1422
+ }
1423
+
1424
+ function supportBusinessProofReadinessGate(
1425
+ readiness: Record<string, any>,
1426
+ now?: Date | string
1427
+ ): AIRunGateResult | undefined {
1428
+ if (!readiness || !Object.keys(readiness).length) {
1429
+ return undefined;
1430
+ }
1431
+ const ready = readiness.ready === true;
1432
+ const readinessStatus = cleanText(readiness.status, 120).toLowerCase();
1433
+ const blockers = cleanStringList(readiness.blockers, 20, 500);
1434
+ const artifactPaths = cleanStringList(readiness.artifactPaths || readiness.artifact_paths, 40, 500);
1435
+ const proofFingerprint = cleanText(readiness.proofFingerprint || readiness.proof_fingerprint, 160);
1436
+ const artifactFingerprint = cleanText(readiness.artifactFingerprint || readiness.artifact_fingerprint, 160);
1437
+ const proofFreshness = cleanText(readiness.proofFreshness || readiness.proof_freshness, 120);
1438
+ const status: AIRunGateResult['status'] = ready
1439
+ ? 'pass'
1440
+ : /fail|failed/.test(readinessStatus)
1441
+ ? 'fail'
1442
+ : /missing|route_only|blocked|weak|stale|incomplete/.test(readinessStatus)
1443
+ ? 'blocked'
1444
+ : 'warn';
1445
+ return {
1446
+ key: 'support_business_proof_readiness',
1447
+ label: 'Support business proof readiness',
1448
+ status,
1449
+ reason: cleanText(readiness.reason || blockers.join('; ') || readinessStatus || 'Support business proof readiness recorded.', 1200),
1450
+ evidenceRefs: artifactPaths,
1451
+ recordedAt: isoNow(now || readiness.recordedAt || readiness.recorded_at),
1452
+ metadata: {
1453
+ ready,
1454
+ status: readinessStatus,
1455
+ blockers,
1456
+ requiredEvidence: cleanStringList(readiness.requiredEvidence || readiness.required_evidence, 20, 500),
1457
+ proofFingerprint,
1458
+ artifactFingerprint,
1459
+ proofFreshness
1460
+ }
1461
+ };
1462
+ }
1463
+
1464
+ function supportCustomerReplyPolicyGate(
1465
+ policy: Record<string, any>,
1466
+ now?: Date | string
1467
+ ): AIRunGateResult | undefined {
1468
+ if (!policy || !Object.keys(policy).length) {
1469
+ return undefined;
1470
+ }
1471
+ const action = cleanText(policy.action, 120);
1472
+ const safety = cleanText(policy.safety, 120);
1473
+ const reason = cleanText(policy.reason, 1200);
1474
+ const canDraft = policy.canDraftCustomerReply === true || policy.can_draft_customer_reply === true;
1475
+ const canSend = policy.canSendCustomerReply === true || policy.can_send_customer_reply === true;
1476
+ const reviewPacket = plainObject(policy.humanReviewPacket || policy.human_review_packet);
1477
+ const reviewType = cleanText(reviewPacket.reviewType || reviewPacket.review_type, 160);
1478
+ const evidenceRefs = cleanStringList(reviewPacket.evidenceRefs || reviewPacket.evidence_refs, 40, 500);
1479
+ const blockers = cleanStringList(policy.blockers || reviewPacket.blockers, 20, 500);
1480
+ const gateStatus: AIRunGateResult['status'] = canSend
1481
+ ? 'blocked'
1482
+ : action === 'draft_resolution_reply' && canDraft
1483
+ ? 'warn'
1484
+ : action === 'ask_clarification' && canDraft
1485
+ ? 'warn'
1486
+ : 'blocked';
1487
+ return {
1488
+ key: 'support_customer_reply_policy',
1489
+ label: 'Support customer reply policy',
1490
+ status: gateStatus,
1491
+ reason: reason || action || 'Support customer reply policy recorded.',
1492
+ evidenceRefs,
1493
+ recordedAt: isoNow(now || policy.recordedAt || policy.recorded_at || reviewPacket.createdAt || reviewPacket.created_at),
1494
+ metadata: {
1495
+ action,
1496
+ safety,
1497
+ canDraftCustomerReply: canDraft,
1498
+ canSendCustomerReply: canSend,
1499
+ confidenceLevel: cleanText(policy.confidenceLevel || policy.confidence_level, 120),
1500
+ reviewType,
1501
+ primaryAction: cleanText(reviewPacket.primaryAction || reviewPacket.primary_action, 160),
1502
+ requiresHumanApproval: reviewPacket.requiresHumanApproval !== false && reviewPacket.requires_human_approval !== false,
1503
+ questionPresent: !!cleanText(policy.clarificationQuestion || policy.clarification_question || reviewPacket.question, 1000),
1504
+ blockers
1505
+ }
1506
+ };
1507
+ }
1508
+
1509
+ function supportNextActionContractObject(ticket: Record<string, any>, job: Record<string, any>, evidence: Record<string, any>): Record<string, any> {
1510
+ const ticketAutomation = plainObject(ticket.automation);
1511
+ const ticketManager = plainObject(ticketAutomation.manager || ticket.manager);
1512
+ const autonomousDecision = plainObject(
1513
+ job.supportV5AutonomousDecision
1514
+ || job.support_v5_autonomous_decision
1515
+ || evidence.supportV5AutonomousDecision
1516
+ || evidence.support_v5_autonomous_decision
1517
+ || ticket.supportV5AutonomousDecision
1518
+ || ticket.support_v5_autonomous_decision
1519
+ );
1520
+ return evidenceObject(
1521
+ autonomousDecision.nextActionContract,
1522
+ autonomousDecision.next_action_contract,
1523
+ job.supportV5NextActionContract,
1524
+ job.support_v5_next_action_contract,
1525
+ job.nextActionContract,
1526
+ job.next_action_contract,
1527
+ evidence.supportV5NextActionContract,
1528
+ evidence.support_v5_next_action_contract,
1529
+ evidence.nextActionContract,
1530
+ evidence.next_action_contract,
1531
+ ticket.supportV5NextActionContract,
1532
+ ticket.support_v5_next_action_contract,
1533
+ ticket.nextActionContract,
1534
+ ticket.next_action_contract,
1535
+ ticketManager.nextActionContract,
1536
+ ticketManager.next_action_contract
1537
+ );
1538
+ }
1539
+
1540
+ function supportNextActionContractRequired(ticket: Record<string, any>, job: Record<string, any>, evidence: Record<string, any>): boolean {
1541
+ const ticketAutomation = plainObject(ticket.automation);
1542
+ const ticketManager = plainObject(ticketAutomation.manager || ticket.manager);
1543
+ const workflowMarkers = [
1544
+ job.supportWorkflowMode,
1545
+ job.workflowMode,
1546
+ job.workflow_mode,
1547
+ job.supportWorkflowVersion,
1548
+ job.support_workflow_version,
1549
+ ticket.supportWorkflowMode,
1550
+ ticket.workflowMode,
1551
+ ticket.workflow_mode
1552
+ ].map((value) => cleanText(value, 80).toLowerCase());
1553
+ return workflowMarkers.some((value) => value === 'support_v5' || value === 'v5')
1554
+ || !!job.supportV5SupervisorState
1555
+ || !!job.supportV5MicrotaskLedger
1556
+ || !!job.supportV5StepHistory
1557
+ || !!job.supportV5DiagnosisGate
1558
+ || !!job.supportV5DiagnosisEvidencePack
1559
+ || !!job.support_v5_supervisor_state
1560
+ || !!job.support_v5_microtask_ledger
1561
+ || !!job.support_v5_step_history
1562
+ || !!job.support_v5_diagnosis_gate
1563
+ || !!job.support_v5_diagnosis_evidence_pack
1564
+ || !!job.supportRootCauseEntryContract
1565
+ || !!job.support_root_cause_entry_contract
1566
+ || !!evidence.supportV5AutonomousDecision
1567
+ || !!evidence.support_v5_autonomous_decision
1568
+ || !!evidence.rootCauseReadiness
1569
+ || !!evidence.root_cause_readiness
1570
+ || !!ticketManager.root_cause_entry_contract
1571
+ || !!ticketManager.rootCauseEntryContract
1572
+ || !!ticketManager.last_watchdog_decision;
1573
+ }
1574
+
1575
+ function supportNextActionContractMetadata(contract: Record<string, any>): Record<string, any> {
1576
+ const decisionBasis = plainObject(contract.decisionBasis || contract.decision_basis);
1577
+ const costRisk = plainObject(contract.costRisk || contract.cost_risk);
1578
+ const source = {
1579
+ contractId: cleanText(contract.contractId || contract.contract_id, 200),
1580
+ action: cleanText(contract.action, 160),
1581
+ label: cleanText(contract.label, 300),
1582
+ primaryCommand: cleanText(contract.primaryCommand || contract.primary_command, 240),
1583
+ lane: cleanText(contract.lane, 120),
1584
+ stepType: cleanText(contract.stepType || contract.step_type, 120),
1585
+ safeToAutoRun: booleanFlag(contract.safeToAutoRun ?? contract.safe_to_auto_run),
1586
+ requiresHumanApproval: booleanFlag(contract.requiresHumanApproval ?? contract.requires_human_approval),
1587
+ canRunWithoutCodexMonitor: booleanFlag(contract.canRunWithoutCodexMonitor ?? contract.can_run_without_codex_monitor),
1588
+ codexFallbackRequired: booleanFlag(contract.codexFallbackRequired ?? contract.codex_fallback_required),
1589
+ codexFallbackReason: cleanText(contract.codexFallbackReason || contract.codex_fallback_reason, 1000),
1590
+ rootCauseFirstSatisfied: booleanFlag(contract.rootCauseFirstSatisfied ?? contract.root_cause_first_satisfied),
1591
+ preconditions: cleanStringList(contract.preconditions, 40, 500),
1592
+ expectedStateTransition: cleanText(contract.expectedStateTransition || contract.expected_state_transition, 1000),
1593
+ successEvidence: cleanStringList(contract.successEvidence || contract.success_evidence, 40, 500),
1594
+ stopConditions: cleanStringList(contract.stopConditions || contract.stop_conditions, 40, 500),
1595
+ forbiddenActions: cleanStringList(contract.forbiddenActions || contract.forbidden_actions, 40, 500),
1596
+ ownerFiles: cleanStringList(contract.ownerFiles || contract.owner_files || decisionBasis.ownerFiles || decisionBasis.owner_files, 40, 500),
1597
+ blockers: cleanStringList(contract.blockers, 40, 500),
1598
+ nextCommands: cleanStringList(contract.nextCommands || contract.next_commands, 40, 500),
1599
+ costRisk: {
1600
+ level: cleanText(costRisk.level || contract.costRiskLevel || contract.cost_risk_level, 80),
1601
+ estimatedUsd: numberValue(costRisk.estimatedUsd ?? costRisk.estimated_usd ?? contract.estimatedUsd ?? contract.estimated_usd),
1602
+ reason: cleanText(costRisk.reason, 800)
1603
+ },
1604
+ diagnosisValid: booleanFlag(decisionBasis.diagnosisValid ?? decisionBasis.diagnosis_valid),
1605
+ ownerFilesReady: booleanFlag(decisionBasis.ownerFilesReady ?? decisionBasis.owner_files_ready),
1606
+ proofPlanReady: booleanFlag(decisionBasis.proofPlanReady ?? decisionBasis.proof_plan_ready),
1607
+ businessProofReady: booleanFlag(decisionBasis.businessProofReady ?? decisionBasis.business_proof_ready),
1608
+ evidenceFreshnessStatus: cleanText(decisionBasis.evidenceFreshnessStatus || decisionBasis.evidence_freshness_status, 120),
1609
+ evidenceStrength: cleanText(decisionBasis.evidenceStrength || decisionBasis.evidence_strength, 120),
1610
+ failureClass: cleanText(decisionBasis.failureClass || decisionBasis.failure_class, 120),
1611
+ blockerFingerprint: cleanText(decisionBasis.blockerFingerprint || decisionBasis.blocker_fingerprint, 200),
1612
+ evidenceHash: cleanText(decisionBasis.evidenceHash || decisionBasis.evidence_hash, 200),
1613
+ sameFailureCount: numberValue(decisionBasis.sameFailureCount ?? decisionBasis.same_failure_count),
1614
+ hotfixCommitRequired: booleanFlag(decisionBasis.hotfixCommitRequired ?? decisionBasis.hotfix_commit_required ?? contract.hotfixCommitRequired ?? contract.hotfix_commit_required),
1615
+ liveHotfixBlockedUntilCommit: booleanFlag(decisionBasis.liveHotfixBlockedUntilCommit ?? decisionBasis.live_hotfix_blocked_until_commit ?? contract.liveHotfixBlockedUntilCommit ?? contract.live_hotfix_blocked_until_commit)
1616
+ };
1617
+ return source;
1618
+ }
1619
+
1620
+ function supportNextActionContractGate(contract: Record<string, any>, now?: Date | string, required = false): AIRunGateResult | undefined {
1621
+ if (!contract || !Object.keys(contract).length) {
1622
+ if (!required) {
1623
+ return undefined;
1624
+ }
1625
+ const blocker = 'Support V5 run is missing a validated next-action contract, so the manager cannot prove one safe primary action without external Codex monitoring.';
1626
+ return {
1627
+ key: 'support_next_action_contract',
1628
+ label: 'Support next-action contract',
1629
+ status: 'blocked',
1630
+ reason: blocker,
1631
+ evidenceRefs: [],
1632
+ recordedAt: isoNow(now),
1633
+ metadata: {
1634
+ required: true,
1635
+ missingContract: true,
1636
+ safeToAutoRun: false,
1637
+ canRunWithoutCodexMonitor: false,
1638
+ codexFallbackRequired: true,
1639
+ codexFallbackReason: 'missing_support_next_action_contract',
1640
+ blockers: [blocker],
1641
+ missingSafetyFlags: ['safeToAutoRun', 'canRunWithoutCodexMonitor', 'codexFallbackRequired']
1642
+ }
1643
+ };
1644
+ }
1645
+ const metadata = supportNextActionContractMetadata(contract);
1646
+ const blockers = cleanStringList(metadata.blockers, 40, 500);
1647
+ if (metadata.safeToAutoRun === false) {
1648
+ blockers.push('Next action contract marks the action unsafe for autonomous run.');
1649
+ }
1650
+ if (metadata.canRunWithoutCodexMonitor === false) {
1651
+ blockers.push('Next action requires Codex fallback/monitoring instead of manager-only execution.');
1652
+ }
1653
+ if (metadata.codexFallbackRequired === true) {
1654
+ blockers.push(metadata.codexFallbackReason || 'Next action contract requires Codex fallback.');
1655
+ }
1656
+ if (metadata.requiresHumanApproval === true) {
1657
+ blockers.push('Next action contract requires human approval.');
1658
+ }
1659
+ if (metadata.liveHotfixBlockedUntilCommit === true) {
1660
+ blockers.push('Live hotfix is blocked until the manager records and pushes the GitHub commit proof.');
1661
+ }
1662
+ const missingSafetyFlags = [
1663
+ metadata.safeToAutoRun === undefined ? 'safeToAutoRun' : '',
1664
+ metadata.canRunWithoutCodexMonitor === undefined ? 'canRunWithoutCodexMonitor' : '',
1665
+ metadata.codexFallbackRequired === undefined ? 'codexFallbackRequired' : ''
1666
+ ].filter(Boolean);
1667
+ const status: AIRunGateResult['status'] = blockers.length
1668
+ ? 'blocked'
1669
+ : missingSafetyFlags.length
1670
+ ? 'warn'
1671
+ : 'pass';
1672
+ const actionLabel = metadata.label || metadata.action || metadata.primaryCommand || 'support next action';
1673
+ return {
1674
+ key: 'support_next_action_contract',
1675
+ label: 'Support next-action contract',
1676
+ status,
1677
+ reason: blockers.length
1678
+ ? blockers.join(' ')
1679
+ : missingSafetyFlags.length
1680
+ ? `Support next-action contract is present for ${actionLabel}, but missing explicit safety flags: ${missingSafetyFlags.join(', ')}.`
1681
+ : `Support next-action contract allows ${actionLabel} without Codex monitoring.`,
1682
+ evidenceRefs: cleanStringList([
1683
+ ...metadata.successEvidence,
1684
+ ...metadata.preconditions,
1685
+ ...metadata.stopConditions
1686
+ ], 40, 500),
1687
+ recordedAt: isoNow(now || contract.createdAt || contract.created_at || contract.recordedAt || contract.recorded_at),
1688
+ metadata: {
1689
+ ...metadata,
1690
+ blockers,
1691
+ missingSafetyFlags
1692
+ }
1693
+ };
1694
+ }
1695
+
1696
+ function supportDiagnosisEvidencePackObject(ticket: Record<string, any>, job: Record<string, any>, evidence: Record<string, any>): Record<string, any> {
1697
+ const ticketAutomation = plainObject(ticket.automation);
1698
+ const ticketManager = plainObject(ticketAutomation.manager || ticket.manager);
1699
+ return evidenceObject(
1700
+ job.supportV5DiagnosisEvidencePack,
1701
+ job.support_v5_diagnosis_evidence_pack,
1702
+ job.diagnosisEvidencePack,
1703
+ job.diagnosis_evidence_pack,
1704
+ evidence.supportV5DiagnosisEvidencePack,
1705
+ evidence.support_v5_diagnosis_evidence_pack,
1706
+ evidence.diagnosisEvidencePack,
1707
+ evidence.diagnosis_evidence_pack,
1708
+ ticket.supportV5DiagnosisEvidencePack,
1709
+ ticket.support_v5_diagnosis_evidence_pack,
1710
+ ticket.diagnosisEvidencePack,
1711
+ ticket.diagnosis_evidence_pack,
1712
+ ticketManager.diagnosisEvidencePack,
1713
+ ticketManager.diagnosis_evidence_pack
1714
+ );
1715
+ }
1716
+
1717
+ function supportSimilarFixHintsObject(ticket: Record<string, any>, job: Record<string, any>, evidence: Record<string, any>): Record<string, any> {
1718
+ const ticketAutomation = plainObject(ticket.automation);
1719
+ const ticketManager = plainObject(ticketAutomation.manager || ticket.manager);
1720
+ return evidenceObject(
1721
+ job.supportRootCauseSimilarFixHints,
1722
+ job.support_root_cause_similar_fix_hints,
1723
+ job.similarFixHints,
1724
+ job.similar_fix_hints,
1725
+ evidence.supportRootCauseSimilarFixHints,
1726
+ evidence.support_root_cause_similar_fix_hints,
1727
+ evidence.similarFixHints,
1728
+ evidence.similar_fix_hints,
1729
+ ticket.supportRootCauseSimilarFixHints,
1730
+ ticket.support_root_cause_similar_fix_hints,
1731
+ ticketManager.rootCauseSimilarFixHints,
1732
+ ticketManager.root_cause_similar_fix_hints
1733
+ );
1734
+ }
1735
+
1736
+ function supportDiagnosisEvidencePackGate(pack: Record<string, any>, now?: Date | string): AIRunGateResult | undefined {
1737
+ if (!pack || !Object.keys(pack).length) {
1738
+ return undefined;
1739
+ }
1740
+ const statusText = cleanText(pack.status, 120);
1741
+ const diagnosisValid = pack.diagnosisValid === true || pack.diagnosis_valid === true;
1742
+ const validationBlockers = cleanStringList(pack.validationBlockers || pack.validation_blockers, 40, 500);
1743
+ const similarSelection = plainObject(pack.similarCaseSelection || pack.similar_case_selection);
1744
+ const rankedHints = asArray<any>(similarSelection.ranked);
1745
+ const gateStatus: AIRunGateResult['status'] = diagnosisValid || statusText === 'diagnosis_ready'
1746
+ ? 'pass'
1747
+ : statusText === 'blocked'
1748
+ ? 'blocked'
1749
+ : 'warn';
1750
+ return {
1751
+ key: 'support_diagnosis_evidence_pack',
1752
+ label: 'Support diagnosis evidence pack',
1753
+ status: gateStatus,
1754
+ reason: gateStatus === 'pass'
1755
+ ? 'Support diagnosis evidence pack has a valid root-cause diagnosis and advisory similar-fix context.'
1756
+ : validationBlockers.join('; ') || 'Support diagnosis evidence pack requires read-only root-cause evidence before repair.',
1757
+ evidenceRefs: cleanStringList(pack.requiredEvidence || pack.required_evidence, 30, 500),
1758
+ recordedAt: isoNow(now || pack.generatedAt || pack.generated_at),
1759
+ metadata: {
1760
+ packId: cleanText(pack.packId || pack.pack_id, 180),
1761
+ status: statusText,
1762
+ readOnly: pack.readOnly !== false && pack.read_only !== false,
1763
+ sourceEditsAllowed: pack.sourceEditsAllowed === true || pack.source_edits_allowed === true,
1764
+ rootCauseFirstRequired: pack.rootCauseFirstRequired !== false && pack.root_cause_first_required !== false,
1765
+ requiredOutputKey: cleanText(pack.requiredOutputKey || pack.required_output_key, 120),
1766
+ requiredFields: cleanStringList(pack.requiredFields || pack.required_fields, 20, 160),
1767
+ forbiddenActions: cleanStringList(pack.forbiddenActions || pack.forbidden_actions, 20, 500),
1768
+ diagnosisValid,
1769
+ diagnosisStatus: cleanText(pack.diagnosisStatus || pack.diagnosis_status, 120),
1770
+ validationBlockers,
1771
+ issueClassHint: cleanText(pack.issueClassHint || pack.issue_class_hint, 120),
1772
+ ownerFileHints: cleanStringList(pack.ownerFileHints || pack.owner_file_hints, 20, 500),
1773
+ similarHintCount: rankedHints.length,
1774
+ similarHintsAdvisoryOnly: true,
1775
+ issueClassProbeCount: asArray(pack.issueClassProbeCatalog || pack.issue_class_probe_catalog).length
1776
+ }
1777
+ };
1778
+ }
1779
+
1780
+ function firstNonEmptyText(values: any[], max = 1000): string | undefined {
1781
+ for (const value of values) {
1782
+ const normalized = idText(value, max);
1783
+ if (normalized) {
1784
+ return normalized;
1785
+ }
1786
+ }
1787
+ return undefined;
1788
+ }
1789
+
1790
+ function booleanFlag(value: any): boolean | undefined {
1791
+ if (value === true || value === false) {
1792
+ return value;
1793
+ }
1794
+ if (typeof value === 'string') {
1795
+ const normalized = value.trim().toLowerCase();
1796
+ if (/^(true|yes|1|passed|pass|ready|safe)$/.test(normalized)) {
1797
+ return true;
1798
+ }
1799
+ if (/^(false|no|0|failed|fail|blocked|unsafe)$/.test(normalized)) {
1800
+ return false;
1801
+ }
1802
+ }
1803
+ return undefined;
1804
+ }
1805
+
1806
+ function numberValue(value: any): number | undefined {
1807
+ const parsed = Number(value);
1808
+ return Number.isFinite(parsed) ? parsed : undefined;
1809
+ }
1810
+
1811
+ function buildAICoderQaEvidence(input: AICoderAIRunAdapterInput): Record<string, any> {
1812
+ const app = input.app || {};
1813
+ const job = input.job || {};
1814
+ const workflowMemory = plainObject(
1815
+ job.aiCoderV6WorkflowMemory
1816
+ || job.ai_coder_v6_workflow_memory
1817
+ || app.aiCoderV6WorkflowMemory
1818
+ || app.ai_coder_v6_workflow_memory
1819
+ );
1820
+ const evidence = mergeEvidenceRecords(
1821
+ job.qaEvidence,
1822
+ job.qa_evidence,
1823
+ app.qaEvidence,
1824
+ app.qa_evidence,
1825
+ input.qaEvidence
1826
+ );
1827
+ evidence.workflowQaRows = [
1828
+ ...asArray(workflowMemory.workflowQaRows),
1829
+ ...asArray(workflowMemory.workflow_qa_rows),
1830
+ ...asArray(job.workflowQaRows),
1831
+ ...asArray(job.workflow_qa_rows),
1832
+ ...asArray(app.workflowQaRows),
1833
+ ...asArray(app.workflow_qa_rows),
1834
+ ...asArray(evidence.workflowQaRows),
1835
+ ...asArray(evidence.workflow_qa_rows)
1836
+ ];
1837
+ evidence.businessAssertions = [
1838
+ ...asArray(job.aiQaBusinessAssertions),
1839
+ ...asArray(job.ai_qa_business_assertions),
1840
+ ...asArray(job.businessAssertions),
1841
+ ...asArray(job.business_assertions),
1842
+ ...asArray(app.businessAssertions),
1843
+ ...asArray(app.business_assertions),
1844
+ ...asArray(evidence.businessAssertions),
1845
+ ...asArray(evidence.business_assertions)
1846
+ ];
1847
+ evidence.routeProbes = [
1848
+ ...asArray(job.workflowProbes),
1849
+ ...asArray(job.workflow_probes),
1850
+ ...asArray(evidence.routeProbes),
1851
+ ...asArray(evidence.route_probes)
1852
+ ];
1853
+ evidence.artifactPaths = [
1854
+ ...asArray(workflowMemory.businessProofArtifacts),
1855
+ ...asArray(workflowMemory.business_proof_artifacts),
1856
+ ...asArray(job.artifactPaths),
1857
+ ...asArray(job.artifact_paths),
1858
+ ...asArray(app.artifactPaths),
1859
+ ...asArray(app.artifact_paths),
1860
+ ...asArray(evidence.artifactPaths),
1861
+ ...asArray(evidence.artifact_paths)
1862
+ ];
1863
+ evidence.journeyContract = evidence.journeyContract
1864
+ || evidence.journey_contract
1865
+ || job.journeyContract
1866
+ || job.journey_contract
1867
+ || app.journeyContract
1868
+ || app.journey_contract
1869
+ || workflowMemory.journeyContract
1870
+ || workflowMemory.journey_contract;
1871
+ evidence.journeyContractMarkdown = evidence.journeyContractMarkdown
1872
+ || evidence.journey_contract_markdown
1873
+ || job.journeyContractMarkdown
1874
+ || job.journey_contract_markdown
1875
+ || app.journeyContractMarkdown
1876
+ || app.journey_contract_markdown;
1877
+ evidence.journeyContractPath = firstNonEmptyText([
1878
+ evidence.journeyContractPath,
1879
+ evidence.journey_contract_path,
1880
+ workflowMemory.journeyContractPath,
1881
+ workflowMemory.journey_contract_path,
1882
+ job.journeyContractPath,
1883
+ job.journey_contract_path,
1884
+ app.journeyContractPath,
1885
+ app.journey_contract_path
1886
+ ], 500) || 'docs/APP_JOURNEY_CONTRACT.md';
1887
+ evidence.deployStatus = firstNonEmptyText([
1888
+ evidence.deployStatus,
1889
+ evidence.deploy_status,
1890
+ job.deployStatus,
1891
+ job.deploy_status,
1892
+ job.testDeployStatus,
1893
+ job.test_deploy_status,
1894
+ app.deployStatus,
1895
+ app.deploy_status
1896
+ ], 160);
1897
+ evidence.publishStatus = firstNonEmptyText([
1898
+ evidence.publishStatus,
1899
+ evidence.publish_status,
1900
+ evidence.pullRequestStatus,
1901
+ evidence.pull_request_status,
1902
+ job.publishStatus,
1903
+ job.publish_status,
1904
+ job.pullRequestStatus,
1905
+ job.pull_request_status,
1906
+ app.publishStatus,
1907
+ app.publish_status
1908
+ ], 160);
1909
+ evidence.sampleDataStatus = firstNonEmptyText([
1910
+ evidence.sampleDataStatus,
1911
+ evidence.sample_data_status,
1912
+ job.sampleDataStatus,
1913
+ job.sample_data_status,
1914
+ app.sampleDataStatus,
1915
+ app.sample_data_status
1916
+ ], 160) || statusFromBoolean(evidence.sampleDataReady ?? evidence.hasSampleData ?? job.sampleDataReady ?? app.sampleDataReady);
1917
+ return evidence;
1918
+ }
1919
+
1920
+ function normalizeAICoderWorkflowProofReadiness(
1921
+ value: Record<string, any>,
1922
+ now?: Date | string
1923
+ ): ResolveIOAICoderWorkflowProofReadiness | undefined {
1924
+ if (!value || !Object.keys(value).length) {
1925
+ return undefined;
1926
+ }
1927
+ const ready = value.ready === true;
1928
+ const status = cleanText(value.status, 120) || (ready ? 'ready' : 'blocked');
1929
+ return {
1930
+ ready,
1931
+ status: status as ResolveIOAICoderWorkflowProofReadiness['status'],
1932
+ reason: cleanText(value.reason || value.summary || status, 1200),
1933
+ blockers: cleanStringList(value.blockers, 40, 500),
1934
+ journeyContractValid: value.journeyContractValid === true || value.journey_contract_valid === true,
1935
+ primaryWorkflowId: cleanText(value.primaryWorkflowId || value.primary_workflow_id, 200),
1936
+ passedWorkflowRows: cleanStringList(value.passedWorkflowRows || value.passed_workflow_rows, 80, 300),
1937
+ missingWorkflowRows: cleanStringList(value.missingWorkflowRows || value.missing_workflow_rows, 80, 300),
1938
+ failedWorkflowRows: cleanStringList(value.failedWorkflowRows || value.failed_workflow_rows, 80, 300),
1939
+ passedBusinessAssertions: cleanStringList(value.passedBusinessAssertions || value.passed_business_assertions, 80, 500),
1940
+ routeOnly: value.routeOnly === true || value.route_only === true,
1941
+ scorecardOnly: value.scorecardOnly === true || value.scorecard_only === true,
1942
+ sampleDataReady: value.sampleDataReady === true || value.sample_data_ready === true,
1943
+ releaseBlockers: cleanStringList(value.releaseBlockers || value.release_blockers, 40, 500),
1944
+ artifactPaths: cleanStringList(value.artifactPaths || value.artifact_paths, 80, 500),
1945
+ workflowProofFingerprint: cleanText(value.workflowProofFingerprint || value.workflow_proof_fingerprint, 200),
1946
+ artifactFingerprint: cleanText(value.artifactFingerprint || value.artifact_fingerprint, 200),
1947
+ proofFreshness: cleanText(value.proofFreshness || value.proof_freshness, 80) as ResolveIOAICoderWorkflowProofReadiness['proofFreshness'],
1948
+ nextAction: cleanText(value.nextAction || value.next_action, 1000),
1949
+ evaluatedAt: isoNow(now || value.evaluatedAt || value.evaluated_at || value.recordedAt || value.recorded_at)
1950
+ };
1951
+ }
1952
+
1953
+ function aicoderJourneyContractValidationFromEvidence(evidence: Record<string, any>): ResolveIOAICoderJourneyContractValidationResult {
1954
+ const journeyInput = evidence.journeyContract !== undefined && evidence.journeyContract !== null
1955
+ ? evidence.journeyContract
1956
+ : evidence.journeyContractMarkdown;
1957
+ const hasJourneyInput = journeyInput !== undefined && journeyInput !== null && cleanText(journeyInput, 100).length > 0;
1958
+ if (!hasJourneyInput) {
1959
+ return {
1960
+ valid: false,
1961
+ contract: null,
1962
+ issues: [{
1963
+ code: 'missing_contract',
1964
+ path: cleanText(evidence.journeyContractPath, 500) || 'docs/APP_JOURNEY_CONTRACT.md',
1965
+ message: 'docs/APP_JOURNEY_CONTRACT.md with structured journey_contract JSON is required before AICoder build, workflow QA, wow UI, publish, or acceptance.',
1966
+ severity: 'error'
1967
+ }],
1968
+ workflowQaRows: [],
1969
+ primaryWorkflowId: ''
1970
+ };
1971
+ }
1972
+ return validateResolveIOAICoderJourneyContract(journeyInput, {
1973
+ requireMarkdownEnvelope: typeof journeyInput === 'string'
1974
+ });
1975
+ }
1976
+
1977
+ function aicoderJourneyContractGate(
1978
+ validation: ResolveIOAICoderJourneyContractValidationResult,
1979
+ evidence: Record<string, any>,
1980
+ now?: Date | string
1981
+ ): AIRunGateResult {
1982
+ const errorIssues = validation.issues.filter((issue) => issue.severity === 'error');
1983
+ const warningIssues = validation.issues.filter((issue) => issue.severity === 'warning');
1984
+ const status: AIRunGateResult['status'] = validation.valid
1985
+ ? (warningIssues.length ? 'warn' : 'pass')
1986
+ : 'blocked';
1987
+ const path = cleanText(evidence.journeyContractPath, 500) || 'docs/APP_JOURNEY_CONTRACT.md';
1988
+ return {
1989
+ key: 'aicoder_journey_contract',
1990
+ label: 'AICoder journey contract',
1991
+ status,
1992
+ reason: validation.valid
1993
+ ? (warningIssues.length
1994
+ ? `Journey contract is valid with warnings: ${warningIssues.map((issue) => issue.message).slice(0, 3).join(' | ')}`
1995
+ : 'Journey contract validates first/next/last workflow, data story, completion states, and QA assertions.')
1996
+ : (errorIssues.map((issue) => issue.message).slice(0, 5).join(' | ') || 'AICoder Journey Contract is missing or invalid.'),
1997
+ evidenceRefs: [path],
1998
+ recordedAt: isoNow(now),
1999
+ metadata: {
2000
+ path,
2001
+ valid: validation.valid,
2002
+ primaryWorkflowId: cleanText(validation.primaryWorkflowId, 200),
2003
+ workflowQaRowCount: validation.workflowQaRows.length,
2004
+ errorCount: errorIssues.length,
2005
+ warningCount: warningIssues.length,
2006
+ issueCodes: cleanStringList(validation.issues.map((issue) => issue.code), 40, 120),
2007
+ issues: validation.issues.slice(0, 20).map((issue) => ({
2008
+ code: cleanText(issue.code, 120),
2009
+ path: cleanText(issue.path, 500),
2010
+ severity: issue.severity,
2011
+ message: cleanText(issue.message, 800)
2012
+ })),
2013
+ blocksBuildUntilValid: !validation.valid,
2014
+ blocksWorkflowQaUntilValid: !validation.valid,
2015
+ blocksWowUiUntilWorkflowProof: !validation.valid,
2016
+ blocksPublishUntilWorkflowProof: !validation.valid,
2017
+ nextAction: validation.valid
2018
+ ? 'Generate workflow QA rows from journey_contract.qa_assertions and execute the north-star workflow.'
2019
+ : 'Generate or repair docs/APP_JOURNEY_CONTRACT.md before app build, wow UI, publish, or acceptance.'
2020
+ }
2021
+ };
2022
+ }
2023
+
2024
+ function aicoderWorkflowProofReadinessGate(
2025
+ readiness: ResolveIOAICoderWorkflowProofReadiness,
2026
+ now?: Date | string
2027
+ ): AIRunGateResult {
2028
+ const status: AIRunGateResult['status'] = readiness.ready
2029
+ ? 'pass'
2030
+ : readiness.status === 'release_blocked' || readiness.status === 'journey_invalid' || readiness.status === 'sample_data_missing'
2031
+ ? 'fail'
2032
+ : 'blocked';
2033
+ return {
2034
+ key: 'aicoder_workflow_proof_readiness',
2035
+ label: 'AICoder workflow proof readiness',
2036
+ status,
2037
+ reason: readiness.reason || readiness.blockers.join('; ') || readiness.status,
2038
+ evidenceRefs: readiness.artifactPaths,
2039
+ recordedAt: isoNow(now || readiness.evaluatedAt),
2040
+ metadata: {
2041
+ ready: readiness.ready,
2042
+ status: readiness.status,
2043
+ primaryWorkflowId: readiness.primaryWorkflowId,
2044
+ journeyContractValid: readiness.journeyContractValid,
2045
+ blockers: readiness.blockers,
2046
+ passedWorkflowRows: readiness.passedWorkflowRows,
2047
+ missingWorkflowRows: readiness.missingWorkflowRows,
2048
+ failedWorkflowRows: readiness.failedWorkflowRows,
2049
+ passedBusinessAssertions: readiness.passedBusinessAssertions,
2050
+ routeOnly: readiness.routeOnly,
2051
+ scorecardOnly: readiness.scorecardOnly,
2052
+ sampleDataReady: readiness.sampleDataReady,
2053
+ releaseBlockers: readiness.releaseBlockers,
2054
+ workflowProofFingerprint: readiness.workflowProofFingerprint,
2055
+ artifactFingerprint: readiness.artifactFingerprint,
2056
+ proofFreshness: readiness.proofFreshness,
2057
+ nextAction: readiness.nextAction
2058
+ }
2059
+ };
2060
+ }
2061
+
2062
+ function aicoderWorkflowProofCheckpointObject(app: Record<string, any>, job: Record<string, any>, evidence: Record<string, any>): Record<string, any> {
2063
+ const workflowMemory = plainObject(
2064
+ job.aiCoderV6WorkflowMemory
2065
+ || job.ai_coder_v6_workflow_memory
2066
+ || app.aiCoderV6WorkflowMemory
2067
+ || app.ai_coder_v6_workflow_memory
2068
+ || evidence.aiCoderV6WorkflowMemory
2069
+ || evidence.ai_coder_v6_workflow_memory
2070
+ );
2071
+ const continuationDecision = plainObject(
2072
+ job.aicoderV6ContinuationDecision
2073
+ || job.aiCoderV6ContinuationDecision
2074
+ || job.aicoder_v6_continuation_decision
2075
+ || job.ai_coder_v6_continuation_decision
2076
+ || app.aicoderV6ContinuationDecision
2077
+ || app.aiCoderV6ContinuationDecision
2078
+ || app.aicoder_v6_continuation_decision
2079
+ || evidence.aicoderV6ContinuationDecision
2080
+ || evidence.aiCoderV6ContinuationDecision
2081
+ || evidence.aicoder_v6_continuation_decision
2082
+ );
2083
+ return evidenceObject(
2084
+ continuationDecision.workflowProofCheckpoint,
2085
+ continuationDecision.workflow_proof_checkpoint,
2086
+ workflowMemory.workflowProofCheckpoint,
2087
+ workflowMemory.workflow_proof_checkpoint,
2088
+ job.workflowProofCheckpoint,
2089
+ job.workflow_proof_checkpoint,
2090
+ job.aicoderWorkflowProofCheckpoint,
2091
+ job.aicoder_workflow_proof_checkpoint,
2092
+ app.workflowProofCheckpoint,
2093
+ app.workflow_proof_checkpoint,
2094
+ evidence.workflowProofCheckpoint,
2095
+ evidence.workflow_proof_checkpoint,
2096
+ evidence.aicoderWorkflowProofCheckpoint,
2097
+ evidence.aicoder_workflow_proof_checkpoint
2098
+ );
2099
+ }
2100
+
2101
+ function aicoderWorkflowProofCheckpointGate(checkpoint: Record<string, any>, now?: Date | string): AIRunGateResult | undefined {
2102
+ if (!checkpoint || !Object.keys(checkpoint).length) {
2103
+ return undefined;
2104
+ }
2105
+ const blocksProductRepairUntilJourneyContract = booleanFlag(checkpoint.blocksProductRepairUntilJourneyContract ?? checkpoint.blocks_product_repair_until_journey_contract) === true;
2106
+ const blocksPublishUntilWorkflowProof = booleanFlag(checkpoint.blocksPublishUntilWorkflowProof ?? checkpoint.blocks_publish_until_workflow_proof) === true;
2107
+ const blocksPublishUntilReleaseGate = booleanFlag(checkpoint.blocksPublishUntilReleaseGate ?? checkpoint.blocks_publish_until_release_gate) === true;
2108
+ const blocksWowUiUntilWorkflowProof = booleanFlag(checkpoint.blocksWowUiUntilWorkflowProof ?? checkpoint.blocks_wow_ui_until_workflow_proof) === true;
2109
+ const statusText = cleanText(checkpoint.status, 120);
2110
+ const required = booleanFlag(checkpoint.required) !== false;
2111
+ const blockers = cleanStringList(checkpoint.blockers, 40, 500);
2112
+ if (blocksProductRepairUntilJourneyContract) {
2113
+ blockers.push('Product repair is blocked until the journey contract is valid.');
2114
+ }
2115
+ if (blocksPublishUntilWorkflowProof) {
2116
+ blockers.push('Publish is blocked until workflow business proof passes.');
2117
+ }
2118
+ if (blocksPublishUntilReleaseGate) {
2119
+ blockers.push('Publish is blocked until release gate evidence passes.');
2120
+ }
2121
+ if (blocksWowUiUntilWorkflowProof) {
2122
+ blockers.push('Wow UI polish is blocked until workflow proof exists.');
2123
+ }
2124
+ const gateStatus: AIRunGateResult['status'] = !required || statusText === 'ready_to_continue'
2125
+ ? 'pass'
2126
+ : blockers.length
2127
+ ? 'blocked'
2128
+ : 'warn';
2129
+ return {
2130
+ key: 'aicoder_workflow_proof_checkpoint',
2131
+ label: 'AICoder workflow proof checkpoint',
2132
+ status: gateStatus,
2133
+ reason: gateStatus === 'pass'
2134
+ ? 'AICoder workflow checkpoint allows the next action.'
2135
+ : (blockers.join(' ') || cleanText(checkpoint.nextAction || checkpoint.next_action || statusText, 1200) || 'AICoder workflow checkpoint requires more proof before continuing.'),
2136
+ evidenceRefs: cleanStringList([
2137
+ ...asArray(checkpoint.requiredEvidence || checkpoint.required_evidence),
2138
+ ...asArray(checkpoint.requiredResetEvidence || checkpoint.required_reset_evidence)
2139
+ ], 40, 500),
2140
+ recordedAt: isoNow(now || checkpoint.recordedAt || checkpoint.recorded_at || checkpoint.createdAt || checkpoint.created_at),
2141
+ metadata: {
2142
+ required,
2143
+ status: statusText,
2144
+ readinessStatus: cleanText(checkpoint.readinessStatus || checkpoint.readiness_status, 120),
2145
+ nextGate: cleanText(checkpoint.nextGate || checkpoint.next_gate, 120),
2146
+ nextAction: cleanText(checkpoint.nextAction || checkpoint.next_action, 1000),
2147
+ startingFailureClass: cleanText(checkpoint.startingFailureClass || checkpoint.starting_failure_class, 120),
2148
+ startingBlockerFingerprint: cleanText(checkpoint.startingBlockerFingerprint || checkpoint.starting_blocker_fingerprint, 200),
2149
+ startingEvidenceHash: cleanText(checkpoint.startingEvidenceHash || checkpoint.starting_evidence_hash, 200),
2150
+ workflowProofFingerprint: cleanText(checkpoint.workflowProofFingerprint || checkpoint.workflow_proof_fingerprint, 200),
2151
+ artifactFingerprint: cleanText(checkpoint.artifactFingerprint || checkpoint.artifact_fingerprint, 200),
2152
+ requiredEvidence: cleanStringList(checkpoint.requiredEvidence || checkpoint.required_evidence, 40, 500),
2153
+ requiredResetEvidence: cleanStringList(checkpoint.requiredResetEvidence || checkpoint.required_reset_evidence, 40, 500),
2154
+ successRequiresWorkflowBusinessProof: booleanFlag(checkpoint.successRequiresWorkflowBusinessProof ?? checkpoint.success_requires_workflow_business_proof) === true,
2155
+ blocksProductRepairUntilJourneyContract,
2156
+ blocksPublishUntilWorkflowProof,
2157
+ blocksPublishUntilReleaseGate,
2158
+ blocksWowUiUntilWorkflowProof,
2159
+ blockers
2160
+ }
2161
+ };
2162
+ }
2163
+
2164
+ function adapterStatusPassed(value: any): boolean {
2165
+ const normalized = cleanText(value, 120).toLowerCase();
2166
+ return !!normalized && /(pass|passed|success|succeeded|ok|done|complete|completed|ready|saved|published|deployed)/i.test(normalized);
2167
+ }
2168
+
2169
+ function applyAICoderWorkflowProofGate(
2170
+ qa: AIQaRun,
2171
+ readiness: ResolveIOAICoderWorkflowProofReadiness,
2172
+ now?: Date | string
2173
+ ): AIQaRun {
2174
+ if (
2175
+ readiness.ready
2176
+ || readiness.status === 'release_blocked'
2177
+ || qa.outcome === 'infra_failed'
2178
+ || qa.outcome === 'compile_failed'
2179
+ || qa.outcome === 'route_failed'
2180
+ || qa.outcome === 'business_assertion_failed'
2181
+ ) {
2182
+ return qa;
2183
+ }
2184
+ const gate = aicoderWorkflowProofReadinessGate(readiness, now);
2185
+ return {
2186
+ ...qa,
2187
+ outcome: qa.routeProbes.some((probe) => adapterStatusPassed(probe.status))
2188
+ ? 'route_only_pass'
2189
+ : 'incomplete',
2190
+ gateResults: [
2191
+ ...qa.gateResults.filter((existing) => !(existing.key === 'qa_business_assertion' && existing.status === 'pass')),
2192
+ gate
2193
+ ]
2194
+ };
2195
+ }
2196
+
2197
+ function assistantQualityObject(input: AssistantAIRunAdapterInput): Record<string, any> {
2198
+ const conversation = input.conversation || {};
2199
+ const latestAssistantMessage = [...asArray<Record<string, any>>(input.messages)]
2200
+ .reverse()
2201
+ .find((message) => /assistant/i.test(cleanText(message.role || message.authorRole || message.type, 80)));
2202
+ return plainObject(
2203
+ input.answerQuality
2204
+ || conversation.answerQuality
2205
+ || conversation.answer_quality
2206
+ || conversation.assistantAnswerQuality
2207
+ || conversation.assistant_answer_quality
2208
+ || latestAssistantMessage?.answerQuality
2209
+ || latestAssistantMessage?.answer_quality
2210
+ || latestAssistantMessage?.metadata?.answerQuality
2211
+ || latestAssistantMessage?.metadata?.answer_quality
2212
+ );
2213
+ }
2214
+
2215
+ function assistantConfidenceLevel(value: any): AssistantAnswerQualityDecision['confidenceLevel'] {
2216
+ const normalized = cleanText(value?.level || value?.confidenceLevel || value?.confidence_level || value, 80).toLowerCase();
2217
+ if (/^high$/.test(normalized)) {
2218
+ return 'high';
2219
+ }
2220
+ if (/^medium$/.test(normalized)) {
2221
+ return 'medium';
2222
+ }
2223
+ if (/^low$/.test(normalized)) {
2224
+ return 'low';
2225
+ }
2226
+ return 'unknown';
2227
+ }
2228
+
2229
+ function assistantQueryStatus(value: any): AssistantAnswerQualityDecision['queryStatus'] {
2230
+ const normalized = cleanText(value?.status || value?.queryStatus || value?.query_status || value, 120).toLowerCase().replace(/[\s-]+/g, '_');
2231
+ if (/^(ok|success|answered|data_found|data)$/.test(normalized)) {
2232
+ return 'ok';
2233
+ }
2234
+ if (/^(no_data|no_results|empty|not_found)$/.test(normalized)) {
2235
+ return 'no_data';
2236
+ }
2237
+ if (/^(query_error|mongo_error|error|failed)$/.test(normalized)) {
2238
+ return 'query_error';
2239
+ }
2240
+ if (/^(permission_error|permission_denied|forbidden|unauthorized)$/.test(normalized)) {
2241
+ return 'permission_error';
2242
+ }
2243
+ return 'unknown';
2244
+ }
2245
+
2246
+ function asObjectList(value: any): Record<string, any>[] {
2247
+ return asArray<Record<string, any>>(value).filter((entry) => entry && typeof entry === 'object' && !Array.isArray(entry));
2248
+ }
2249
+
2250
+ function assistantEvidenceRefs(quality: Record<string, any>): string[] {
2251
+ return cleanStringList([
2252
+ ...asArray(quality.evidenceRefs),
2253
+ ...asArray(quality.evidence_refs),
2254
+ ...asArray(quality.citationRefs),
2255
+ ...asArray(quality.citation_refs),
2256
+ ...asArray(quality.artifactPaths),
2257
+ ...asArray(quality.artifact_paths),
2258
+ ...asObjectList(quality.citations).map((citation) => citation.path || citation.url || citation.id || citation.ref),
2259
+ ...asObjectList(quality.evidence).map((entry) => entry.path || entry.url || entry.id || entry.ref)
2260
+ ], 80, 500);
2261
+ }
2262
+
2263
+ function assistantCitationRefs(quality: Record<string, any>): string[] {
2264
+ return cleanStringList([
2265
+ ...asArray(quality.citationRefs),
2266
+ ...asArray(quality.citation_refs),
2267
+ ...asObjectList(quality.citations).map((citation) => citation.path || citation.url || citation.id || citation.ref),
2268
+ ...asObjectList(quality.evidence).map((entry) => entry.citation || entry.ref || entry.path || entry.url)
2269
+ ], 80, 500);
2270
+ }
2271
+
2272
+ function assistantQueryExecutions(quality: Record<string, any>): Record<string, any>[] {
2273
+ const queryExecution = plainObject(quality.queryExecution || quality.query_execution);
2274
+ const toolResult = plainObject(quality.toolResult || quality.tool_result);
2275
+ return [
2276
+ ...asObjectList(quality.queryExecutions),
2277
+ ...asObjectList(quality.query_executions),
2278
+ ...asObjectList(quality.dataQueries),
2279
+ ...asObjectList(quality.data_queries),
2280
+ ...asObjectList(quality.toolResults),
2281
+ ...asObjectList(quality.tool_results),
2282
+ ...asObjectList(quality.mongoReads),
2283
+ ...asObjectList(quality.mongo_reads),
2284
+ ...asObjectList(quality.mongoAggregations),
2285
+ ...asObjectList(quality.mongo_aggregations),
2286
+ ...(Object.keys(queryExecution).length ? [queryExecution] : []),
2287
+ ...(Object.keys(toolResult).length ? [toolResult] : [])
2288
+ ].filter((entry) => Object.keys(entry).length);
2289
+ }
2290
+
2291
+ function assistantQueryEvidenceRefs(quality: Record<string, any>, executions = assistantQueryExecutions(quality)): string[] {
2292
+ return cleanStringList([
2293
+ ...asArray(quality.queryEvidenceRefs),
2294
+ ...asArray(quality.query_evidence_refs),
2295
+ ...asArray(quality.toolResultRefs),
2296
+ ...asArray(quality.tool_result_refs),
2297
+ ...asArray(quality.queryResultRefs),
2298
+ ...asArray(quality.query_result_refs),
2299
+ ...executions.map((execution) => {
2300
+ return execution.id
2301
+ || execution._id
2302
+ || execution.ref
2303
+ || execution.toolResultId
2304
+ || execution.tool_result_id
2305
+ || execution.toolCallId
2306
+ || execution.tool_call_id
2307
+ || execution.artifactPath
2308
+ || execution.artifact_path
2309
+ || execution.path
2310
+ || execution.url;
2311
+ })
2312
+ ], 80, 500);
2313
+ }
2314
+
2315
+ function assistantNextActions(quality: Record<string, any>): string[] {
2316
+ return cleanStringList([
2317
+ ...asArray(quality.nextActions),
2318
+ ...asArray(quality.next_actions),
2319
+ ...asArray(quality.recommendedActions),
2320
+ ...asArray(quality.recommended_actions)
2321
+ ], 20, 500);
2322
+ }
2323
+
2324
+ function normalizedIsoDay(value: any): string {
2325
+ if (!value) {
2326
+ return '';
2327
+ }
2328
+ const date = value instanceof Date ? value : new Date(value);
2329
+ if (!Number.isFinite(date.getTime())) {
2330
+ return cleanText(value, 40).slice(0, 10);
2331
+ }
2332
+ return date.toISOString().slice(0, 10);
2333
+ }
2334
+
2335
+ function assistantDateWindowObject(quality: Record<string, any>): Record<string, any> {
2336
+ const dateWindow = plainObject(quality.dateWindow || quality.date_window);
2337
+ const nestedWindow = plainObject(quality.verification?.metrics?.window || quality.metrics?.window);
2338
+ return Object.keys(dateWindow).length ? dateWindow : nestedWindow;
2339
+ }
2340
+
2341
+ function collectMongoProjectionObjects(value: any, result: Record<string, any>[] = []): Record<string, any>[] {
2342
+ if (!value || typeof value !== 'object') {
2343
+ return result;
2344
+ }
2345
+ if (Array.isArray(value)) {
2346
+ for (const entry of value) {
2347
+ collectMongoProjectionObjects(entry, result);
2348
+ }
2349
+ return result;
2350
+ }
2351
+ if (value.projection && typeof value.projection === 'object' && !Array.isArray(value.projection)) {
2352
+ result.push(value.projection);
2353
+ }
2354
+ if (value.options && typeof value.options === 'object' && !Array.isArray(value.options) && value.options.projection) {
2355
+ collectMongoProjectionObjects({ projection: value.options.projection }, result);
2356
+ }
2357
+ if (value.query && typeof value.query === 'object') {
2358
+ collectMongoProjectionObjects(value.query, result);
2359
+ }
2360
+ if (value.mongoQuery && typeof value.mongoQuery === 'object') {
2361
+ collectMongoProjectionObjects(value.mongoQuery, result);
2362
+ }
2363
+ if (value.mongo_query && typeof value.mongo_query === 'object') {
2364
+ collectMongoProjectionObjects(value.mongo_query, result);
2365
+ }
2366
+ return result;
2367
+ }
2368
+
2369
+ function projectionHasPositionalOperator(projection: Record<string, any>): boolean {
2370
+ return Object.keys(projection || {}).some((key) => /\.(?:\$|\$\[)|\.\$$/.test(key));
2371
+ }
2372
+
2373
+ function assistantQueryShapeLegal(quality: Record<string, any>): boolean {
2374
+ if (quality.legalQueryShape === true || quality.legal_query_shape === true) {
2375
+ return true;
2376
+ }
2377
+ if (quality.legalQueryShape === false || quality.legal_query_shape === false || quality.illegalQueryShape === true || quality.illegal_query_shape === true) {
2378
+ return false;
2379
+ }
2380
+ const queryShape = plainObject(quality.queryShape || quality.query_shape);
2381
+ if (queryShape.legal === true || queryShape.status === 'pass') {
2382
+ return true;
2383
+ }
2384
+ if (queryShape.legal === false || queryShape.status === 'fail') {
2385
+ return false;
2386
+ }
2387
+ const projections = collectMongoProjectionObjects(quality);
2388
+ if (projections.some(projectionHasPositionalOperator)) {
2389
+ return false;
2390
+ }
2391
+ return true;
2392
+ }
2393
+
2394
+ function assistantCorrectnessFailed(checks: Array<Record<string, any>>): string[] {
2395
+ return checks
2396
+ .filter((check) => {
2397
+ const status = cleanText(check.status || (check.passed === true ? 'pass' : check.passed === false ? 'fail' : ''), 80).toLowerCase();
2398
+ return /^(fail|failed|blocked|incorrect|wrong|error)$/.test(status);
2399
+ })
2400
+ .map((check) => cleanText(check.assertion || check.name || check.case || check.reason || 'assistant correctness check failed', 300))
2401
+ .slice(0, 20);
2402
+ }
2403
+
2404
+ export function evaluateAssistantAnswerQuality(input: AssistantAnswerQualityInput = {}): AssistantAnswerQualityDecision {
2405
+ const quality = plainObject(input.answerQuality);
2406
+ const now = isoNow(input.now);
2407
+ const correctnessChecks = asArray<Record<string, any>>(input.correctnessChecks);
2408
+ const failedChecks = assistantCorrectnessFailed(correctnessChecks);
2409
+ const queryStatus = assistantQueryStatus(quality.queryStatus || quality.query_status || quality.queryResult || quality.query_result);
2410
+ const confidenceLevel = assistantConfidenceLevel(quality.confidence || quality.confidenceLevel || quality.confidence_level);
2411
+ const evidenceRefs = assistantEvidenceRefs(quality);
2412
+ const citationRefs = assistantCitationRefs(quality);
2413
+ const queryExecutions = assistantQueryExecutions(quality);
2414
+ const queryEvidenceRefs = assistantQueryEvidenceRefs(quality, queryExecutions);
2415
+ const nextActions = assistantNextActions(quality);
2416
+ const requiresCurrentDate = quality.requiresCurrentDate === true || quality.requires_current_date === true;
2417
+ const dateWindowRequired = quality.requiresDateWindow === true
2418
+ || quality.requires_date_window === true
2419
+ || quality.dateWindowRequired === true
2420
+ || quality.date_window_required === true;
2421
+ const requiresCitations = quality.requiresCitations !== false && quality.requires_citations !== false;
2422
+ const requiresNextAction = quality.requiresNextAction !== false && quality.requires_next_action !== false;
2423
+ const dateWindow = assistantDateWindowObject(quality);
2424
+ const dateWindowStart = cleanText(dateWindow.startDate || dateWindow.start_date || dateWindow.start, 80);
2425
+ const dateWindowEnd = cleanText(dateWindow.endDate || dateWindow.end_date || dateWindow.end, 80);
2426
+ const dateWindowPresent = !!dateWindowStart && !!dateWindowEnd;
2427
+ const dateBasis = cleanText(
2428
+ quality.currentDateUsed
2429
+ || quality.current_date_used
2430
+ || quality.dateBasis
2431
+ || quality.date_basis
2432
+ || quality.dateWindow?.basisDate
2433
+ || quality.date_window?.basis_date,
2434
+ 80
2435
+ );
2436
+ const expectedCurrentDate = cleanText(quality.expectedCurrentDate || quality.expected_current_date, 80)
2437
+ || normalizedIsoDay(input.now);
2438
+ const noDataConfirmed = quality.noDataConfirmed === true
2439
+ || quality.no_data_confirmed === true
2440
+ || quality.confirmedNoData === true
2441
+ || quality.confirmed_no_data === true;
2442
+ const legalQueryShape = assistantQueryShapeLegal(quality);
2443
+ const queryEvidenceRequired = quality.requiresQueryEvidence !== false
2444
+ && quality.requires_query_evidence !== false
2445
+ && (queryStatus === 'ok' || queryStatus === 'no_data');
2446
+ const queryEvidencePresent = queryExecutions.length > 0 || queryEvidenceRefs.length > 0;
2447
+ const blockers: string[] = [];
2448
+ let status: AssistantAnswerQualityStatus = 'ready';
2449
+ let reason = 'Assistant answer has structured data evidence, legal query shape, citations, confidence, and next action.';
2450
+ if (!Object.keys(quality).length) {
2451
+ status = 'missing_data_source';
2452
+ blockers.push('Assistant answerQuality evidence is missing.');
2453
+ reason = 'Assistant answer cannot be accepted without structured answerQuality evidence.';
2454
+ }
2455
+ else if (failedChecks.length) {
2456
+ status = 'incorrect';
2457
+ blockers.push(...failedChecks);
2458
+ reason = 'Assistant correctness checks failed.';
2459
+ }
2460
+ else if (queryStatus === 'permission_error') {
2461
+ status = 'permission_error';
2462
+ blockers.push('Assistant data query was blocked by permission or authorization.');
2463
+ reason = 'Assistant must report permission-error state and avoid claiming no data or success.';
2464
+ }
2465
+ else if (queryStatus === 'query_error') {
2466
+ status = 'query_error';
2467
+ blockers.push('Assistant data query failed.');
2468
+ reason = 'Assistant must report query-error state and avoid claiming no data or success.';
2469
+ }
2470
+ else if (!legalQueryShape) {
2471
+ status = 'illegal_query_shape';
2472
+ blockers.push('Assistant query shape is illegal or uses an unsafe Mongo projection shape.');
2473
+ reason = 'Assistant must use legal Mongo query/projection shapes before answering.';
2474
+ }
2475
+ else if (requiresCurrentDate && (!dateBasis || normalizedIsoDay(dateBasis) !== normalizedIsoDay(expectedCurrentDate))) {
2476
+ status = 'date_incorrect';
2477
+ blockers.push(`Expected current date ${normalizedIsoDay(expectedCurrentDate) || expectedCurrentDate}, got ${dateBasis || 'missing date basis'}.`);
2478
+ reason = 'Assistant answer did not prove it used the actual current date.';
2479
+ }
2480
+ else if (dateWindowRequired && !dateWindowPresent) {
2481
+ status = 'missing_date_window';
2482
+ blockers.push('Assistant dated answer did not record concrete date-window start and end evidence.');
2483
+ reason = 'Assistant dated answers must record the exact query date window used.';
2484
+ }
2485
+ else if (queryStatus === 'unknown') {
2486
+ status = 'missing_data_source';
2487
+ blockers.push('Assistant answer did not record queryStatus ok/no_data/query_error/permission_error.');
2488
+ reason = 'Assistant answer must classify data access result before acceptance.';
2489
+ }
2490
+ else if (queryStatus === 'no_data' && !noDataConfirmed) {
2491
+ status = 'no_data_unverified';
2492
+ blockers.push('No-data answer was not confirmed by a probe or query evidence.');
2493
+ reason = 'Assistant no-data answers require explicit noDataConfirmed evidence.';
2494
+ }
2495
+ else if (queryEvidenceRequired && !queryEvidencePresent) {
2496
+ status = 'missing_query_proof';
2497
+ blockers.push('Assistant data answer did not record a structured query execution, tool result, or query evidence reference.');
2498
+ reason = 'Assistant answers from system data require structured query/tool execution proof before acceptance.';
2499
+ }
2500
+ else if (requiresCitations && !citationRefs.length && !evidenceRefs.length) {
2501
+ status = 'missing_citations';
2502
+ blockers.push('Assistant answer has no cited evidence or artifact reference.');
2503
+ reason = 'Assistant answer must cite system data evidence.';
2504
+ }
2505
+ else if (confidenceLevel === 'low' || confidenceLevel === 'unknown') {
2506
+ status = 'low_confidence';
2507
+ blockers.push('Assistant answer confidence is low or unknown.');
2508
+ reason = 'Assistant answer requires recorded medium/high confidence.';
2509
+ }
2510
+ else if (requiresNextAction && !nextActions.length) {
2511
+ status = 'missing_next_action';
2512
+ blockers.push('Assistant answer did not record next actions.');
2513
+ reason = 'Assistant answer must provide a next action or explicit no-action state.';
2514
+ }
2515
+ return {
2516
+ ready: status === 'ready',
2517
+ status,
2518
+ reason,
2519
+ blockers,
2520
+ queryStatus,
2521
+ confidenceLevel,
2522
+ dateBasis: dateBasis || undefined,
2523
+ expectedCurrentDate: expectedCurrentDate || undefined,
2524
+ dateWindowRequired,
2525
+ dateWindowPresent,
2526
+ dateWindow: dateWindowPresent ? {
2527
+ startDate: dateWindowStart,
2528
+ endDate: dateWindowEnd,
2529
+ mode: cleanText(dateWindow.mode || dateWindow.type, 80) || undefined,
2530
+ months: typeof dateWindow.months === 'number' ? dateWindow.months : undefined
2531
+ } : undefined,
2532
+ noDataConfirmed,
2533
+ legalQueryShape,
2534
+ citationRefs,
2535
+ queryEvidenceRequired,
2536
+ queryEvidencePresent,
2537
+ queryEvidenceRefs,
2538
+ queryExecutionCount: queryExecutions.length,
2539
+ nextActions,
2540
+ evidenceRefs,
2541
+ failedChecks,
2542
+ recordedAt: now
2543
+ };
2544
+ }
2545
+
2546
+ function assistantAnswerQualityGate(
2547
+ decision: AssistantAnswerQualityDecision,
2548
+ now?: Date | string
2549
+ ): AIRunGateResult {
2550
+ const status: AIRunGateResult['status'] = decision.ready
2551
+ ? 'pass'
2552
+ : /incorrect|date_incorrect|missing_date_window|illegal_query_shape/.test(decision.status)
2553
+ ? 'fail'
2554
+ : 'blocked';
2555
+ return {
2556
+ key: 'assistant_answer_quality',
2557
+ label: 'Assistant answer quality',
2558
+ status,
2559
+ reason: decision.reason,
2560
+ evidenceRefs: decision.evidenceRefs.length ? decision.evidenceRefs : decision.citationRefs,
2561
+ recordedAt: isoNow(now || decision.recordedAt),
2562
+ metadata: {
2563
+ ready: decision.ready,
2564
+ status: decision.status,
2565
+ queryStatus: decision.queryStatus,
2566
+ confidenceLevel: decision.confidenceLevel,
2567
+ dateBasis: decision.dateBasis,
2568
+ expectedCurrentDate: decision.expectedCurrentDate,
2569
+ dateWindowRequired: decision.dateWindowRequired,
2570
+ dateWindowPresent: decision.dateWindowPresent,
2571
+ dateWindow: decision.dateWindow,
2572
+ noDataConfirmed: decision.noDataConfirmed,
2573
+ legalQueryShape: decision.legalQueryShape,
2574
+ citationRefs: decision.citationRefs,
2575
+ queryEvidenceRequired: decision.queryEvidenceRequired,
2576
+ queryEvidencePresent: decision.queryEvidencePresent,
2577
+ queryEvidenceRefs: decision.queryEvidenceRefs,
2578
+ queryExecutionCount: decision.queryExecutionCount,
2579
+ nextActions: decision.nextActions,
2580
+ blockers: decision.blockers,
2581
+ failedChecks: decision.failedChecks
2582
+ }
2583
+ };
2584
+ }
2585
+
2586
+ function applyAssistantAnswerQualityGate(
2587
+ qa: AIQaRun,
2588
+ decision: AssistantAnswerQualityDecision,
2589
+ now?: Date | string
2590
+ ): AIQaRun {
2591
+ const gate = assistantAnswerQualityGate(decision, now);
2592
+ if (decision.ready) {
2593
+ return {
2594
+ ...qa,
2595
+ gateResults: [...qa.gateResults, gate]
2596
+ };
2597
+ }
2598
+ return {
2599
+ ...qa,
2600
+ outcome: /incorrect|date_incorrect|missing_date_window|illegal_query_shape/.test(decision.status)
2601
+ ? 'business_assertion_failed'
2602
+ : 'incomplete',
2603
+ gateResults: [
2604
+ ...qa.gateResults.filter((existing) => !(existing.key === 'qa_business_assertion' && existing.status === 'pass')),
2605
+ gate
2606
+ ]
2607
+ };
2608
+ }
2609
+
2610
+ export function buildSupportAIRunFromEvidence(input: SupportAIRunAdapterInput): AIRun {
2611
+ const ticket = input.ticket || {};
2612
+ const job = input.job || {};
2613
+ const evidence = buildSupportQaEvidence(input);
2614
+ const events: AIRunEvent[] = [];
2615
+ const gates: AIRunGateResult[] = [];
2616
+ const sourceIds: Record<string, string> = {};
2617
+ addSourceId(sourceIds, 'ticketId', ticket._id || ticket.id || job.ticketId || job.supportTicketId);
2618
+ addSourceId(sourceIds, 'ticketNumber', ticket.ticketNumber || ticket.number || job.ticketNumber);
2619
+ addSourceId(sourceIds, 'jobId', job._id || job.id || job.jobId);
2620
+ addSourceId(sourceIds, 'buildPlanId', job.buildPlanId || ticket.buildPlanId);
2621
+
2622
+ for (const event of asArray<Record<string, any>>(input.taskEvents)) {
2623
+ pushEvent(events, {
2624
+ type: event.type === 'human_intervention' ? 'human_intervention' : 'log',
2625
+ category: cleanText(event.category || event.phase || event.type, 160),
2626
+ message: cleanText(event.message || event.summary || event.text || event.title, 1800),
2627
+ recordedAt: eventDate(event),
2628
+ metadata: {
2629
+ status: event.status,
2630
+ phase: event.phase,
2631
+ taskId: event.taskId || event._id || event.id
2632
+ }
2633
+ });
2634
+ }
2635
+ for (const aiJob of asArray<Record<string, any>>(input.aiJobs)) {
2636
+ pushEvent(events, {
2637
+ type: aiJob.model ? 'model_call' : 'log',
2638
+ category: cleanText(aiJob.category || aiJob.phase || aiJob.runner, 160),
2639
+ message: cleanText(aiJob.summary || aiJob.promptSummary || aiJob.status || aiJob.phase, 1800),
2640
+ recordedAt: eventDate(aiJob),
2641
+ metadata: {
2642
+ id: aiJob._id || aiJob.id,
2643
+ model: aiJob.model,
2644
+ status: aiJob.status,
2645
+ phase: aiJob.phase
2646
+ }
2647
+ });
2648
+ }
2649
+ collectCommitEvents(asArray<Record<string, any>>(input.commits), events);
2650
+ collectHotfixEvidenceEvents([job, ticket, evidence, input.qaEvidence], events, gates, input.now);
2651
+ const noBlindLoopGate = supportManagerNoBlindLoopGate(job, evidence, input.now);
2652
+ if (noBlindLoopGate) {
2653
+ gates.push(noBlindLoopGate);
2654
+ pushEvent(events, {
2655
+ type: 'log',
2656
+ category: 'manager_no_blind_loop_policy',
2657
+ message: noBlindLoopGate.reason,
2658
+ artifactPaths: noBlindLoopGate.evidenceRefs,
2659
+ recordedAt: noBlindLoopGate.recordedAt,
2660
+ metadata: noBlindLoopGate.metadata
2661
+ });
2662
+ }
2663
+ const businessProofReadiness = plainObject(evidence.businessProofReadiness || evidence.business_proof_readiness);
2664
+ const businessProofReadinessGate = supportBusinessProofReadinessGate(businessProofReadiness, input.now);
2665
+ if (businessProofReadinessGate) {
2666
+ gates.push(businessProofReadinessGate);
2667
+ pushEvent(events, {
2668
+ type: 'qa_business_assertion',
2669
+ category: 'support_business_proof_readiness',
2670
+ message: businessProofReadinessGate.reason,
2671
+ artifactPaths: businessProofReadinessGate.evidenceRefs,
2672
+ recordedAt: businessProofReadinessGate.recordedAt,
2673
+ metadata: businessProofReadinessGate.metadata
2674
+ });
2675
+ }
2676
+ const customerReplyPolicy = plainObject(evidence.customerReplyPolicy || evidence.customer_reply_policy);
2677
+ const customerReplyPolicyGate = supportCustomerReplyPolicyGate(customerReplyPolicy, input.now);
2678
+ if (customerReplyPolicyGate) {
2679
+ gates.push(customerReplyPolicyGate);
2680
+ pushEvent(events, {
2681
+ type: 'human_intervention',
2682
+ category: 'support_customer_reply_policy',
2683
+ message: customerReplyPolicyGate.reason,
2684
+ artifactPaths: customerReplyPolicyGate.evidenceRefs,
2685
+ recordedAt: customerReplyPolicyGate.recordedAt,
2686
+ metadata: customerReplyPolicyGate.metadata
2687
+ });
2688
+ }
2689
+ const nextActionContract = supportNextActionContractObject(ticket, job, evidence);
2690
+ const nextActionContractRequired = supportNextActionContractRequired(ticket, job, evidence);
2691
+ const nextActionContractGate = supportNextActionContractGate(nextActionContract, input.now, nextActionContractRequired);
2692
+ if (nextActionContractGate) {
2693
+ gates.push(nextActionContractGate);
2694
+ pushEvent(events, {
2695
+ type: 'log',
2696
+ category: 'support_next_action_contract',
2697
+ message: nextActionContractGate.reason,
2698
+ artifactPaths: nextActionContractGate.evidenceRefs,
2699
+ recordedAt: nextActionContractGate.recordedAt,
2700
+ metadata: nextActionContractGate.metadata
2701
+ });
2702
+ }
2703
+ const ticketAutomation = plainObject(ticket.automation);
2704
+ const ticketManagerState = plainObject(ticketAutomation.manager || ticket.manager);
2705
+ const rootCauseEntryContract = evidenceObject(
2706
+ job.supportRootCauseEntryContract,
2707
+ job.support_root_cause_entry_contract,
2708
+ job.rootCauseEntryContract,
2709
+ ticket.supportRootCauseEntryContract,
2710
+ ticket.rootCauseEntryContract,
2711
+ ticketManagerState.root_cause_entry_contract,
2712
+ ticketManagerState.rootCauseEntryContract,
2713
+ evidence.rootCauseEntryContract,
2714
+ evidence.root_cause_entry_contract
2715
+ );
2716
+ const rootCauseEntryContractGate = supportRootCauseEntryContractGate(rootCauseEntryContract, input.now);
2717
+ if (rootCauseEntryContractGate) {
2718
+ gates.push(rootCauseEntryContractGate);
2719
+ pushEvent(events, {
2720
+ type: 'log',
2721
+ category: 'support_root_cause_entry_contract',
2722
+ message: rootCauseEntryContractGate.reason,
2723
+ artifactPaths: rootCauseEntryContractGate.evidenceRefs,
2724
+ recordedAt: rootCauseEntryContractGate.recordedAt,
2725
+ metadata: rootCauseEntryContractGate.metadata
2726
+ });
2727
+ }
2728
+ const cost = collectUsageEvents(asArray<Record<string, any>>(input.usageLedger), events);
2729
+
2730
+ const diagnosisGate = evidenceObject(
2731
+ job.supportV5DiagnosisGate,
2732
+ job.support_v5_diagnosis_gate,
2733
+ job.diagnosisGate,
2734
+ ticket.supportV5DiagnosisGate,
2735
+ ticket.diagnosisGate,
2736
+ evidence.diagnosisGate,
2737
+ evidence.supportV5DiagnosisGate
2738
+ );
2739
+ const diagnosisEvidencePackSource = supportDiagnosisEvidencePackObject(ticket, job, evidence);
2740
+ const similarFixHints = supportSimilarFixHintsObject(ticket, job, evidence);
2741
+ const diagnosisEvidencePack = Object.keys(diagnosisEvidencePackSource).length
2742
+ ? diagnosisEvidencePackSource
2743
+ : (Object.keys(diagnosisGate).length || Object.keys(similarFixHints).length)
2744
+ ? buildResolveIOSupportDiagnosisEvidencePack({
2745
+ diagnosisGate,
2746
+ similarCaseHints: similarFixHints,
2747
+ issueClass: diagnosisGate.issue_class || diagnosisGate.issueClass,
2748
+ ownerFiles: diagnosisGate.owner_files || diagnosisGate.ownerFiles,
2749
+ text: [
2750
+ ticket.title,
2751
+ ticket.subject,
2752
+ ticket.summary,
2753
+ job.title,
2754
+ job.summary
2755
+ ].filter(Boolean).join(' '),
2756
+ now: input.now
2757
+ }) as Record<string, any>
2758
+ : {};
2759
+ const diagnosisEvidencePackGate = supportDiagnosisEvidencePackGate(diagnosisEvidencePack, input.now);
2760
+ if (diagnosisEvidencePackGate) {
2761
+ gates.push(diagnosisEvidencePackGate);
2762
+ pushEvent(events, {
2763
+ type: 'log',
2764
+ category: 'support_diagnosis_evidence_pack',
2765
+ message: diagnosisEvidencePackGate.reason,
2766
+ artifactPaths: diagnosisEvidencePackGate.evidenceRefs,
2767
+ recordedAt: diagnosisEvidencePackGate.recordedAt,
2768
+ metadata: diagnosisEvidencePackGate.metadata
2769
+ });
2770
+ }
2771
+ const productRepairEvidence = supportProductRepairEvidence(input, evidence);
2772
+ const diagnosisBeforeRepairGate = supportDiagnosisBeforeRepairGate(diagnosisGate, productRepairEvidence, input.now);
2773
+ if (diagnosisBeforeRepairGate) {
2774
+ gates.push(diagnosisBeforeRepairGate);
2775
+ pushEvent(events, {
2776
+ type: 'log',
2777
+ category: 'support_diagnosis_before_repair',
2778
+ message: diagnosisBeforeRepairGate.reason,
2779
+ artifactPaths: diagnosisBeforeRepairGate.evidenceRefs,
2780
+ recordedAt: diagnosisBeforeRepairGate.recordedAt,
2781
+ metadata: diagnosisBeforeRepairGate.metadata
2782
+ });
2783
+ }
2784
+ const qa = applySupportDiagnosisProofGate(buildQaFromEvidence(evidence, input.now), diagnosisGate, input.now);
2785
+ const diagnosisOwnerFiles = asArray<string>(diagnosisGate.owner_files || diagnosisGate.ownerFiles);
2786
+ const diagnosisIssueClass = cleanText(diagnosisGate.issue_class || diagnosisGate.issueClass, 120);
2787
+ if (Object.keys(diagnosisGate).length) {
2788
+ pushEvent(events, {
2789
+ type: 'log',
2790
+ category: 'diagnosis_gate',
2791
+ message: cleanText(diagnosisGate.accepted_hypothesis?.statement || diagnosisGate.acceptedHypothesis?.statement || diagnosisGate.summary || 'Support diagnosis gate recorded.', 1800),
2792
+ metadata: {
2793
+ status: diagnosisGate.status,
2794
+ issueClass: diagnosisIssueClass,
2795
+ ownerFiles: diagnosisOwnerFiles
2796
+ }
2797
+ });
2798
+ }
2799
+ return buildAIRun({
2800
+ source: 'support_ticket',
2801
+ sourceIds,
2802
+ title: firstText(ticket, ['title', 'subject', 'summary']) || firstText(job, ['title', 'summary']),
2803
+ status: firstText(job, ['status', 'state']) || firstText(ticket, ['status', 'state']),
2804
+ phase: firstText(job, ['phase', 'runnerPhase']),
2805
+ startedAt: dateValue(job, ['startedAt', 'createdAt']) || dateValue(ticket, ['createdAt']),
2806
+ completedAt: dateValue(job, ['completedAt', 'finishedAt', 'updatedAt']),
2807
+ events,
2808
+ gates,
2809
+ qa,
2810
+ cost,
2811
+ scorecardPassed: evidence.scorecardPassed === true || evidence.scorecardStatus === 'pass',
2812
+ deployStatus: evidence.deployStatus,
2813
+ publishStatus: evidence.publishStatus,
2814
+ sampleDataStatus: evidence.sampleDataStatus,
2815
+ manualHandoff: evidence.manualHandoff === true || job.manualHandoff === true,
2816
+ stopped: evidence.stopped === true || job.stopped === true,
2817
+ rejected: evidence.rejected === true,
2818
+ explicitAccepted: evidence.explicitAccepted === true || ticket.accepted === true,
2819
+ terminal: /complete|closed|resolved|done|failed|rejected/i.test(`${job.status || ''} ${ticket.status || ''}`),
2820
+ now: input.now,
2821
+ metadata: {
2822
+ versionCount: asArray(input.versions).length,
2823
+ buildPlanCount: asArray(input.buildPlans).length,
2824
+ managerNoBlindLoopPolicy: managerNoBlindLoopMetadata(noBlindLoopGate),
2825
+ diagnosisBeforeRepair: diagnosisBeforeRepairGate ? {
2826
+ status: diagnosisBeforeRepairGate.status,
2827
+ diagnosisValid: diagnosisBeforeRepairGate.metadata?.diagnosisValid === true,
2828
+ diagnosisStatus: cleanText(diagnosisBeforeRepairGate.metadata?.diagnosisStatus, 120),
2829
+ activityCount: Number(diagnosisBeforeRepairGate.metadata?.activityCount || 0),
2830
+ changedFiles: cleanStringList(diagnosisBeforeRepairGate.metadata?.changedFiles, 40, 500),
2831
+ allowedDispatchAction: cleanText(diagnosisBeforeRepairGate.metadata?.allowedDispatchAction, 160),
2832
+ productRepairAllowed: diagnosisBeforeRepairGate.metadata?.productRepairAllowed === true,
2833
+ blockers: cleanStringList(diagnosisBeforeRepairGate.metadata?.blockers, 20, 500)
2834
+ } : undefined,
2835
+ rootCauseEntryContract: Object.keys(rootCauseEntryContract).length ? {
2836
+ contractId: cleanText(rootCauseEntryContract.contract_id || rootCauseEntryContract.contractId, 160),
2837
+ version: cleanText(rootCauseEntryContract.version, 120),
2838
+ status: cleanText(rootCauseEntryContract.status, 120),
2839
+ issueClassProbeCount: asArray(rootCauseEntryContract.issue_class_probes || rootCauseEntryContract.issueClassProbes).length,
2840
+ ownerFileMax: Number(rootCauseEntryContract.owner_file_policy?.max_files || rootCauseEntryContract.ownerFilePolicy?.maxFiles || 0) || 0,
2841
+ requiresBusinessAssertion: rootCauseEntryContract.business_proof_policy?.requires_aiqa_business_assertion === true
2842
+ || rootCauseEntryContract.businessProofPolicy?.requiresAiqaBusinessAssertion === true
2843
+ } : undefined,
2844
+ diagnosis: Object.keys(diagnosisGate).length ? {
2845
+ status: cleanText(diagnosisGate.status, 80),
2846
+ issueClass: diagnosisIssueClass,
2847
+ ownerFiles: diagnosisOwnerFiles,
2848
+ acceptedHypothesis: cleanText(diagnosisGate.accepted_hypothesis?.statement || diagnosisGate.acceptedHypothesis?.statement, 1000),
2849
+ proofPlan: diagnosisGate.proof_plan || diagnosisGate.proofPlan
2850
+ } : undefined,
2851
+ diagnosisEvidencePack: diagnosisEvidencePackGate ? {
2852
+ status: cleanText(diagnosisEvidencePackGate.metadata?.status, 120),
2853
+ gateStatus: diagnosisEvidencePackGate.status,
2854
+ packId: cleanText(diagnosisEvidencePackGate.metadata?.packId, 180),
2855
+ readOnly: diagnosisEvidencePackGate.metadata?.readOnly !== false,
2856
+ sourceEditsAllowed: diagnosisEvidencePackGate.metadata?.sourceEditsAllowed === true,
2857
+ requiredOutputKey: cleanText(diagnosisEvidencePackGate.metadata?.requiredOutputKey, 120),
2858
+ diagnosisValid: diagnosisEvidencePackGate.metadata?.diagnosisValid === true,
2859
+ issueClassHint: cleanText(diagnosisEvidencePackGate.metadata?.issueClassHint, 120),
2860
+ ownerFileHints: cleanStringList(diagnosisEvidencePackGate.metadata?.ownerFileHints, 20, 500),
2861
+ similarHintCount: Number(diagnosisEvidencePackGate.metadata?.similarHintCount || 0),
2862
+ similarHintsAdvisoryOnly: diagnosisEvidencePackGate.metadata?.similarHintsAdvisoryOnly === true,
2863
+ issueClassProbeCount: Number(diagnosisEvidencePackGate.metadata?.issueClassProbeCount || 0),
2864
+ validationBlockers: cleanStringList(diagnosisEvidencePackGate.metadata?.validationBlockers, 20, 500)
2865
+ } : undefined,
2866
+ businessProofReadiness: Object.keys(businessProofReadiness).length ? {
2867
+ ready: businessProofReadiness.ready === true,
2868
+ status: cleanText(businessProofReadiness.status, 120),
2869
+ reason: cleanText(businessProofReadiness.reason, 1000),
2870
+ blockers: cleanStringList(businessProofReadiness.blockers, 20, 500)
2871
+ } : undefined,
2872
+ nextActionContract: nextActionContractGate ? {
2873
+ status: nextActionContractGate.status,
2874
+ action: cleanText(nextActionContractGate.metadata?.action, 160),
2875
+ primaryCommand: cleanText(nextActionContractGate.metadata?.primaryCommand, 240),
2876
+ safeToAutoRun: nextActionContractGate.metadata?.safeToAutoRun === true,
2877
+ canRunWithoutCodexMonitor: nextActionContractGate.metadata?.canRunWithoutCodexMonitor === true,
2878
+ codexFallbackRequired: nextActionContractGate.metadata?.codexFallbackRequired === true,
2879
+ requiresHumanApproval: nextActionContractGate.metadata?.requiresHumanApproval === true,
2880
+ rootCauseFirstSatisfied: nextActionContractGate.metadata?.rootCauseFirstSatisfied === true,
2881
+ hotfixCommitRequired: nextActionContractGate.metadata?.hotfixCommitRequired === true,
2882
+ liveHotfixBlockedUntilCommit: nextActionContractGate.metadata?.liveHotfixBlockedUntilCommit === true,
2883
+ failureClass: cleanText(nextActionContractGate.metadata?.failureClass, 120),
2884
+ evidenceFreshnessStatus: cleanText(nextActionContractGate.metadata?.evidenceFreshnessStatus, 120),
2885
+ sameFailureCount: Number(nextActionContractGate.metadata?.sameFailureCount || 0),
2886
+ blockers: cleanStringList(nextActionContractGate.metadata?.blockers, 20, 500)
2887
+ } : undefined,
2888
+ customerReplyPolicy: Object.keys(customerReplyPolicy).length ? {
2889
+ action: cleanText(customerReplyPolicy.action, 120),
2890
+ safety: cleanText(customerReplyPolicy.safety, 120),
2891
+ canDraftCustomerReply: customerReplyPolicy.canDraftCustomerReply === true || customerReplyPolicy.can_draft_customer_reply === true,
2892
+ canSendCustomerReply: customerReplyPolicy.canSendCustomerReply === true || customerReplyPolicy.can_send_customer_reply === true,
2893
+ reason: cleanText(customerReplyPolicy.reason, 1000),
2894
+ reviewType: cleanText(
2895
+ customerReplyPolicy.humanReviewPacket?.reviewType
2896
+ || customerReplyPolicy.human_review_packet?.review_type
2897
+ || customerReplyPolicy.human_review_packet?.reviewType,
2898
+ 160
2899
+ ),
2900
+ primaryAction: cleanText(
2901
+ customerReplyPolicy.humanReviewPacket?.primaryAction
2902
+ || customerReplyPolicy.human_review_packet?.primary_action
2903
+ || customerReplyPolicy.human_review_packet?.primaryAction,
2904
+ 160
2905
+ )
2906
+ } : undefined
2907
+ }
2908
+ });
2909
+ }
2910
+
2911
+ export function buildAICoderAIRunFromEvidence(input: AICoderAIRunAdapterInput): AIRun {
2912
+ const app = input.app || {};
2913
+ const job = input.job || {};
2914
+ const evidence = buildAICoderQaEvidence(input);
2915
+ const events: AIRunEvent[] = [];
2916
+ const gates: AIRunGateResult[] = [];
2917
+ const sourceIds: Record<string, string> = {};
2918
+ addSourceId(sourceIds, 'appId', app._id || app.id || job.appId || job.aicoderAppId);
2919
+ addSourceId(sourceIds, 'jobId', job._id || job.id || job.jobId);
2920
+ addSourceId(sourceIds, 'domain', app.domain || app.customDomain || job.domain);
2921
+
2922
+ for (const log of asArray<Record<string, any>>(input.logs)) {
2923
+ pushEvent(events, {
2924
+ type: log.type === 'scorecard' ? 'scorecard' : 'log',
2925
+ category: cleanText(log.category || log.phase || log.level, 160),
2926
+ message: cleanText(log.message || log.summary || log.text || log.title, 1800),
2927
+ artifactPaths: asArray<string>(log.artifactPaths || log.artifacts),
2928
+ recordedAt: eventDate(log),
2929
+ metadata: {
2930
+ status: log.status,
2931
+ level: log.level,
2932
+ score: log.score
2933
+ }
2934
+ });
2935
+ }
2936
+ collectCommitEvents(asArray<Record<string, any>>(input.commits), events);
2937
+ collectHotfixEvidenceEvents([job, app, evidence, input.qaEvidence], events, gates, input.now);
2938
+ const noBlindLoopGate = aicoderManagerNoBlindLoopGate(app, job, evidence, input.now);
2939
+ if (noBlindLoopGate) {
2940
+ gates.push(noBlindLoopGate);
2941
+ pushEvent(events, {
2942
+ type: 'log',
2943
+ category: 'manager_no_blind_loop_policy',
2944
+ message: noBlindLoopGate.reason,
2945
+ artifactPaths: noBlindLoopGate.evidenceRefs,
2946
+ recordedAt: noBlindLoopGate.recordedAt,
2947
+ metadata: noBlindLoopGate.metadata
2948
+ });
2949
+ }
2950
+ const cost = collectUsageEvents(asArray<Record<string, any>>(input.usageLedger), events);
2951
+ const journeyContractValidation = aicoderJourneyContractValidationFromEvidence(evidence);
2952
+ const journeyContractGate = aicoderJourneyContractGate(journeyContractValidation, evidence, input.now);
2953
+ gates.push(journeyContractGate);
2954
+ pushEvent(events, {
2955
+ type: 'log',
2956
+ category: 'aicoder_journey_contract',
2957
+ message: journeyContractGate.reason,
2958
+ artifactPaths: journeyContractGate.evidenceRefs,
2959
+ recordedAt: journeyContractGate.recordedAt,
2960
+ metadata: journeyContractGate.metadata
2961
+ });
2962
+ const scorecardPassed = evidence.scorecardPassed === true
2963
+ || evidence.scorecardStatus === 'pass'
2964
+ || (Number(evidence.score ?? evidence.qualityScore ?? 0) >= Number(evidence.scoreThreshold ?? job.scoreThreshold ?? 90));
2965
+ const explicitWorkflowReadiness = normalizeAICoderWorkflowProofReadiness(plainObject(
2966
+ evidence.workflowProofReadiness
2967
+ || evidence.workflow_proof_readiness
2968
+ || evidence.aicoderWorkflowProofReadiness
2969
+ || evidence.aicoder_workflow_proof_readiness
2970
+ || job.workflowProofReadiness
2971
+ || job.workflow_proof_readiness
2972
+ || job.aicoderWorkflowProofReadiness
2973
+ || job.aicoder_workflow_proof_readiness
2974
+ ), input.now);
2975
+ const workflowReadiness = explicitWorkflowReadiness || evaluateResolveIOAICoderWorkflowProofReadiness({
2976
+ journeyContract: evidence.journeyContract,
2977
+ journeyContractMarkdown: evidence.journeyContractMarkdown,
2978
+ workflowQaRows: asArray(evidence.workflowQaRows || evidence.workflow_qa_rows),
2979
+ businessAssertions: [
2980
+ ...asArray(evidence.businessAssertions),
2981
+ ...asArray(evidence.business_assertions),
2982
+ ...asArray(evidence.workflowAssertions),
2983
+ ...asArray(evidence.workflow_assertions)
2984
+ ],
2985
+ routeProbes: [
2986
+ ...asArray(evidence.routeProbes),
2987
+ ...asArray(evidence.route_probes),
2988
+ ...asArray(evidence.workflowProbes),
2989
+ ...asArray(evidence.workflow_probes)
2990
+ ],
2991
+ artifactPaths: [
2992
+ ...asArray(evidence.artifactPaths),
2993
+ ...asArray(evidence.artifact_paths)
2994
+ ],
2995
+ scorecardPassed,
2996
+ deployStatus: evidence.deployStatus,
2997
+ publishStatus: evidence.publishStatus,
2998
+ sampleDataStatus: evidence.sampleDataStatus,
2999
+ previousWorkflowProofFingerprint: firstNonEmptyText([
3000
+ evidence.previousWorkflowProofFingerprint,
3001
+ evidence.previous_workflow_proof_fingerprint,
3002
+ job.previousWorkflowProofFingerprint,
3003
+ job.previous_workflow_proof_fingerprint,
3004
+ app.previousWorkflowProofFingerprint,
3005
+ app.previous_workflow_proof_fingerprint
3006
+ ], 200),
3007
+ previousArtifactFingerprint: firstNonEmptyText([
3008
+ evidence.previousArtifactFingerprint,
3009
+ evidence.previous_artifact_fingerprint,
3010
+ job.previousArtifactFingerprint,
3011
+ job.previous_artifact_fingerprint,
3012
+ app.previousArtifactFingerprint,
3013
+ app.previous_artifact_fingerprint
3014
+ ], 200),
3015
+ previousWorkflowProofFingerprints: [
3016
+ ...asArray(evidence.previousWorkflowProofFingerprints),
3017
+ ...asArray(evidence.previous_workflow_proof_fingerprints),
3018
+ ...asArray(job.previousWorkflowProofFingerprints),
3019
+ ...asArray(job.previous_workflow_proof_fingerprints),
3020
+ ...asArray(app.previousWorkflowProofFingerprints),
3021
+ ...asArray(app.previous_workflow_proof_fingerprints)
3022
+ ],
3023
+ previousArtifactFingerprints: [
3024
+ ...asArray(evidence.previousArtifactFingerprints),
3025
+ ...asArray(evidence.previous_artifact_fingerprints),
3026
+ ...asArray(job.previousArtifactFingerprints),
3027
+ ...asArray(job.previous_artifact_fingerprints),
3028
+ ...asArray(app.previousArtifactFingerprints),
3029
+ ...asArray(app.previous_artifact_fingerprints)
3030
+ ],
3031
+ now: input.now
3032
+ });
3033
+ const workflowReadinessGate = aicoderWorkflowProofReadinessGate(workflowReadiness, input.now);
3034
+ gates.push(workflowReadinessGate);
3035
+ pushEvent(events, {
3036
+ type: 'qa_business_assertion',
3037
+ category: 'aicoder_workflow_proof_readiness',
3038
+ message: workflowReadinessGate.reason,
3039
+ artifactPaths: workflowReadinessGate.evidenceRefs,
3040
+ recordedAt: workflowReadinessGate.recordedAt,
3041
+ metadata: workflowReadinessGate.metadata
3042
+ });
3043
+ const workflowProofCheckpoint = aicoderWorkflowProofCheckpointObject(app, job, evidence);
3044
+ const workflowProofCheckpointGate = aicoderWorkflowProofCheckpointGate(workflowProofCheckpoint, input.now);
3045
+ if (workflowProofCheckpointGate) {
3046
+ gates.push(workflowProofCheckpointGate);
3047
+ pushEvent(events, {
3048
+ type: 'log',
3049
+ category: 'aicoder_workflow_proof_checkpoint',
3050
+ message: workflowProofCheckpointGate.reason,
3051
+ artifactPaths: workflowProofCheckpointGate.evidenceRefs,
3052
+ recordedAt: workflowProofCheckpointGate.recordedAt,
3053
+ metadata: workflowProofCheckpointGate.metadata
3054
+ });
3055
+ }
3056
+ const qa = applyAICoderWorkflowProofGate(buildQaFromEvidence(evidence, input.now), workflowReadiness, input.now);
3057
+
3058
+ return buildAIRun({
3059
+ source: 'aicoder_app',
3060
+ sourceIds,
3061
+ title: firstText(app, ['name', 'title']) || firstText(job, ['appName', 'title', 'goal']),
3062
+ status: firstText(job, ['status', 'state']) || firstText(app, ['status', 'state']),
3063
+ phase: firstText(job, ['phase', 'runnerPhase']),
3064
+ startedAt: dateValue(job, ['startedAt', 'createdAt']) || dateValue(app, ['createdAt']),
3065
+ completedAt: dateValue(job, ['completedAt', 'finishedAt', 'updatedAt']),
3066
+ events,
3067
+ gates,
3068
+ qa,
3069
+ cost,
3070
+ scorecardPassed,
3071
+ deployStatus: evidence.deployStatus || statusFromBoolean(evidence.deployReady),
3072
+ publishStatus: evidence.publishStatus || evidence.pullRequestStatus,
3073
+ sampleDataStatus: evidence.sampleDataStatus || statusFromBoolean(evidence.sampleDataReady ?? evidence.hasSampleData),
3074
+ manualHandoff: evidence.manualHandoff === true || job.manualHandoff === true,
3075
+ stopped: evidence.stopped === true || job.stopped === true,
3076
+ rejected: evidence.rejected === true,
3077
+ explicitAccepted: evidence.explicitAccepted === true || app.accepted === true,
3078
+ terminal: /complete|closed|resolved|done|failed|rejected/i.test(`${job.status || ''} ${job.phase || ''}`),
3079
+ now: input.now,
3080
+ metadata: {
3081
+ domain: app.domain || app.customDomain || job.domain,
3082
+ wowScore: evidence.wowScore ?? job.wowScore,
3083
+ score: evidence.score ?? evidence.qualityScore,
3084
+ managerNoBlindLoopPolicy: managerNoBlindLoopMetadata(noBlindLoopGate),
3085
+ journeyContract: {
3086
+ status: journeyContractGate.status,
3087
+ valid: journeyContractGate.metadata?.valid === true,
3088
+ path: cleanText(journeyContractGate.metadata?.path, 500),
3089
+ primaryWorkflowId: cleanText(journeyContractGate.metadata?.primaryWorkflowId, 200),
3090
+ workflowQaRowCount: Number(journeyContractGate.metadata?.workflowQaRowCount || 0),
3091
+ errorCount: Number(journeyContractGate.metadata?.errorCount || 0),
3092
+ warningCount: Number(journeyContractGate.metadata?.warningCount || 0),
3093
+ issueCodes: cleanStringList(journeyContractGate.metadata?.issueCodes, 40, 120),
3094
+ blocksBuildUntilValid: journeyContractGate.metadata?.blocksBuildUntilValid === true,
3095
+ blocksWorkflowQaUntilValid: journeyContractGate.metadata?.blocksWorkflowQaUntilValid === true,
3096
+ blocksWowUiUntilWorkflowProof: journeyContractGate.metadata?.blocksWowUiUntilWorkflowProof === true,
3097
+ blocksPublishUntilWorkflowProof: journeyContractGate.metadata?.blocksPublishUntilWorkflowProof === true,
3098
+ nextAction: cleanText(journeyContractGate.metadata?.nextAction, 1000)
3099
+ },
3100
+ workflowProofReadiness: {
3101
+ ready: workflowReadiness.ready,
3102
+ status: workflowReadiness.status,
3103
+ reason: workflowReadiness.reason,
3104
+ blockers: workflowReadiness.blockers,
3105
+ primaryWorkflowId: workflowReadiness.primaryWorkflowId,
3106
+ passedWorkflowRows: workflowReadiness.passedWorkflowRows,
3107
+ missingWorkflowRows: workflowReadiness.missingWorkflowRows,
3108
+ failedWorkflowRows: workflowReadiness.failedWorkflowRows,
3109
+ sampleDataReady: workflowReadiness.sampleDataReady,
3110
+ releaseBlockers: workflowReadiness.releaseBlockers,
3111
+ workflowProofFingerprint: workflowReadiness.workflowProofFingerprint,
3112
+ artifactFingerprint: workflowReadiness.artifactFingerprint,
3113
+ proofFreshness: workflowReadiness.proofFreshness
3114
+ },
3115
+ workflowProofCheckpoint: workflowProofCheckpointGate ? {
3116
+ status: cleanText(workflowProofCheckpointGate.metadata?.status, 120),
3117
+ gateStatus: workflowProofCheckpointGate.status,
3118
+ readinessStatus: cleanText(workflowProofCheckpointGate.metadata?.readinessStatus, 120),
3119
+ nextGate: cleanText(workflowProofCheckpointGate.metadata?.nextGate, 120),
3120
+ nextAction: cleanText(workflowProofCheckpointGate.metadata?.nextAction, 1000),
3121
+ blocksProductRepairUntilJourneyContract: workflowProofCheckpointGate.metadata?.blocksProductRepairUntilJourneyContract === true,
3122
+ blocksPublishUntilWorkflowProof: workflowProofCheckpointGate.metadata?.blocksPublishUntilWorkflowProof === true,
3123
+ blocksPublishUntilReleaseGate: workflowProofCheckpointGate.metadata?.blocksPublishUntilReleaseGate === true,
3124
+ blocksWowUiUntilWorkflowProof: workflowProofCheckpointGate.metadata?.blocksWowUiUntilWorkflowProof === true,
3125
+ workflowProofFingerprint: cleanText(workflowProofCheckpointGate.metadata?.workflowProofFingerprint, 200),
3126
+ artifactFingerprint: cleanText(workflowProofCheckpointGate.metadata?.artifactFingerprint, 200),
3127
+ blockers: cleanStringList(workflowProofCheckpointGate.metadata?.blockers, 20, 500)
3128
+ } : undefined
3129
+ }
3130
+ });
3131
+ }
3132
+
3133
+ export function buildAssistantAIRunFromEvidence(input: AssistantAIRunAdapterInput): AIRun {
3134
+ const conversation = input.conversation || {};
3135
+ const events: AIRunEvent[] = [];
3136
+ const sourceIds: Record<string, string> = {};
3137
+ addSourceId(sourceIds, 'runId', input.runId);
3138
+ addSourceId(sourceIds, 'requestId', input.requestId);
3139
+ addSourceId(sourceIds, 'messageId', input.messageId);
3140
+ addSourceId(sourceIds, 'conversationId', conversation._id || conversation.id || conversation.conversationId);
3141
+ addSourceId(sourceIds, 'userId', conversation.userId || conversation.ownerId);
3142
+
3143
+ for (const message of asArray<Record<string, any>>(input.messages)) {
3144
+ pushEvent(events, {
3145
+ type: 'assistant_message',
3146
+ category: cleanText(message.role || message.authorRole || message.type, 160),
3147
+ message: cleanText(message.content || message.text || message.message || message.summary, 1800),
3148
+ recordedAt: eventDate(message),
3149
+ metadata: {
3150
+ id: message._id || message.id,
3151
+ role: message.role,
3152
+ model: message.model || message?.usage?.model
3153
+ }
3154
+ });
3155
+ }
3156
+ for (const report of asArray<Record<string, any>>(input.issueReports)) {
3157
+ pushEvent(events, {
3158
+ type: 'human_intervention',
3159
+ category: cleanText(report.category || report.status || 'issue_report', 160),
3160
+ message: cleanText(report.summary || report.description || report.message || report.title, 1800),
3161
+ recordedAt: eventDate(report),
3162
+ metadata: {
3163
+ id: report._id || report.id,
3164
+ status: report.status,
3165
+ severity: report.severity
3166
+ }
3167
+ });
3168
+ }
3169
+ const cost = collectUsageEvents(asArray<Record<string, any>>(input.usageLedger), events);
3170
+ const answerQuality = assistantQualityObject(input);
3171
+ for (const execution of assistantQueryExecutions(answerQuality).slice(0, 40)) {
3172
+ const status = cleanText(execution.status || execution.queryStatus || execution.query_status || execution.resultStatus || execution.result_status, 120);
3173
+ const toolName = cleanText(execution.tool || execution.toolName || execution.tool_name || execution.type || 'query', 160);
3174
+ const collection = cleanText(execution.collection || execution.collectionName || execution.collection_name, 240);
3175
+ pushEvent(events, {
3176
+ type: 'assistant_query',
3177
+ category: status || toolName,
3178
+ message: cleanText(execution.summary || execution.message || `${toolName}${collection ? ` ${collection}` : ''}`, 1200),
3179
+ artifactPaths: cleanStringList([
3180
+ execution.artifactPath,
3181
+ execution.artifact_path,
3182
+ execution.path,
3183
+ ...asArray(execution.artifactPaths),
3184
+ ...asArray(execution.artifact_paths)
3185
+ ], 20, 500),
3186
+ recordedAt: eventDate(execution),
3187
+ metadata: {
3188
+ id: execution.id || execution._id || execution.ref,
3189
+ tool: toolName,
3190
+ status,
3191
+ collection,
3192
+ resultCount: execution.resultCount ?? execution.result_count ?? execution.count,
3193
+ queryStatus: execution.queryStatus || execution.query_status,
3194
+ legalQueryShape: execution.legalQueryShape ?? execution.legal_query_shape
3195
+ }
3196
+ });
3197
+ }
3198
+ const answerQualityDecision = evaluateAssistantAnswerQuality({
3199
+ answerQuality,
3200
+ correctnessChecks: input.correctnessChecks,
3201
+ now: input.now
3202
+ });
3203
+ const answerQualityGate = assistantAnswerQualityGate(answerQualityDecision, input.now);
3204
+ pushEvent(events, {
3205
+ type: 'assistant_message',
3206
+ category: 'assistant_answer_quality',
3207
+ message: answerQualityGate.reason,
3208
+ artifactPaths: answerQualityGate.evidenceRefs,
3209
+ recordedAt: answerQualityGate.recordedAt,
3210
+ metadata: answerQualityGate.metadata
3211
+ });
3212
+ const correctnessChecks = asArray<Record<string, any>>(input.correctnessChecks);
3213
+ const hasPassedCorrectnessCheck = correctnessChecks.some((check) => /^(pass|passed|success|ok)$/i.test(cleanText(check.status || (check.passed === true ? 'pass' : ''), 80)));
3214
+ const correctnessEvidence = {
3215
+ businessAssertions: [
3216
+ ...correctnessChecks.map((check) => ({
3217
+ assertion: check.assertion || check.name || check.case || 'assistant correctness check',
3218
+ status: check.status || statusFromBoolean(check.passed),
3219
+ expected: check.expected,
3220
+ observed: check.observed || check.actual,
3221
+ dataProof: check.dataProof || check.proof,
3222
+ artifactPaths: check.artifactPaths,
3223
+ message: check.message || check.reason,
3224
+ metadata: check.metadata,
3225
+ recordedAt: eventDate(check)
3226
+ })),
3227
+ ...(answerQualityDecision.ready && !hasPassedCorrectnessCheck ? [{
3228
+ assertion: 'Assistant answer quality gate passed.',
3229
+ status: 'pass',
3230
+ expected: 'Correct dates, legal query shape, query result classification, cited evidence, confidence, and next action.',
3231
+ observed: answerQualityDecision.reason,
3232
+ dataProof: answerQualityDecision.evidenceRefs.concat(answerQualityDecision.citationRefs).join(', '),
3233
+ artifactPaths: answerQualityDecision.evidenceRefs,
3234
+ message: answerQualityDecision.reason,
3235
+ metadata: {
3236
+ answerQualityStatus: answerQualityDecision.status,
3237
+ queryStatus: answerQualityDecision.queryStatus,
3238
+ dateWindowRequired: answerQualityDecision.dateWindowRequired,
3239
+ dateWindowPresent: answerQualityDecision.dateWindowPresent,
3240
+ dateWindow: answerQualityDecision.dateWindow
3241
+ }
3242
+ }] : [])
3243
+ ]
3244
+ };
3245
+ const qa = applyAssistantAnswerQualityGate(buildQaFromEvidence(correctnessEvidence, input.now), answerQualityDecision, input.now);
3246
+ const failedReports = asArray<Record<string, any>>(input.issueReports)
3247
+ .filter((report) => /open|new|fail|bug|wrong|incorrect|error/i.test(`${report.status || ''} ${report.category || ''} ${report.summary || ''}`));
3248
+
3249
+ return buildAIRun({
3250
+ id: cleanText(input.runId, 240) || undefined,
3251
+ source: 'ai_assistant',
3252
+ sourceIds,
3253
+ title: firstText(conversation, ['title', 'subject', 'summary']) || 'AI assistant conversation',
3254
+ status: firstText(conversation, ['status', 'state']),
3255
+ phase: firstText(conversation, ['phase']),
3256
+ startedAt: dateValue(conversation, ['startedAt', 'createdAt']),
3257
+ completedAt: dateValue(conversation, ['completedAt', 'closedAt', 'updatedAt']),
3258
+ events,
3259
+ qa,
3260
+ cost,
3261
+ rejected: failedReports.length > 0 || conversation.rejected === true,
3262
+ manualHandoff: conversation.manualHandoff === true,
3263
+ stopped: conversation.stopped === true,
3264
+ explicitAccepted: conversation.accepted === true,
3265
+ terminal: /complete|closed|resolved|done|failed|rejected/i.test(`${conversation.status || ''}`),
3266
+ now: input.now,
3267
+ metadata: {
3268
+ issueReportCount: asArray(input.issueReports).length,
3269
+ correctnessCheckCount: correctnessChecks.length,
3270
+ answerQuality: {
3271
+ ready: answerQualityDecision.ready,
3272
+ status: answerQualityDecision.status,
3273
+ queryStatus: answerQualityDecision.queryStatus,
3274
+ confidenceLevel: answerQualityDecision.confidenceLevel,
3275
+ dateBasis: answerQualityDecision.dateBasis,
3276
+ expectedCurrentDate: answerQualityDecision.expectedCurrentDate,
3277
+ dateWindowRequired: answerQualityDecision.dateWindowRequired,
3278
+ dateWindowPresent: answerQualityDecision.dateWindowPresent,
3279
+ dateWindow: answerQualityDecision.dateWindow,
3280
+ noDataConfirmed: answerQualityDecision.noDataConfirmed,
3281
+ legalQueryShape: answerQualityDecision.legalQueryShape,
3282
+ citationRefs: answerQualityDecision.citationRefs,
3283
+ queryEvidenceRequired: answerQualityDecision.queryEvidenceRequired,
3284
+ queryEvidencePresent: answerQualityDecision.queryEvidencePresent,
3285
+ queryEvidenceRefs: answerQualityDecision.queryEvidenceRefs,
3286
+ queryExecutionCount: answerQualityDecision.queryExecutionCount,
3287
+ nextActions: answerQualityDecision.nextActions,
3288
+ blockers: answerQualityDecision.blockers
3289
+ }
3290
+ }
3291
+ });
3292
+ }