@prismatic-io/lux 0.0.2-preview.3 → 0.0.2-preview.32

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 (727) hide show
  1. package/lib/answerers/claude-code/index.d.ts +15 -3
  2. package/lib/answerers/claude-code/index.d.ts.map +1 -1
  3. package/lib/answerers/claude-code/index.js +6 -5
  4. package/lib/answerers/claude-code/index.js.map +1 -1
  5. package/lib/answerers/persona/index.d.ts +44 -5
  6. package/lib/answerers/persona/index.d.ts.map +1 -1
  7. package/lib/answerers/persona/index.js +60 -34
  8. package/lib/answerers/persona/index.js.map +1 -1
  9. package/lib/answerers/terminal/index.d.ts +9 -3
  10. package/lib/answerers/terminal/index.d.ts.map +1 -1
  11. package/lib/answerers/terminal/index.js +7 -5
  12. package/lib/answerers/terminal/index.js.map +1 -1
  13. package/lib/assertions/authoring.d.ts +2 -2
  14. package/lib/assertions/authoring.d.ts.map +1 -1
  15. package/lib/assertions/authoring.js.map +1 -1
  16. package/lib/assertions/core/command-exits-zero.d.ts +4 -1
  17. package/lib/assertions/core/command-exits-zero.d.ts.map +1 -1
  18. package/lib/assertions/core/command-exits-zero.js +50 -7
  19. package/lib/assertions/core/command-exits-zero.js.map +1 -1
  20. package/lib/assertions/core/event-checks.d.ts +3 -0
  21. package/lib/assertions/core/event-checks.d.ts.map +1 -1
  22. package/lib/assertions/core/event-checks.js +49 -3
  23. package/lib/assertions/core/event-checks.js.map +1 -1
  24. package/lib/assertions/core/file-checks.d.ts +6 -0
  25. package/lib/assertions/core/file-checks.d.ts.map +1 -1
  26. package/lib/assertions/core/helpers.d.ts +2 -7
  27. package/lib/assertions/core/helpers.d.ts.map +1 -1
  28. package/lib/assertions/core/helpers.js +2 -6
  29. package/lib/assertions/core/helpers.js.map +1 -1
  30. package/lib/assertions/core/index.d.ts.map +1 -1
  31. package/lib/assertions/core/index.js +2 -1
  32. package/lib/assertions/core/index.js.map +1 -1
  33. package/lib/assertions/core/json-pointer-equals.d.ts +1 -0
  34. package/lib/assertions/core/json-pointer-equals.d.ts.map +1 -1
  35. package/lib/assertions/core/output-checks.d.ts +3 -0
  36. package/lib/assertions/core/output-checks.d.ts.map +1 -1
  37. package/lib/assertions/core/predicate.d.ts +1 -0
  38. package/lib/assertions/core/predicate.d.ts.map +1 -1
  39. package/lib/assertions/core/resource-checks.d.ts +25 -0
  40. package/lib/assertions/core/resource-checks.d.ts.map +1 -1
  41. package/lib/assertions/core/resource-checks.js +35 -5
  42. package/lib/assertions/core/resource-checks.js.map +1 -1
  43. package/lib/assertions/rubric/index.d.ts +28 -3
  44. package/lib/assertions/rubric/index.d.ts.map +1 -1
  45. package/lib/assertions/rubric/index.js +59 -13
  46. package/lib/assertions/rubric/index.js.map +1 -1
  47. package/lib/assertions/rubric/internal.d.ts +30 -5
  48. package/lib/assertions/rubric/internal.d.ts.map +1 -1
  49. package/lib/assertions/rubric/internal.js +181 -28
  50. package/lib/assertions/rubric/internal.js.map +1 -1
  51. package/lib/authoring.d.ts +36 -2
  52. package/lib/authoring.d.ts.map +1 -1
  53. package/lib/authoring.js +36 -1
  54. package/lib/authoring.js.map +1 -1
  55. package/lib/cli/bin.js +2 -13
  56. package/lib/cli/bin.js.map +1 -1
  57. package/lib/cli/command-runtime.d.ts +3 -3
  58. package/lib/cli/command-runtime.d.ts.map +1 -1
  59. package/lib/cli/command-runtime.js +17 -6
  60. package/lib/cli/command-runtime.js.map +1 -1
  61. package/lib/cli/doctor.d.ts +55 -0
  62. package/lib/cli/doctor.d.ts.map +1 -0
  63. package/lib/cli/doctor.js +14 -0
  64. package/lib/cli/doctor.js.map +1 -0
  65. package/lib/cli/harness-plan.d.ts +26 -0
  66. package/lib/cli/harness-plan.d.ts.map +1 -0
  67. package/lib/cli/harness-plan.js +75 -0
  68. package/lib/cli/harness-plan.js.map +1 -0
  69. package/lib/cli/init-templates.d.ts +11 -0
  70. package/lib/cli/init-templates.d.ts.map +1 -0
  71. package/lib/cli/init-templates.js +215 -0
  72. package/lib/cli/init-templates.js.map +1 -0
  73. package/lib/cli/init.d.ts +10 -0
  74. package/lib/cli/init.d.ts.map +1 -1
  75. package/lib/cli/init.js +34 -173
  76. package/lib/cli/init.js.map +1 -1
  77. package/lib/cli/output-schemas.d.ts +1408 -0
  78. package/lib/cli/output-schemas.d.ts.map +1 -0
  79. package/lib/cli/output-schemas.js +91 -0
  80. package/lib/cli/output-schemas.js.map +1 -0
  81. package/lib/cli/program.d.ts +140 -12
  82. package/lib/cli/program.d.ts.map +1 -1
  83. package/lib/cli/program.js +779 -646
  84. package/lib/cli/program.js.map +1 -1
  85. package/lib/cli/render/reporter.d.ts.map +1 -1
  86. package/lib/cli/render/reporter.js +48 -8
  87. package/lib/cli/render/reporter.js.map +1 -1
  88. package/lib/cli/run-options.d.ts +20 -4
  89. package/lib/cli/run-options.d.ts.map +1 -1
  90. package/lib/cli/run-options.js +100 -19
  91. package/lib/cli/run-options.js.map +1 -1
  92. package/lib/cli/view.d.ts +15 -0
  93. package/lib/cli/view.d.ts.map +1 -0
  94. package/lib/cli/view.js +168 -0
  95. package/lib/cli/view.js.map +1 -0
  96. package/lib/container/driver.d.ts +27 -0
  97. package/lib/container/driver.d.ts.map +1 -0
  98. package/lib/container/driver.js +197 -0
  99. package/lib/container/driver.js.map +1 -0
  100. package/lib/container/protocol.d.ts +12 -0
  101. package/lib/container/protocol.d.ts.map +1 -0
  102. package/lib/container/protocol.js +38 -0
  103. package/lib/container/protocol.js.map +1 -0
  104. package/lib/container/resources.d.ts +12 -0
  105. package/lib/container/resources.d.ts.map +1 -0
  106. package/lib/container/resources.js +133 -0
  107. package/lib/container/resources.js.map +1 -0
  108. package/lib/container/worker.d.ts +2 -0
  109. package/lib/container/worker.d.ts.map +1 -0
  110. package/lib/container/worker.js +85 -0
  111. package/lib/container/worker.js.map +1 -0
  112. package/lib/core/action-events.d.ts +29 -0
  113. package/lib/core/action-events.d.ts.map +1 -0
  114. package/lib/core/action-events.js +51 -0
  115. package/lib/core/action-events.js.map +1 -0
  116. package/lib/core/agent-activity.d.ts +49 -0
  117. package/lib/core/agent-activity.d.ts.map +1 -0
  118. package/lib/core/agent-activity.js +206 -0
  119. package/lib/core/agent-activity.js.map +1 -0
  120. package/lib/core/agent-environment.d.ts +5 -0
  121. package/lib/core/agent-environment.d.ts.map +1 -0
  122. package/lib/core/agent-environment.js +47 -0
  123. package/lib/core/agent-environment.js.map +1 -0
  124. package/lib/core/agent-events.d.ts +26 -0
  125. package/lib/core/agent-events.d.ts.map +1 -0
  126. package/lib/core/agent-events.js +13 -0
  127. package/lib/core/agent-events.js.map +1 -0
  128. package/lib/core/annotation.d.ts +6 -2
  129. package/lib/core/annotation.d.ts.map +1 -1
  130. package/lib/core/annotation.js +2 -1
  131. package/lib/core/annotation.js.map +1 -1
  132. package/lib/core/answerer.d.ts +12 -0
  133. package/lib/core/answerer.d.ts.map +1 -1
  134. package/lib/core/answerer.js +2 -0
  135. package/lib/core/answerer.js.map +1 -1
  136. package/lib/core/artifact-evidence.d.ts +8 -0
  137. package/lib/core/artifact-evidence.d.ts.map +1 -1
  138. package/lib/core/artifact-evidence.js +36 -4
  139. package/lib/core/artifact-evidence.js.map +1 -1
  140. package/lib/core/assertion.d.ts +20 -2
  141. package/lib/core/assertion.d.ts.map +1 -1
  142. package/lib/core/assertion.js +19 -1
  143. package/lib/core/assertion.js.map +1 -1
  144. package/lib/core/case.d.ts +111 -3
  145. package/lib/core/case.d.ts.map +1 -1
  146. package/lib/core/case.js +17 -2
  147. package/lib/core/case.js.map +1 -1
  148. package/lib/core/command-verification.d.ts +10 -0
  149. package/lib/core/command-verification.d.ts.map +1 -0
  150. package/lib/core/command-verification.js +66 -0
  151. package/lib/core/command-verification.js.map +1 -0
  152. package/lib/core/driver-execution.d.ts +83 -0
  153. package/lib/core/driver-execution.d.ts.map +1 -0
  154. package/lib/core/driver-execution.js +72 -0
  155. package/lib/core/driver-execution.js.map +1 -0
  156. package/lib/core/driver-metrics.d.ts +98 -0
  157. package/lib/core/driver-metrics.d.ts.map +1 -0
  158. package/lib/core/driver-metrics.js +36 -0
  159. package/lib/core/driver-metrics.js.map +1 -0
  160. package/lib/core/driver.d.ts +64 -0
  161. package/lib/core/driver.d.ts.map +1 -1
  162. package/lib/core/driver.js +28 -0
  163. package/lib/core/driver.js.map +1 -1
  164. package/lib/core/duration.d.ts +5 -0
  165. package/lib/core/duration.d.ts.map +1 -0
  166. package/lib/core/duration.js +49 -0
  167. package/lib/core/duration.js.map +1 -0
  168. package/lib/core/environment.d.ts +18 -0
  169. package/lib/core/environment.d.ts.map +1 -0
  170. package/lib/core/environment.js +21 -0
  171. package/lib/core/environment.js.map +1 -0
  172. package/lib/core/evaluation-clusters.d.ts +17 -0
  173. package/lib/core/evaluation-clusters.d.ts.map +1 -0
  174. package/lib/core/evaluation-clusters.js +23 -0
  175. package/lib/core/evaluation-clusters.js.map +1 -0
  176. package/lib/core/events.d.ts +36 -0
  177. package/lib/core/events.d.ts.map +1 -1
  178. package/lib/core/events.js +12 -2
  179. package/lib/core/events.js.map +1 -1
  180. package/lib/core/experiment.d.ts +32 -2
  181. package/lib/core/experiment.d.ts.map +1 -1
  182. package/lib/core/experiment.js +35 -18
  183. package/lib/core/experiment.js.map +1 -1
  184. package/lib/core/file-tree.d.ts +7 -0
  185. package/lib/core/file-tree.d.ts.map +1 -0
  186. package/lib/core/file-tree.js +35 -0
  187. package/lib/core/file-tree.js.map +1 -0
  188. package/lib/core/global-id.d.ts +6 -0
  189. package/lib/core/global-id.d.ts.map +1 -0
  190. package/lib/core/global-id.js +17 -0
  191. package/lib/core/global-id.js.map +1 -0
  192. package/lib/core/harness-catalog.d.ts +35 -0
  193. package/lib/core/harness-catalog.d.ts.map +1 -0
  194. package/lib/core/harness-catalog.js +45 -0
  195. package/lib/core/harness-catalog.js.map +1 -0
  196. package/lib/core/harness-catalogs/antigravity.d.ts +11 -0
  197. package/lib/core/harness-catalogs/antigravity.d.ts.map +1 -0
  198. package/lib/core/harness-catalogs/antigravity.js +23 -0
  199. package/lib/core/harness-catalogs/antigravity.js.map +1 -0
  200. package/lib/core/harness-catalogs/claude-code.d.ts +12 -0
  201. package/lib/core/harness-catalogs/claude-code.d.ts.map +1 -0
  202. package/lib/core/harness-catalogs/claude-code.js +24 -0
  203. package/lib/core/harness-catalogs/claude-code.js.map +1 -0
  204. package/lib/core/harness-catalogs/codex.d.ts +11 -0
  205. package/lib/core/harness-catalogs/codex.d.ts.map +1 -0
  206. package/lib/core/harness-catalogs/codex.js +26 -0
  207. package/lib/core/harness-catalogs/codex.js.map +1 -0
  208. package/lib/core/harness-catalogs/copilot.d.ts +29 -0
  209. package/lib/core/harness-catalogs/copilot.d.ts.map +1 -0
  210. package/lib/core/harness-catalogs/copilot.js +45 -0
  211. package/lib/core/harness-catalogs/copilot.js.map +1 -0
  212. package/lib/core/harness-catalogs/cursor.d.ts +2 -0
  213. package/lib/core/harness-catalogs/cursor.d.ts.map +1 -0
  214. package/lib/core/harness-catalogs/cursor.js +13 -0
  215. package/lib/core/harness-catalogs/cursor.js.map +1 -0
  216. package/lib/core/harness-catalogs/grok-build.d.ts +7 -0
  217. package/lib/core/harness-catalogs/grok-build.d.ts.map +1 -0
  218. package/lib/core/harness-catalogs/grok-build.js +18 -0
  219. package/lib/core/harness-catalogs/grok-build.js.map +1 -0
  220. package/lib/core/harness-catalogs/index.d.ts +73 -0
  221. package/lib/core/harness-catalogs/index.d.ts.map +1 -0
  222. package/lib/core/harness-catalogs/index.js +17 -0
  223. package/lib/core/harness-catalogs/index.js.map +1 -0
  224. package/lib/core/harness-matrix.d.ts +154 -0
  225. package/lib/core/harness-matrix.d.ts.map +1 -0
  226. package/lib/core/harness-matrix.js +204 -0
  227. package/lib/core/harness-matrix.js.map +1 -0
  228. package/lib/core/index.d.ts +12 -0
  229. package/lib/core/index.d.ts.map +1 -1
  230. package/lib/core/index.js +12 -0
  231. package/lib/core/index.js.map +1 -1
  232. package/lib/core/lifecycle-fixtures.d.ts.map +1 -1
  233. package/lib/core/lifecycle-fixtures.js +4 -2
  234. package/lib/core/lifecycle-fixtures.js.map +1 -1
  235. package/lib/core/model-cli.d.ts +9 -11
  236. package/lib/core/model-cli.d.ts.map +1 -1
  237. package/lib/core/model-cli.js +31 -19
  238. package/lib/core/model-cli.js.map +1 -1
  239. package/lib/core/platform-process.d.ts +6 -1
  240. package/lib/core/platform-process.d.ts.map +1 -1
  241. package/lib/core/platform-process.js +49 -11
  242. package/lib/core/platform-process.js.map +1 -1
  243. package/lib/core/rubric.d.ts +1 -0
  244. package/lib/core/rubric.d.ts.map +1 -1
  245. package/lib/core/run.d.ts +293 -1
  246. package/lib/core/run.d.ts.map +1 -1
  247. package/lib/core/run.js +27 -0
  248. package/lib/core/run.js.map +1 -1
  249. package/lib/core/secure-copy.d.ts.map +1 -0
  250. package/lib/core/secure-copy.js.map +1 -0
  251. package/lib/core/usage.d.ts +5 -0
  252. package/lib/core/usage.d.ts.map +1 -1
  253. package/lib/core/usage.js +3 -0
  254. package/lib/core/usage.js.map +1 -1
  255. package/lib/core/wire.d.ts +12 -0
  256. package/lib/core/wire.d.ts.map +1 -1
  257. package/lib/core/wire.js +5 -1
  258. package/lib/core/wire.js.map +1 -1
  259. package/lib/drivers/antigravity/artifacts.d.ts +2 -0
  260. package/lib/drivers/antigravity/artifacts.d.ts.map +1 -0
  261. package/lib/drivers/antigravity/artifacts.js +33 -0
  262. package/lib/drivers/antigravity/artifacts.js.map +1 -0
  263. package/lib/drivers/antigravity/config.d.ts +88 -0
  264. package/lib/drivers/antigravity/config.d.ts.map +1 -0
  265. package/lib/drivers/antigravity/config.js +87 -0
  266. package/lib/drivers/antigravity/config.js.map +1 -0
  267. package/lib/drivers/antigravity/events.d.ts +4 -0
  268. package/lib/drivers/antigravity/events.d.ts.map +1 -0
  269. package/lib/drivers/antigravity/events.js +241 -0
  270. package/lib/drivers/antigravity/events.js.map +1 -0
  271. package/lib/drivers/antigravity/index.d.ts +114 -0
  272. package/lib/drivers/antigravity/index.d.ts.map +1 -0
  273. package/lib/drivers/antigravity/index.js +113 -0
  274. package/lib/drivers/antigravity/index.js.map +1 -0
  275. package/lib/drivers/antigravity/runtime.d.ts +7 -0
  276. package/lib/drivers/antigravity/runtime.d.ts.map +1 -0
  277. package/lib/drivers/antigravity/runtime.js +83 -0
  278. package/lib/drivers/antigravity/runtime.js.map +1 -0
  279. package/lib/drivers/claude-code/deferred-process.d.ts +17 -0
  280. package/lib/drivers/claude-code/deferred-process.d.ts.map +1 -0
  281. package/lib/drivers/claude-code/deferred-process.js +108 -0
  282. package/lib/drivers/claude-code/deferred-process.js.map +1 -0
  283. package/lib/drivers/claude-code/index.d.ts +180 -10
  284. package/lib/drivers/claude-code/index.d.ts.map +1 -1
  285. package/lib/drivers/claude-code/index.js +186 -252
  286. package/lib/drivers/claude-code/index.js.map +1 -1
  287. package/lib/drivers/claude-code/isolation.d.ts +8 -0
  288. package/lib/drivers/claude-code/isolation.d.ts.map +1 -0
  289. package/lib/drivers/claude-code/isolation.js +199 -0
  290. package/lib/drivers/claude-code/isolation.js.map +1 -0
  291. package/lib/drivers/claude-code/parse-events.d.ts.map +1 -1
  292. package/lib/drivers/claude-code/parse-events.js +73 -8
  293. package/lib/drivers/claude-code/parse-events.js.map +1 -1
  294. package/lib/drivers/codex/app-events.d.ts +9 -2
  295. package/lib/drivers/codex/app-events.d.ts.map +1 -1
  296. package/lib/drivers/codex/app-events.js +104 -12
  297. package/lib/drivers/codex/app-events.js.map +1 -1
  298. package/lib/drivers/codex/config.d.ts +66 -5
  299. package/lib/drivers/codex/config.d.ts.map +1 -1
  300. package/lib/drivers/codex/config.js +39 -8
  301. package/lib/drivers/codex/config.js.map +1 -1
  302. package/lib/drivers/codex/exec-events.d.ts +4 -0
  303. package/lib/drivers/codex/exec-events.d.ts.map +1 -0
  304. package/lib/drivers/codex/exec-events.js +230 -0
  305. package/lib/drivers/codex/exec-events.js.map +1 -0
  306. package/lib/drivers/codex/index.d.ts +138 -4
  307. package/lib/drivers/codex/index.d.ts.map +1 -1
  308. package/lib/drivers/codex/index.js +112 -276
  309. package/lib/drivers/codex/index.js.map +1 -1
  310. package/lib/drivers/copilot/config.d.ts +84 -0
  311. package/lib/drivers/copilot/config.d.ts.map +1 -0
  312. package/lib/drivers/copilot/config.js +57 -0
  313. package/lib/drivers/copilot/config.js.map +1 -0
  314. package/lib/drivers/copilot/index.d.ts +107 -0
  315. package/lib/drivers/copilot/index.d.ts.map +1 -0
  316. package/lib/drivers/copilot/index.js +268 -0
  317. package/lib/drivers/copilot/index.js.map +1 -0
  318. package/lib/drivers/cursor/config.d.ts +28 -0
  319. package/lib/drivers/cursor/config.d.ts.map +1 -0
  320. package/lib/drivers/cursor/config.js +28 -0
  321. package/lib/drivers/cursor/config.js.map +1 -0
  322. package/lib/drivers/cursor/index.d.ts +44 -0
  323. package/lib/drivers/cursor/index.d.ts.map +1 -0
  324. package/lib/drivers/cursor/index.js +286 -0
  325. package/lib/drivers/cursor/index.js.map +1 -0
  326. package/lib/drivers/cursor/interaction.d.ts +6 -0
  327. package/lib/drivers/cursor/interaction.d.ts.map +1 -0
  328. package/lib/drivers/cursor/interaction.js +55 -0
  329. package/lib/drivers/cursor/interaction.js.map +1 -0
  330. package/lib/drivers/grok-build/config.d.ts +68 -0
  331. package/lib/drivers/grok-build/config.d.ts.map +1 -0
  332. package/lib/drivers/grok-build/config.js +40 -0
  333. package/lib/drivers/grok-build/config.js.map +1 -0
  334. package/lib/drivers/grok-build/index.d.ts +87 -0
  335. package/lib/drivers/grok-build/index.d.ts.map +1 -0
  336. package/lib/drivers/grok-build/index.js +293 -0
  337. package/lib/drivers/grok-build/index.js.map +1 -0
  338. package/lib/drivers/mcp/index.d.ts +37 -4
  339. package/lib/drivers/mcp/index.d.ts.map +1 -1
  340. package/lib/drivers/mcp/index.js +25 -11
  341. package/lib/drivers/mcp/index.js.map +1 -1
  342. package/lib/drivers/shared/acp-events.d.ts +15 -0
  343. package/lib/drivers/shared/acp-events.d.ts.map +1 -0
  344. package/lib/drivers/shared/acp-events.js +83 -0
  345. package/lib/drivers/shared/acp-events.js.map +1 -0
  346. package/lib/drivers/shared/acp-interaction.d.ts +7 -0
  347. package/lib/drivers/shared/acp-interaction.d.ts.map +1 -0
  348. package/lib/drivers/shared/acp-interaction.js +20 -0
  349. package/lib/drivers/shared/acp-interaction.js.map +1 -0
  350. package/lib/drivers/shared/acp-session.d.ts +4 -0
  351. package/lib/drivers/shared/acp-session.d.ts.map +1 -0
  352. package/lib/drivers/shared/acp-session.js +23 -0
  353. package/lib/drivers/shared/acp-session.js.map +1 -0
  354. package/lib/drivers/shared/acp-transport.d.ts +40 -0
  355. package/lib/drivers/shared/acp-transport.d.ts.map +1 -0
  356. package/lib/drivers/shared/acp-transport.js +152 -0
  357. package/lib/drivers/shared/acp-transport.js.map +1 -0
  358. package/lib/drivers/shared/artifacts.d.ts.map +1 -1
  359. package/lib/drivers/shared/artifacts.js +3 -2
  360. package/lib/drivers/shared/artifacts.js.map +1 -1
  361. package/lib/drivers/shared/experiment-behavior.d.ts +13 -0
  362. package/lib/drivers/shared/experiment-behavior.d.ts.map +1 -0
  363. package/lib/drivers/shared/experiment-behavior.js +197 -0
  364. package/lib/drivers/shared/experiment-behavior.js.map +1 -0
  365. package/lib/drivers/shared/experiment-policy.d.ts +4 -0
  366. package/lib/drivers/shared/experiment-policy.d.ts.map +1 -0
  367. package/lib/drivers/shared/experiment-policy.js +143 -0
  368. package/lib/drivers/shared/experiment-policy.js.map +1 -0
  369. package/lib/drivers/subprocess/index.d.ts +39 -4
  370. package/lib/drivers/subprocess/index.d.ts.map +1 -1
  371. package/lib/drivers/subprocess/index.js +56 -12
  372. package/lib/drivers/subprocess/index.js.map +1 -1
  373. package/lib/index.d.ts +22 -8
  374. package/lib/index.d.ts.map +1 -1
  375. package/lib/index.js +20 -6
  376. package/lib/index.js.map +1 -1
  377. package/lib/optimization/reflective.d.ts +3 -1
  378. package/lib/optimization/reflective.d.ts.map +1 -1
  379. package/lib/optimization/reflective.js +3 -2
  380. package/lib/optimization/reflective.js.map +1 -1
  381. package/lib/orchestrator/annotation-loader.d.ts +6 -2
  382. package/lib/orchestrator/annotation-loader.d.ts.map +1 -1
  383. package/lib/orchestrator/annotation-loader.js +8 -0
  384. package/lib/orchestrator/annotation-loader.js.map +1 -1
  385. package/lib/orchestrator/annotation-store.d.ts.map +1 -1
  386. package/lib/orchestrator/annotation-store.js +16 -2
  387. package/lib/orchestrator/annotation-store.js.map +1 -1
  388. package/lib/orchestrator/artifact-origin.d.ts +3 -0
  389. package/lib/orchestrator/artifact-origin.d.ts.map +1 -0
  390. package/lib/orchestrator/artifact-origin.js +40 -0
  391. package/lib/orchestrator/artifact-origin.js.map +1 -0
  392. package/lib/orchestrator/campaign-lifecycle.d.ts +1 -0
  393. package/lib/orchestrator/campaign-lifecycle.d.ts.map +1 -1
  394. package/lib/orchestrator/campaign-lifecycle.js +4 -1
  395. package/lib/orchestrator/campaign-lifecycle.js.map +1 -1
  396. package/lib/orchestrator/candidates.d.ts.map +1 -1
  397. package/lib/orchestrator/candidates.js +3 -2
  398. package/lib/orchestrator/candidates.js.map +1 -1
  399. package/lib/orchestrator/compare.d.ts +19 -3
  400. package/lib/orchestrator/compare.d.ts.map +1 -1
  401. package/lib/orchestrator/compare.js +32 -6
  402. package/lib/orchestrator/compare.js.map +1 -1
  403. package/lib/orchestrator/comparison-identity.d.ts.map +1 -1
  404. package/lib/orchestrator/comparison-identity.js +2 -0
  405. package/lib/orchestrator/comparison-identity.js.map +1 -1
  406. package/lib/orchestrator/config.d.ts +179 -1
  407. package/lib/orchestrator/config.d.ts.map +1 -1
  408. package/lib/orchestrator/config.js +18 -4
  409. package/lib/orchestrator/config.js.map +1 -1
  410. package/lib/orchestrator/discover.d.ts +1 -0
  411. package/lib/orchestrator/discover.d.ts.map +1 -1
  412. package/lib/orchestrator/discover.js +7 -1
  413. package/lib/orchestrator/discover.js.map +1 -1
  414. package/lib/orchestrator/doctor.d.ts +1 -0
  415. package/lib/orchestrator/doctor.d.ts.map +1 -1
  416. package/lib/orchestrator/doctor.js +121 -33
  417. package/lib/orchestrator/doctor.js.map +1 -1
  418. package/lib/orchestrator/driver-capabilities.d.ts +4 -0
  419. package/lib/orchestrator/driver-capabilities.d.ts.map +1 -0
  420. package/lib/orchestrator/driver-capabilities.js +13 -0
  421. package/lib/orchestrator/driver-capabilities.js.map +1 -0
  422. package/lib/orchestrator/driver-identity.d.ts +5 -0
  423. package/lib/orchestrator/driver-identity.d.ts.map +1 -0
  424. package/lib/orchestrator/driver-identity.js +51 -0
  425. package/lib/orchestrator/driver-identity.js.map +1 -0
  426. package/lib/orchestrator/experiment-context.d.ts +51 -3
  427. package/lib/orchestrator/experiment-context.d.ts.map +1 -1
  428. package/lib/orchestrator/experiment-context.js.map +1 -1
  429. package/lib/orchestrator/experiment-contracts.d.ts +30 -1
  430. package/lib/orchestrator/experiment-contracts.d.ts.map +1 -1
  431. package/lib/orchestrator/experiment-corpus.d.ts +2 -1
  432. package/lib/orchestrator/experiment-corpus.d.ts.map +1 -1
  433. package/lib/orchestrator/experiment-corpus.js +28 -22
  434. package/lib/orchestrator/experiment-corpus.js.map +1 -1
  435. package/lib/orchestrator/experiment-effects.d.ts +33 -0
  436. package/lib/orchestrator/experiment-effects.d.ts.map +1 -0
  437. package/lib/orchestrator/experiment-effects.js +44 -0
  438. package/lib/orchestrator/experiment-effects.js.map +1 -0
  439. package/lib/orchestrator/experiment-evaluation.d.ts.map +1 -1
  440. package/lib/orchestrator/experiment-evaluation.js +56 -5
  441. package/lib/orchestrator/experiment-evaluation.js.map +1 -1
  442. package/lib/orchestrator/experiment-identity.d.ts +3 -0
  443. package/lib/orchestrator/experiment-identity.d.ts.map +1 -1
  444. package/lib/orchestrator/experiment-identity.js +22 -33
  445. package/lib/orchestrator/experiment-identity.js.map +1 -1
  446. package/lib/orchestrator/experiment-loader.d.ts +8 -3
  447. package/lib/orchestrator/experiment-loader.d.ts.map +1 -1
  448. package/lib/orchestrator/experiment-preflight.d.ts +3 -0
  449. package/lib/orchestrator/experiment-preflight.d.ts.map +1 -0
  450. package/lib/orchestrator/experiment-preflight.js +21 -0
  451. package/lib/orchestrator/experiment-preflight.js.map +1 -0
  452. package/lib/orchestrator/experiment-report.d.ts +90 -1
  453. package/lib/orchestrator/experiment-report.d.ts.map +1 -1
  454. package/lib/orchestrator/experiment-report.js +9 -1
  455. package/lib/orchestrator/experiment-report.js.map +1 -1
  456. package/lib/orchestrator/experiment-runs.d.ts +61 -2
  457. package/lib/orchestrator/experiment-runs.d.ts.map +1 -1
  458. package/lib/orchestrator/experiment-runs.js +13 -5
  459. package/lib/orchestrator/experiment-runs.js.map +1 -1
  460. package/lib/orchestrator/experiment-runtime-identity.d.ts +0 -16
  461. package/lib/orchestrator/experiment-runtime-identity.d.ts.map +1 -1
  462. package/lib/orchestrator/experiment-runtime-identity.js +5 -220
  463. package/lib/orchestrator/experiment-runtime-identity.js.map +1 -1
  464. package/lib/orchestrator/experiment-selection.d.ts.map +1 -1
  465. package/lib/orchestrator/experiment-selection.js +52 -0
  466. package/lib/orchestrator/experiment-selection.js.map +1 -1
  467. package/lib/orchestrator/experiment.d.ts +3 -3
  468. package/lib/orchestrator/experiment.d.ts.map +1 -1
  469. package/lib/orchestrator/experiment.js +26 -21
  470. package/lib/orchestrator/experiment.js.map +1 -1
  471. package/lib/orchestrator/fixture-identity.d.ts +1 -0
  472. package/lib/orchestrator/fixture-identity.d.ts.map +1 -1
  473. package/lib/orchestrator/fixture-identity.js +6 -1
  474. package/lib/orchestrator/fixture-identity.js.map +1 -1
  475. package/lib/orchestrator/fs-read.d.ts +1 -6
  476. package/lib/orchestrator/fs-read.d.ts.map +1 -1
  477. package/lib/orchestrator/fs-read.js +2 -32
  478. package/lib/orchestrator/fs-read.js.map +1 -1
  479. package/lib/orchestrator/grader-experiment.d.ts +33 -3
  480. package/lib/orchestrator/grader-experiment.d.ts.map +1 -1
  481. package/lib/orchestrator/grader-experiment.js +54 -9
  482. package/lib/orchestrator/grader-experiment.js.map +1 -1
  483. package/lib/orchestrator/grading.d.ts +2 -1
  484. package/lib/orchestrator/grading.d.ts.map +1 -1
  485. package/lib/orchestrator/grading.js +46 -31
  486. package/lib/orchestrator/grading.js.map +1 -1
  487. package/lib/orchestrator/harness-catalog-drift.d.ts +22 -0
  488. package/lib/orchestrator/harness-catalog-drift.d.ts.map +1 -0
  489. package/lib/orchestrator/harness-catalog-drift.js +90 -0
  490. package/lib/orchestrator/harness-catalog-drift.js.map +1 -0
  491. package/lib/orchestrator/harness-registry.d.ts +4 -0
  492. package/lib/orchestrator/harness-registry.d.ts.map +1 -0
  493. package/lib/orchestrator/harness-registry.js +5 -0
  494. package/lib/orchestrator/harness-registry.js.map +1 -0
  495. package/lib/orchestrator/index.d.ts +1 -1
  496. package/lib/orchestrator/index.d.ts.map +1 -1
  497. package/lib/orchestrator/index.js +1 -1
  498. package/lib/orchestrator/index.js.map +1 -1
  499. package/lib/orchestrator/list-runs.d.ts +8 -1
  500. package/lib/orchestrator/list-runs.d.ts.map +1 -1
  501. package/lib/orchestrator/list-runs.js +81 -14
  502. package/lib/orchestrator/list-runs.js.map +1 -1
  503. package/lib/orchestrator/optimization-lifecycle.d.ts.map +1 -1
  504. package/lib/orchestrator/optimization-lifecycle.js +3 -1
  505. package/lib/orchestrator/optimization-lifecycle.js.map +1 -1
  506. package/lib/orchestrator/orchestrator.d.ts +18 -2
  507. package/lib/orchestrator/orchestrator.d.ts.map +1 -1
  508. package/lib/orchestrator/orchestrator.js +50 -168
  509. package/lib/orchestrator/orchestrator.js.map +1 -1
  510. package/lib/orchestrator/paired-effects.d.ts +34 -0
  511. package/lib/orchestrator/paired-effects.d.ts.map +1 -0
  512. package/lib/orchestrator/paired-effects.js +158 -0
  513. package/lib/orchestrator/paired-effects.js.map +1 -0
  514. package/lib/orchestrator/provenance.d.ts +5 -1
  515. package/lib/orchestrator/provenance.d.ts.map +1 -1
  516. package/lib/orchestrator/provenance.js +16 -3
  517. package/lib/orchestrator/provenance.js.map +1 -1
  518. package/lib/orchestrator/regrade.d.ts +9 -1
  519. package/lib/orchestrator/regrade.d.ts.map +1 -1
  520. package/lib/orchestrator/rubric-loader.js +1 -1
  521. package/lib/orchestrator/rubric-loader.js.map +1 -1
  522. package/lib/orchestrator/run-dir.d.ts +2 -0
  523. package/lib/orchestrator/run-dir.d.ts.map +1 -1
  524. package/lib/orchestrator/run-dir.js +1 -1
  525. package/lib/orchestrator/run-dir.js.map +1 -1
  526. package/lib/orchestrator/run-execution.d.ts +3 -0
  527. package/lib/orchestrator/run-execution.d.ts.map +1 -1
  528. package/lib/orchestrator/run-execution.js +66 -12
  529. package/lib/orchestrator/run-execution.js.map +1 -1
  530. package/lib/orchestrator/run-lifecycle.d.ts +1 -0
  531. package/lib/orchestrator/run-lifecycle.d.ts.map +1 -1
  532. package/lib/orchestrator/run-lifecycle.js +3 -2
  533. package/lib/orchestrator/run-lifecycle.js.map +1 -1
  534. package/lib/orchestrator/run-workspace.d.ts +10 -0
  535. package/lib/orchestrator/run-workspace.d.ts.map +1 -0
  536. package/lib/orchestrator/run-workspace.js +58 -0
  537. package/lib/orchestrator/run-workspace.js.map +1 -0
  538. package/lib/orchestrator/suite-compare.d.ts +37 -0
  539. package/lib/orchestrator/suite-compare.d.ts.map +1 -1
  540. package/lib/orchestrator/suite-compare.js +27 -17
  541. package/lib/orchestrator/suite-compare.js.map +1 -1
  542. package/lib/orchestrator/suite-runs.d.ts +6 -1
  543. package/lib/orchestrator/suite-runs.d.ts.map +1 -1
  544. package/lib/orchestrator/suite-runs.js +14 -2
  545. package/lib/orchestrator/suite-runs.js.map +1 -1
  546. package/lib/orchestrator/suite-summary.d.ts +77 -0
  547. package/lib/orchestrator/suite-summary.d.ts.map +1 -1
  548. package/lib/orchestrator/suite-summary.js +61 -12
  549. package/lib/orchestrator/suite-summary.js.map +1 -1
  550. package/lib/previewer/artifacts.d.ts +25 -0
  551. package/lib/previewer/artifacts.d.ts.map +1 -0
  552. package/lib/previewer/artifacts.js +180 -0
  553. package/lib/previewer/artifacts.js.map +1 -0
  554. package/lib/previewer/server.d.ts +111 -0
  555. package/lib/previewer/server.d.ts.map +1 -0
  556. package/lib/previewer/server.js +558 -0
  557. package/lib/previewer/server.js.map +1 -0
  558. package/package.json +3 -3
  559. package/skills/lux-answerer/SKILL.md +2 -2
  560. package/src/answerers/claude-code/index.ts +6 -5
  561. package/src/answerers/persona/index.ts +75 -34
  562. package/src/answerers/terminal/index.ts +8 -12
  563. package/src/assertions/authoring.ts +2 -0
  564. package/src/assertions/core/command-exits-zero.ts +61 -7
  565. package/src/assertions/core/event-checks.ts +64 -3
  566. package/src/assertions/core/helpers.ts +3 -6
  567. package/src/assertions/core/index.ts +2 -0
  568. package/src/assertions/core/resource-checks.ts +38 -5
  569. package/src/assertions/rubric/index.ts +68 -13
  570. package/src/assertions/rubric/internal.ts +227 -27
  571. package/src/authoring.ts +345 -2
  572. package/src/cli/bin.ts +2 -12
  573. package/src/cli/command-runtime.ts +20 -9
  574. package/src/cli/doctor.ts +17 -0
  575. package/src/cli/harness-plan.ts +117 -0
  576. package/src/cli/init-templates.ts +238 -0
  577. package/src/cli/init.ts +53 -184
  578. package/src/cli/output-schemas.ts +106 -0
  579. package/src/cli/program.ts +691 -721
  580. package/src/cli/render/reporter.ts +50 -8
  581. package/src/cli/run-options.ts +132 -25
  582. package/src/cli/view.ts +129 -0
  583. package/src/container/driver.ts +211 -0
  584. package/src/container/protocol.ts +48 -0
  585. package/src/container/resources.ts +153 -0
  586. package/src/container/worker.ts +107 -0
  587. package/src/core/action-events.ts +59 -0
  588. package/src/core/agent-activity.ts +236 -0
  589. package/src/core/agent-environment.ts +64 -0
  590. package/src/core/agent-events.ts +17 -0
  591. package/src/core/annotation.ts +2 -1
  592. package/src/core/answerer.ts +9 -0
  593. package/src/core/artifact-evidence.ts +47 -4
  594. package/src/core/assertion.ts +21 -1
  595. package/src/core/case.ts +21 -3
  596. package/src/core/command-verification.ts +74 -0
  597. package/src/core/driver-execution.ts +84 -0
  598. package/src/core/driver-metrics.ts +51 -0
  599. package/src/core/driver.ts +76 -0
  600. package/src/core/duration.ts +52 -0
  601. package/src/core/environment.ts +22 -0
  602. package/src/core/evaluation-clusters.ts +35 -0
  603. package/src/core/events.ts +14 -2
  604. package/src/core/experiment.ts +64 -24
  605. package/src/core/file-tree.ts +35 -0
  606. package/src/core/global-id.ts +26 -0
  607. package/src/core/harness-catalog.ts +100 -0
  608. package/src/core/harness-catalogs/antigravity.ts +24 -0
  609. package/src/core/harness-catalogs/claude-code.ts +25 -0
  610. package/src/core/harness-catalogs/codex.ts +27 -0
  611. package/src/core/harness-catalogs/copilot.ts +46 -0
  612. package/src/core/harness-catalogs/cursor.ts +13 -0
  613. package/src/core/harness-catalogs/grok-build.ts +19 -0
  614. package/src/core/harness-catalogs/index.ts +31 -0
  615. package/src/core/harness-matrix.ts +316 -0
  616. package/src/core/index.ts +12 -0
  617. package/src/core/lifecycle-fixtures.ts +4 -2
  618. package/src/core/model-cli.ts +52 -23
  619. package/src/core/platform-process.ts +58 -10
  620. package/src/core/run.ts +31 -0
  621. package/src/core/usage.ts +8 -0
  622. package/src/core/wire.ts +5 -1
  623. package/src/drivers/antigravity/README.md +171 -0
  624. package/src/drivers/antigravity/artifacts.ts +37 -0
  625. package/src/drivers/antigravity/config.ts +92 -0
  626. package/src/drivers/antigravity/events.ts +290 -0
  627. package/src/drivers/antigravity/index.ts +132 -0
  628. package/src/drivers/antigravity/runtime.ts +94 -0
  629. package/src/drivers/claude-code/deferred-process.ts +118 -0
  630. package/src/drivers/claude-code/index.ts +216 -281
  631. package/src/drivers/claude-code/isolation.ts +228 -0
  632. package/src/drivers/claude-code/parse-events.ts +98 -12
  633. package/src/drivers/codex/app-events.ts +126 -16
  634. package/src/drivers/codex/config.ts +50 -8
  635. package/src/drivers/codex/exec-events.ts +258 -0
  636. package/src/drivers/codex/index.ts +144 -312
  637. package/src/drivers/copilot/README.md +60 -0
  638. package/src/drivers/copilot/config.ts +61 -0
  639. package/src/drivers/copilot/index.ts +310 -0
  640. package/src/drivers/cursor/README.md +66 -0
  641. package/src/drivers/cursor/config.ts +31 -0
  642. package/src/drivers/cursor/index.ts +326 -0
  643. package/src/drivers/cursor/interaction.ts +74 -0
  644. package/src/drivers/grok-build/README.md +112 -0
  645. package/src/drivers/grok-build/config.ts +43 -0
  646. package/src/drivers/grok-build/index.ts +331 -0
  647. package/src/drivers/mcp/index.ts +25 -11
  648. package/src/drivers/shared/acp-events.ts +94 -0
  649. package/src/drivers/shared/acp-interaction.ts +27 -0
  650. package/src/drivers/shared/acp-session.ts +40 -0
  651. package/src/drivers/shared/acp-transport.ts +187 -0
  652. package/src/drivers/shared/artifacts.ts +6 -2
  653. package/src/drivers/shared/experiment-behavior.ts +214 -0
  654. package/src/drivers/shared/experiment-policy.ts +195 -0
  655. package/src/drivers/subprocess/index.ts +55 -12
  656. package/src/index.ts +115 -10
  657. package/src/optimization/reflective.ts +3 -2
  658. package/src/orchestrator/annotation-loader.ts +10 -0
  659. package/src/orchestrator/annotation-store.ts +24 -3
  660. package/src/orchestrator/artifact-origin.ts +50 -0
  661. package/src/orchestrator/campaign-lifecycle.ts +5 -1
  662. package/src/orchestrator/candidates.ts +3 -2
  663. package/src/orchestrator/compare.ts +41 -6
  664. package/src/orchestrator/comparison-identity.ts +2 -0
  665. package/src/orchestrator/config.ts +20 -3
  666. package/src/orchestrator/discover.ts +8 -1
  667. package/src/orchestrator/doctor.ts +189 -28
  668. package/src/orchestrator/driver-capabilities.ts +21 -0
  669. package/src/orchestrator/driver-identity.ts +69 -0
  670. package/src/orchestrator/experiment-context.ts +3 -2
  671. package/src/orchestrator/experiment-corpus.ts +53 -37
  672. package/src/orchestrator/experiment-effects.ts +61 -0
  673. package/src/orchestrator/experiment-evaluation.ts +71 -10
  674. package/src/orchestrator/experiment-identity.ts +42 -48
  675. package/src/orchestrator/experiment-preflight.ts +27 -0
  676. package/src/orchestrator/experiment-report.ts +21 -1
  677. package/src/orchestrator/experiment-runs.ts +11 -4
  678. package/src/orchestrator/experiment-runtime-identity.ts +5 -243
  679. package/src/orchestrator/experiment-selection.ts +77 -0
  680. package/src/orchestrator/experiment.ts +29 -29
  681. package/src/orchestrator/fixture-identity.ts +9 -1
  682. package/src/orchestrator/fs-read.ts +2 -32
  683. package/src/orchestrator/grader-experiment.ts +74 -13
  684. package/src/orchestrator/grading.ts +67 -28
  685. package/src/orchestrator/harness-catalog-drift.ts +132 -0
  686. package/src/orchestrator/harness-registry.ts +12 -0
  687. package/src/orchestrator/index.ts +1 -0
  688. package/src/orchestrator/list-runs.ts +98 -13
  689. package/src/orchestrator/optimization-lifecycle.ts +4 -1
  690. package/src/orchestrator/orchestrator.ts +92 -216
  691. package/src/orchestrator/paired-effects.ts +209 -0
  692. package/src/orchestrator/provenance.ts +23 -3
  693. package/src/orchestrator/rubric-loader.ts +1 -1
  694. package/src/orchestrator/run-dir.ts +8 -1
  695. package/src/orchestrator/run-execution.ts +77 -11
  696. package/src/orchestrator/run-lifecycle.ts +4 -2
  697. package/src/orchestrator/run-workspace.ts +62 -0
  698. package/src/orchestrator/suite-compare.ts +29 -19
  699. package/src/orchestrator/suite-runs.ts +13 -2
  700. package/src/orchestrator/suite-summary.ts +122 -13
  701. package/src/previewer/artifacts.ts +221 -0
  702. package/src/previewer/server.ts +787 -0
  703. package/viewer/app.css +2079 -0
  704. package/viewer/app.js +627 -0
  705. package/viewer/artifact-view.js +48 -0
  706. package/viewer/charts.js +86 -0
  707. package/viewer/detail.js +1237 -0
  708. package/viewer/format.js +129 -0
  709. package/viewer/index.html +151 -0
  710. package/lib/answerers/scripted/index.d.ts +0 -8
  711. package/lib/answerers/scripted/index.d.ts.map +0 -1
  712. package/lib/answerers/scripted/index.js +0 -163
  713. package/lib/answerers/scripted/index.js.map +0 -1
  714. package/lib/cli/skills.d.ts +0 -38
  715. package/lib/cli/skills.d.ts.map +0 -1
  716. package/lib/cli/skills.js +0 -84
  717. package/lib/cli/skills.js.map +0 -1
  718. package/lib/drivers/shared/secure-copy.d.ts.map +0 -1
  719. package/lib/drivers/shared/secure-copy.js.map +0 -1
  720. package/skills/lux/SKILL.md +0 -85
  721. package/skills/lux/references/cli.md +0 -102
  722. package/skills/lux/references/eval-authoring.md +0 -105
  723. package/src/answerers/scripted/index.ts +0 -186
  724. package/src/cli/skills.ts +0 -129
  725. /package/lib/{drivers/shared → core}/secure-copy.d.ts +0 -0
  726. /package/lib/{drivers/shared → core}/secure-copy.js +0 -0
  727. /package/src/{drivers/shared → core}/secure-copy.ts +0 -0
@@ -0,0 +1,1237 @@
1
+ const assertionPresentation = (assertion) => {
2
+ if (assertion.passed === null) return { className: " informational", mark: "ℹ", tone: "" };
3
+ if (assertion.passed) return { className: " passed", mark: "✓", tone: "tone-positive" };
4
+ return { className: "", mark: "×", tone: "tone-negative" };
5
+ };
6
+
7
+ import { comparisonBars, compositionFigure, quantizedBar } from "./charts.js";
8
+ import {
9
+ badge,
10
+ compactNumber,
11
+ detailEmpty,
12
+ duration,
13
+ escapeHtml,
14
+ finite,
15
+ fullDate,
16
+ metricName,
17
+ metricValue,
18
+ percentage,
19
+ signedDuration,
20
+ signedPercentage,
21
+ statusLabel,
22
+ toneForDelta,
23
+ totalTokens,
24
+ } from "./format.js";
25
+
26
+ const json = (value) => {
27
+ if (value === undefined) return "";
28
+ try {
29
+ return JSON.stringify(value, null, 2);
30
+ } catch {
31
+ return String(value);
32
+ }
33
+ };
34
+
35
+ const eventContent = (event) => {
36
+ if (event.type === "lifecycle") return [event.stage, "Run lifecycle"];
37
+ if (event.type === "ready") {
38
+ return [
39
+ `Agent ready · ${event.agentId}`,
40
+ event.model ? `Model ${event.model}` : "Driver ready",
41
+ ];
42
+ }
43
+ if (event.type === "interrupt") {
44
+ return [
45
+ `Human-in-the-loop · ${event.kind}`,
46
+ event.question ?? event.request ?? json(event.payload),
47
+ ];
48
+ }
49
+ if (event.type === "responded") {
50
+ let answer = event.text ?? null;
51
+ if (!answer && typeof event.approved === "boolean") {
52
+ answer = event.approved ? "Approved" : "Denied";
53
+ }
54
+ return [`Answerer · ${event.kind}`, answer ?? json(event.payload)];
55
+ }
56
+ if (event.type === "progress") {
57
+ const payload = event.payload ?? {};
58
+ const actor = event.agent?.role === "subagent" ? ` · Subagent ${event.agent.id}` : "";
59
+ if (event.kind === "agent-spawn") {
60
+ const spawned = payload.agent ?? {};
61
+ return [
62
+ `Subagent spawned · ${spawned.name ?? spawned.id ?? "unknown"}`,
63
+ [spawned.id, spawned.model, payload.prompt].filter(Boolean).join(" · "),
64
+ ];
65
+ }
66
+ if (event.kind === "agent-completed") {
67
+ return [
68
+ `Subagent ${payload.status ?? "completed"} · ${payload.agentId ?? "unknown"}`,
69
+ "Agent lifecycle",
70
+ ];
71
+ }
72
+ if (event.kind === "action") {
73
+ return [
74
+ `Action${actor} · ${payload.name ?? "unknown"}`,
75
+ payload.category ? `Category: ${payload.category}` : json(payload),
76
+ ];
77
+ }
78
+ const name = event.kind === "tool-call" ? payload.name : event.kind;
79
+ return [`Progress${actor} · ${name ?? event.kind}`, json(payload)];
80
+ }
81
+ if (event.type === "error") return [`Error · ${event.exitReason}`, event.reason];
82
+ return ["Completed", event.summary ? json(event.summary) : "Driver finished"];
83
+ };
84
+
85
+ const eventTime = (event, startedAt) => {
86
+ if (typeof event.ts !== "number") return "setup";
87
+ if (event.ts > 1_000_000_000_000) return fullDate(event.ts);
88
+ const start = Date.parse(startedAt ?? "");
89
+ if (!Number.isFinite(start)) return duration(event.ts);
90
+ return `+${duration(Math.max(0, event.ts - start))}`;
91
+ };
92
+
93
+ const assertionMeta = (assertion) =>
94
+ [
95
+ assertion.type,
96
+ `threshold ${finite(assertion.threshold)}`,
97
+ `weight ${finite(assertion.weight)}`,
98
+ duration(assertion.durationMs),
99
+ assertion.usage ? `${compactNumber(totalTokens(assertion.usage))} judge tokens` : null,
100
+ ]
101
+ .filter(Boolean)
102
+ .join(" · ");
103
+
104
+ const assertionDiagnostics = (assertion) => {
105
+ const rows = [
106
+ assertion.comment ? `<p class="assertion-comment">${escapeHtml(assertion.comment)}</p>` : "",
107
+ assertion.errorMessage
108
+ ? `<p class="diagnostic-error">${escapeHtml(assertion.errorMessage)}</p>`
109
+ : "",
110
+ assertion.detail !== undefined
111
+ ? `<details class="diagnostic"><summary>Structured detail</summary><pre>${escapeHtml(
112
+ json(assertion.detail),
113
+ )}</pre></details>`
114
+ : "",
115
+ ];
116
+ return rows.join("");
117
+ };
118
+
119
+ const inferredMetricUnit = (name, unit) => {
120
+ if (unit) return unit;
121
+ if (name.endsWith("Ms")) return "milliseconds";
122
+ if (name.endsWith(".bytes")) return "bytes";
123
+ if (name.endsWith(".usd")) return "usd";
124
+ if (name.endsWith("Rate")) return "ratio";
125
+ if (
126
+ name.includes("tokens") ||
127
+ /(?:^|\.)(?:input|output|total|cacheRead|cacheCreation)$/.test(name)
128
+ ) {
129
+ return "tokens";
130
+ }
131
+ return "number";
132
+ };
133
+
134
+ const driverMetricStats = (metrics) =>
135
+ Object.entries(metrics ?? {})
136
+ .map(
137
+ ([name, metric]) =>
138
+ `<div><dt>${escapeHtml(metricName(name, metric.label))}</dt><dd>${escapeHtml(
139
+ metricValue(metric.value, metric.unit),
140
+ )}</dd><small>${escapeHtml(name)}</small></div>`,
141
+ )
142
+ .join("");
143
+
144
+ const agentActivityCards = (activities) =>
145
+ (activities ?? [])
146
+ .filter((activity) => activity.agent?.role === "subagent")
147
+ .map((activity, index) => {
148
+ const agent = activity.agent;
149
+ const actions = activity.actions ?? {};
150
+ const tools = activity.toolCalls ?? {};
151
+ const label = agent.name ?? `Subagent ${index + 1}`;
152
+ return `<article class="agent-card">
153
+ <header><div><strong>${escapeHtml(label)}</strong><small>${escapeHtml(agent.id)}</small></div><span>${escapeHtml(
154
+ `${activity.status ?? "unknown"} · ${duration(activity.durationMs)}`,
155
+ )}</span></header>
156
+ <dl>
157
+ <div><dt>Events</dt><dd>${compactNumber(activity.eventCount)}</dd></div>
158
+ <div><dt>Tokens</dt><dd>${compactNumber(activity.usage?.total)}</dd></div>
159
+ <div><dt>Tool calls</dt><dd>${compactNumber(tools.total)}</dd></div>
160
+ <div><dt>Succeeded</dt><dd>${compactNumber(tools.succeeded)}</dd></div>
161
+ <div><dt>Failed</dt><dd>${compactNumber(tools.failed)}</dd></div>
162
+ <div><dt>Actions</dt><dd>${compactNumber(actions.total)}</dd></div>
163
+ <div><dt>Mutations</dt><dd>${compactNumber(actions.mutations)}</dd></div>
164
+ <div><dt>First action</dt><dd>${escapeHtml(duration(actions.timeToFirstActionMs))}</dd></div>
165
+ <div><dt>First mutation</dt><dd>${escapeHtml(duration(actions.timeToFirstMutationMs))}</dd></div>
166
+ </dl>
167
+ <small class="agent-parent">Spawn ${escapeHtml(agent.spawnToolUseId ?? "unknown")} · parent ${escapeHtml(
168
+ agent.parentId ?? "unknown",
169
+ )}</small>
170
+ </article>`;
171
+ })
172
+ .join("");
173
+
174
+ export const renderRunDetail = (item, detail) => {
175
+ const metadata = detail.metadata;
176
+ const grading = detail.grading;
177
+ const assertions = grading?.assertions ?? [];
178
+ const events = detail.events ?? [];
179
+ const artifacts = detail.artifacts ?? [];
180
+ const reportedAgentTokens = totalTokens(metadata?.usage?.agent);
181
+ const agentTokens =
182
+ metadata?.agentUsageAvailability === "reported" ||
183
+ (metadata?.agentUsageAvailability === undefined && reportedAgentTokens > 0)
184
+ ? reportedAgentTokens
185
+ : undefined;
186
+ const answererTokens = totalTokens(metadata?.usage?.answerer);
187
+ const gradingTokens = totalTokens(metadata?.usage?.grading);
188
+ const harnessTokens = answererTokens + gradingTokens;
189
+ const phase = metadata?.phaseDurationMs;
190
+ const actionTiming = metadata?.actionTiming;
191
+ const prompt = detail.case?.prompt;
192
+ const conditions = metadata?.provenance?.environment;
193
+ const host = metadata?.provenance?.runtime?.host;
194
+ const driverMetrics = driverMetricStats(metadata?.driverMetrics);
195
+ const subagents = agentActivityCards(metadata?.agentActivity);
196
+ return `
197
+ <header class="detail-header">
198
+ <div class="detail-kicker">
199
+ ${badge(item.status)}
200
+ <span class="detail-path">${escapeHtml(item.path)}</span>
201
+ </div>
202
+ <div class="detail-title-row">
203
+ <div>
204
+ <h2>${escapeHtml(detail.case?.id ?? item.caseId)}</h2>
205
+ <p class="detail-subtitle">${escapeHtml(
206
+ [metadata?.driver?.name, metadata?.driver?.model, fullDate(metadata?.startedAt)]
207
+ .filter(Boolean)
208
+ .join(" · "),
209
+ )}</p>
210
+ </div>
211
+ <div class="score-block">
212
+ <strong>${escapeHtml(percentage(grading?.caseScore))}</strong>
213
+ <small>case score</small>
214
+ </div>
215
+ </div>
216
+ </header>
217
+ <div class="detail-body">
218
+ ${
219
+ grading?.incomplete
220
+ ? `<div class="attention-banner">Incomplete ${escapeHtml(
221
+ grading.incomplete.phase ?? "run",
222
+ )}: ${escapeHtml(grading.incomplete.exitReason)}</div>`
223
+ : ""
224
+ }
225
+ <dl class="detail-stats">
226
+ <div><dt>Duration</dt><dd>${escapeHtml(duration(item.durationMs))}</dd></div>
227
+ <div><dt>Agent tokens</dt><dd>${escapeHtml(compactNumber(agentTokens))}</dd></div>
228
+ <div><dt>Harness tokens</dt><dd>${escapeHtml(compactNumber(harnessTokens))}</dd></div>
229
+ <div><dt>Artifacts</dt><dd>${artifacts.length}</dd></div>
230
+ <div><dt>First action</dt><dd>${escapeHtml(duration(actionTiming?.timeToFirstActionMs))}</dd></div>
231
+ <div><dt>First mutation</dt><dd>${escapeHtml(duration(actionTiming?.timeToFirstMutationMs))}</dd></div>
232
+ <div><dt>First durable mutation</dt><dd>${escapeHtml(
233
+ duration(actionTiming?.timeToFirstDurableMutationMs),
234
+ )}</dd></div>
235
+ </dl>
236
+ ${
237
+ conditions
238
+ ? `<section class="detail-section"><h3>Execution conditions</h3>
239
+ <p>Lux concurrency: ${escapeHtml(conditions.concurrency)}${host ? ` · Host: ${escapeHtml(host.availableParallelism)} available CPUs, ${escapeHtml(metricValue(host.totalMemoryBytes, "bytes"))} memory` : ""}</p>
240
+ ${conditions.declared ? `<details><summary>Declared environment (not enforced by Lux)</summary><pre>${escapeHtml(JSON.stringify(conditions.declared, null, 2))}</pre></details>` : ""}
241
+ </section>`
242
+ : ""
243
+ }
244
+ ${
245
+ driverMetrics
246
+ ? `<section class="detail-section"><h3>Driver metrics <span>reported by ${escapeHtml(
247
+ metadata?.driver?.name ?? "driver",
248
+ )}</span></h3><dl class="detail-stats driver-metrics">${driverMetrics}</dl></section>`
249
+ : ""
250
+ }
251
+ ${
252
+ subagents
253
+ ? `<section class="detail-section"><h3>Subagent activity <span>${
254
+ metadata.agentActivity.filter((activity) => activity.agent.role === "subagent").length
255
+ } spawned</span></h3><div class="agent-grid">${subagents}</div></section>`
256
+ : ""
257
+ }
258
+ <div class="evidence-visual-grid">
259
+ ${compositionFigure(
260
+ "Judgment outcome",
261
+ [
262
+ {
263
+ key: "passed",
264
+ label: "Passed",
265
+ value: assertions.filter((assertion) => assertion.passed).length,
266
+ },
267
+ {
268
+ key: "regressed",
269
+ label: "Failed",
270
+ value: assertions.filter((assertion) => assertion.passed === false).length,
271
+ },
272
+ ],
273
+ `${assertions.filter((assertion) => assertion.passed).length} of ${
274
+ assertions.filter((assertion) => assertion.passed !== null).length
275
+ } assertions passed`,
276
+ )}
277
+ ${
278
+ agentTokens + answererTokens + gradingTokens > 0
279
+ ? compositionFigure(
280
+ "Token composition",
281
+ [
282
+ { key: "agent", label: "Agent", value: agentTokens },
283
+ { key: "answerer", label: "Answerer", value: answererTokens },
284
+ { key: "grading", label: "Grading", value: gradingTokens },
285
+ ],
286
+ `${agentTokens ?? "unavailable"} agent, ${answererTokens} answerer, ${gradingTokens} grading tokens`,
287
+ )
288
+ : ""
289
+ }
290
+ ${
291
+ phase
292
+ ? compositionFigure(
293
+ "Runtime composition",
294
+ [
295
+ { key: "agent", label: "Agent", value: phase.agent },
296
+ { key: "collect", label: "Collect", value: phase.collect },
297
+ { key: "grading", label: "Grading", value: phase.grading },
298
+ ],
299
+ `${duration(phase.agent)} agent, ${duration(phase.collect)} collection, ${duration(
300
+ phase.grading,
301
+ )} grading`,
302
+ )
303
+ : ""
304
+ }
305
+ </div>
306
+ ${
307
+ prompt
308
+ ? `<section class="detail-section"><h3>Case prompt</h3><div class="prompt-block">${escapeHtml(
309
+ prompt,
310
+ )}</div></section>`
311
+ : ""
312
+ }
313
+ <section class="detail-section">
314
+ <h3>Judgment <span>${assertions.filter((entry) => entry.passed).length}/${assertions.filter((entry) => entry.passed !== null).length} passed</span></h3>
315
+ <div class="assertion-list">
316
+ ${
317
+ assertions.length
318
+ ? assertions
319
+ .map(
320
+ (assertion) => `
321
+ <article class="assertion${assertionPresentation(assertion).className}">
322
+ <span class="assertion-mark">${assertionPresentation(assertion).mark}</span>
323
+ <span class="assertion-content">
324
+ <strong>${escapeHtml(assertion.name)}</strong>
325
+ <small>${escapeHtml(assertionMeta(assertion))}</small>
326
+ ${quantizedBar(
327
+ assertion.score,
328
+ assertionPresentation(assertion).tone,
329
+ `${assertion.name} scored ${percentage(
330
+ assertion.score,
331
+ )}${assertion.passed === null ? " (informational)" : `; threshold ${percentage(assertion.threshold)}`}`,
332
+ assertion.passed === null ? null : assertion.threshold,
333
+ )}
334
+ ${assertionDiagnostics(assertion)}
335
+ </span>
336
+ <span class="assertion-score">${escapeHtml(
337
+ percentage(assertion.score),
338
+ )}</span>
339
+ </article>`,
340
+ )
341
+ .join("")
342
+ : '<div class="artifact"><span>No grading assertions recorded</span><small>ungraded</small></div>'
343
+ }
344
+ </div>
345
+ </section>
346
+ <section class="detail-section">
347
+ <h3>Trace <span>${events.length} events</span></h3>
348
+ <div class="trace-list">
349
+ ${
350
+ events.length
351
+ ? events
352
+ .map((event) => {
353
+ const [title, content] = eventContent(event);
354
+ return `<article class="trace-item">
355
+ <time>${escapeHtml(eventTime(event, metadata?.startedAt))}</time>
356
+ <div><strong>${escapeHtml(title)}</strong><p>${escapeHtml(content)}</p></div>
357
+ </article>`;
358
+ })
359
+ .join("")
360
+ : '<div class="artifact"><span>No trace events recorded</span><small>empty</small></div>'
361
+ }
362
+ </div>
363
+ </section>
364
+ <section class="detail-section">
365
+ <h3>Artifacts <span>${artifacts.length} files</span></h3>
366
+ <div class="artifact-list">
367
+ ${
368
+ artifacts.length
369
+ ? artifacts
370
+ .map(
371
+ (artifact) => `<button class="artifact artifact-button" type="button"
372
+ data-artifact="${escapeHtml(artifact)}">
373
+ <span>${escapeHtml(artifact)}</span><small>preview →</small>
374
+ </button>`,
375
+ )
376
+ .join("")
377
+ : '<div class="artifact"><span>No captured artifacts</span><small>empty</small></div>'
378
+ }
379
+ </div>
380
+ </section>
381
+ </div>`;
382
+ };
383
+
384
+ const suiteItemStatus = (item) => {
385
+ if (item.status !== "completed") return item.status;
386
+ if (item.exitReason !== "done" || item.casePassed === false) return "failed";
387
+ if (item.casePassed === true) return "passed";
388
+ return "ungraded";
389
+ };
390
+
391
+ const metricSummaryRows = (metrics) =>
392
+ Object.entries(metrics ?? {})
393
+ .map(
394
+ ([name, summary]) => `<tr>
395
+ <td>${escapeHtml(name)}</td>
396
+ <td>${escapeHtml(metricValue(summary.mean, inferredMetricUnit(name, summary.unit)))}</td>
397
+ <td>${escapeHtml(metricValue(summary.median, inferredMetricUnit(name, summary.unit)))}</td>
398
+ <td>${escapeHtml(metricValue(summary.p90, inferredMetricUnit(name, summary.unit)))}</td>
399
+ <td>${summary.n}</td>
400
+ </tr>`,
401
+ )
402
+ .join("");
403
+
404
+ export const renderSuiteDetail = (item, payload) => {
405
+ const summary = payload?.summary;
406
+ const totals = summary?.totals;
407
+ const metrics = metricSummaryRows(summary?.metrics);
408
+ return `
409
+ <header class="detail-header">
410
+ <div class="detail-kicker">${badge(item.status)}<span class="detail-path">${escapeHtml(
411
+ item.path,
412
+ )}</span></div>
413
+ <div class="detail-title-row">
414
+ <div><h2>${escapeHtml(item.suiteId)}</h2><p class="detail-subtitle">${escapeHtml(
415
+ `${item.profileId ?? "default profile"} · ${fullDate(item.startedAt)}`,
416
+ )}</p></div>
417
+ <div class="score-block"><strong>${escapeHtml(
418
+ percentage(totals?.passRate),
419
+ )}</strong><small>${totals?.passed ?? 0}/${totals?.attempts ?? item.items.length} cases passed</small></div>
420
+ </div>
421
+ </header>
422
+ <div class="detail-body">
423
+ <dl class="detail-stats">
424
+ <div><dt>Attempts</dt><dd>${totals?.attempts ?? item.items.length}</dd></div>
425
+ <div><dt>Concurrency</dt><dd>${item.concurrency}</dd></div>
426
+ <div><dt>Failed</dt><dd>${totals?.failed ?? 0}</dd></div>
427
+ <div><dt>Pending</dt><dd>${totals?.pending ?? 0}</dd></div>
428
+ </dl>
429
+ ${compositionFigure(
430
+ "Suite outcomes",
431
+ [
432
+ { key: "passed", label: "Passed", value: totals?.passed ?? 0 },
433
+ { key: "regressed", label: "Failed", value: totals?.failed ?? 0 },
434
+ { key: "ungraded", label: "Ungraded", value: totals?.ungraded ?? 0 },
435
+ { key: "pending", label: "Pending", value: totals?.pending ?? 0 },
436
+ ],
437
+ `${totals?.passed ?? 0} passed, ${totals?.failed ?? 0} failed, ${
438
+ totals?.ungraded ?? 0
439
+ } ungraded, ${totals?.pending ?? 0} pending`,
440
+ )}
441
+ ${
442
+ metrics
443
+ ? `<section class="detail-section">
444
+ <h3>Aggregate metrics <span>mean / median / p90</span></h3>
445
+ <div class="matrix-wrap"><table class="matrix">
446
+ <thead><tr><th>Metric</th><th>Mean</th><th>Median</th><th>P90</th><th>N</th></tr></thead>
447
+ <tbody>${metrics}</tbody>
448
+ </table></div>
449
+ </section>`
450
+ : ""
451
+ }
452
+ <section class="detail-section">
453
+ <h3>Case attempts <span>${item.items.length} total</span></h3>
454
+ <div class="suite-list">
455
+ ${item.items
456
+ .map((entry) => {
457
+ const content = `<span>${escapeHtml(entry.caseId)}</span>
458
+ <small>${escapeHtml(statusLabel(suiteItemStatus(entry)))}</small>`;
459
+ return entry.runId
460
+ ? `<button class="suite-item suite-button" type="button" data-open-run="${escapeHtml(
461
+ entry.runId,
462
+ )}">${content}</button>`
463
+ : `<div class="suite-item">${content}</div>`;
464
+ })
465
+ .join("")}
466
+ </div>
467
+ </section>
468
+ </div>`;
469
+ };
470
+
471
+ const candidateById = (payload, id) => payload.candidates.find((candidate) => candidate.id === id);
472
+
473
+ const baselineCandidate = (payload) =>
474
+ candidateById(payload, "baseline") ??
475
+ payload.candidates.find((candidate) => candidate.parentIds.length === 0) ??
476
+ payload.candidates[0] ??
477
+ null;
478
+
479
+ const evaluationKey = (evaluation) => `${evaluation.profileId}/${evaluation.split}`;
480
+
481
+ const caseDeltaRows = (baseline, target) => {
482
+ const cases = new Set([
483
+ ...Object.keys(baseline?.casePassRates ?? {}),
484
+ ...Object.keys(target?.casePassRates ?? {}),
485
+ ]);
486
+ return [...cases]
487
+ .sort()
488
+ .map((caseId) => {
489
+ const before = baseline?.casePassRates?.[caseId];
490
+ const after = target?.casePassRates?.[caseId];
491
+ const delta = typeof before === "number" && typeof after === "number" ? after - before : null;
492
+ return `<tr>
493
+ <td>${escapeHtml(caseId)}</td>
494
+ <td class="bar-table-cell">${quantizedBar(
495
+ before,
496
+ "tone-before",
497
+ `${caseId} baseline pass rate ${percentage(before)}`,
498
+ )}<small>${escapeHtml(percentage(before))}</small></td>
499
+ <td class="bar-table-cell">${quantizedBar(
500
+ after,
501
+ "tone-after",
502
+ `${caseId} candidate pass rate ${percentage(after)}`,
503
+ )}<small>${escapeHtml(percentage(after))}</small></td>
504
+ <td class="${toneForDelta(delta)}">${escapeHtml(signedPercentage(delta))}</td>
505
+ </tr>`;
506
+ })
507
+ .join("");
508
+ };
509
+
510
+ const evaluationMetricComparisons = (before, after) => {
511
+ const definitions = { ...(before?.metricDefinitions ?? {}), ...(after?.metricDefinitions ?? {}) };
512
+ return Object.keys(definitions)
513
+ .sort()
514
+ .map((name) => {
515
+ const definition = definitions[name];
516
+ const a = before?.metrics?.[name];
517
+ const b = after?.metrics?.[name];
518
+ const delta = typeof a === "number" && typeof b === "number" ? b - a : null;
519
+ const tone =
520
+ definition.better === "neutral"
521
+ ? "metric-neutral"
522
+ : toneForDelta(delta, definition.better === "lower");
523
+ return comparisonBars({
524
+ title: metricName(name, definition.label),
525
+ before: a,
526
+ after: b,
527
+ beforeText: metricValue(a, definition.unit),
528
+ afterText: metricValue(b, definition.unit),
529
+ deltaText: signedMetricValue(delta, definition.unit),
530
+ deltaTone: tone,
531
+ });
532
+ })
533
+ .join("");
534
+ };
535
+
536
+ const experimentScopes = (baselineEvaluations, targetEvaluations) => {
537
+ const baselineByKey = new Map(baselineEvaluations.map((entry) => [evaluationKey(entry), entry]));
538
+ const targetByKey = new Map(targetEvaluations.map((entry) => [evaluationKey(entry), entry]));
539
+ const keys = new Set([...baselineByKey.keys(), ...targetByKey.keys()]);
540
+ return [...keys]
541
+ .sort()
542
+ .map((key) => {
543
+ const before = baselineByKey.get(key);
544
+ const after = targetByKey.get(key);
545
+ const delta =
546
+ typeof before?.passRate === "number" && typeof after?.passRate === "number"
547
+ ? after.passRate - before.passRate
548
+ : null;
549
+ const metrics = evaluationMetricComparisons(before, after);
550
+ return `<article class="comparison-scope">
551
+ <header>
552
+ <strong>${escapeHtml(key)}</strong>
553
+ <span class="${toneForDelta(delta)}">${escapeHtml(signedPercentage(delta))}</span>
554
+ </header>
555
+ <div class="matrix-wrap"><table class="matrix">
556
+ <thead><tr><th>Case</th><th>Baseline</th><th>Candidate</th><th>Δ</th></tr></thead>
557
+ <tbody>${caseDeltaRows(before, after) || '<tr><td colspan="4">No per-case evidence.</td></tr>'}</tbody>
558
+ </table></div>
559
+ ${metrics ? `<div class="comparison-plot-grid experiment-metrics">${metrics}</div>` : ""}
560
+ </article>`;
561
+ })
562
+ .join("");
563
+ };
564
+
565
+ const candidateLandscape = (evaluations, baselineId, targetId) => {
566
+ const rows = evaluations
567
+ .slice()
568
+ .sort((left, right) =>
569
+ `${left.profileId}/${left.split}/${left.candidateId}`.localeCompare(
570
+ `${right.profileId}/${right.split}/${right.candidateId}`,
571
+ ),
572
+ )
573
+ .map((evaluation) => {
574
+ let role = "peer";
575
+ let tone = "tone-neutral";
576
+ if (evaluation.candidateId === baselineId) {
577
+ role = "baseline";
578
+ tone = "tone-before";
579
+ } else if (evaluation.candidateId === targetId) {
580
+ role = "target";
581
+ tone = "tone-after";
582
+ }
583
+ return `<div class="landscape-row ${role}">
584
+ <span><strong>${escapeHtml(evaluation.candidateId)}</strong><small>${escapeHtml(
585
+ `${evaluation.profileId} / ${evaluation.split}`,
586
+ )}</small></span>
587
+ ${quantizedBar(
588
+ evaluation.passRate,
589
+ tone,
590
+ `${evaluation.candidateId} ${evaluation.profileId} ${evaluation.split} pass rate ${percentage(
591
+ evaluation.passRate,
592
+ )}`,
593
+ )}
594
+ <b>${escapeHtml(percentage(evaluation.passRate))}</b>
595
+ </div>`;
596
+ })
597
+ .join("");
598
+ return `<figure class="candidate-landscape">
599
+ <figcaption><span>Candidate pass-rate landscape</span><small>shared 0–100% scale</small></figcaption>
600
+ <div class="landscape-axis"><span>0</span><span>50</span><span>100%</span></div>
601
+ ${rows || '<div class="artifact"><span>No completed candidate evaluations</span><small>empty</small></div>'}
602
+ </figure>`;
603
+ };
604
+
605
+ const budgetUtilization = (decision) => {
606
+ if (!decision) return "";
607
+ const entries = [
608
+ ["Candidates", decision.spend.candidates, decision.budget.maxCandidates, "count"],
609
+ ["Metric calls", decision.spend.metricCalls, decision.budget.maxMetricCalls, "count"],
610
+ ["Judge calls", decision.spend.judgeCalls, decision.budget.maxJudgeCalls, "count"],
611
+ ["Proposal calls", decision.spend.proposalCalls, decision.budget.maxProposalCalls, "count"],
612
+ ["Tokens", decision.spend.totalTokens, decision.budget.maxTokens, "compact"],
613
+ ["Cost", decision.spend.costUsd, decision.budget.maxCostUsd, "currency"],
614
+ ["Active time", decision.spend.activeTimeMs, decision.budget.maxWallTimeMs, "duration"],
615
+ ];
616
+ const formatValue = (value, kind) => {
617
+ if (kind === "compact") return compactNumber(value);
618
+ if (kind === "currency") return `$${finite(value)}`;
619
+ if (kind === "duration") return duration(value);
620
+ return finite(value, 0);
621
+ };
622
+ return `<figure class="budget-visual">
623
+ <figcaption><span>Budget utilization</span><small>observed / ceiling</small></figcaption>
624
+ ${entries
625
+ .map(([label, value, limit, kind]) => {
626
+ const ratio = limit > 0 ? value / limit : 0;
627
+ return `<div class="budget-row">
628
+ <span>${escapeHtml(label)}</span>
629
+ ${quantizedBar(
630
+ ratio,
631
+ ratio > 0.9 ? "tone-negative" : "tone-neutral",
632
+ `${label} ${formatValue(value, kind)} of ${formatValue(limit, kind)}`,
633
+ )}
634
+ <b>${escapeHtml(`${formatValue(value, kind)} / ${formatValue(limit, kind)}`)}</b>
635
+ </div>`;
636
+ })
637
+ .join("")}
638
+ </figure>`;
639
+ };
640
+
641
+ const failedEvidence = (evaluations) =>
642
+ evaluations
643
+ .flatMap((evaluation) =>
644
+ evaluation.failedCases.map((failure) => ({
645
+ ...failure,
646
+ scope: evaluationKey(evaluation),
647
+ })),
648
+ )
649
+ .map(
650
+ (failure) => `<article class="failure-card">
651
+ <header><strong>${escapeHtml(failure.caseId)}</strong><small>${escapeHtml(
652
+ failure.scope,
653
+ )} · ${escapeHtml(percentage(failure.score))}</small></header>
654
+ ${
655
+ failure.feedback.length
656
+ ? `<ul>${failure.feedback.map((entry) => `<li>${escapeHtml(entry)}</li>`).join("")}</ul>`
657
+ : "<p>No evaluator feedback recorded.</p>"
658
+ }
659
+ </article>`,
660
+ )
661
+ .join("");
662
+
663
+ export const experimentTargetId = (payload, requested) => {
664
+ const baseline = baselineCandidate(payload);
665
+ const ids = new Set(payload.experiment.evaluations.map((entry) => entry.candidateId));
666
+ if (requested && ids.has(requested)) return requested;
667
+ const champion = payload.experiment.championCandidateId;
668
+ if (champion && ids.has(champion)) return champion;
669
+ return [...ids].find((id) => id !== baseline?.id) ?? [...ids][0] ?? null;
670
+ };
671
+
672
+ export const renderExperimentDetail = (item, payload, requestedTargetId) => {
673
+ const experiment = payload.experiment;
674
+ const decision = payload.decision;
675
+ const baseline = baselineCandidate(payload);
676
+ const targetId = experimentTargetId(payload, requestedTargetId);
677
+ const target = targetId ? candidateById(payload, targetId) : null;
678
+ const baselineEvaluations = experiment.evaluations.filter(
679
+ (evaluation) => evaluation.candidateId === baseline?.id,
680
+ );
681
+ const targetEvaluations = experiment.evaluations.filter(
682
+ (evaluation) => evaluation.candidateId === targetId,
683
+ );
684
+ const options = [...new Set(experiment.evaluations.map((entry) => entry.candidateId))]
685
+ .filter((id) => id !== baseline?.id || id === targetId)
686
+ .map(
687
+ (id) =>
688
+ `<option value="${escapeHtml(id)}"${id === targetId ? " selected" : ""}>${escapeHtml(
689
+ id,
690
+ )}</option>`,
691
+ )
692
+ .join("");
693
+ const gates = (decision?.gates ?? [])
694
+ .map(
695
+ (gate) => `<article class="gate ${gate.passed ? "passed" : "failed"}">
696
+ <span>${gate.passed ? "✓" : "×"}</span>
697
+ <div><strong>${escapeHtml(gate.metric)}</strong><small>${escapeHtml(
698
+ [gate.profile, gate.split, gate.interval, gate.note].filter(Boolean).join(" · "),
699
+ )}</small></div>
700
+ <b>${gate.values.map((value) => finite(value)).join(", ")}</b>
701
+ </article>`,
702
+ )
703
+ .join("");
704
+ const lineage = target
705
+ ? `<article class="lineage-card">
706
+ <span>${escapeHtml(target.id)}</span>
707
+ <small>from ${escapeHtml(target.parentIds.join(" + ") || "root")} · ${escapeHtml(
708
+ target.proposer,
709
+ )} · ${compactNumber(target.sourceBytes)} bytes</small>
710
+ ${target.rationale ? `<p>${escapeHtml(target.rationale)}</p>` : ""}
711
+ <div>${target.components
712
+ .map((component) => `<code>${escapeHtml(component)}</code>`)
713
+ .join("")}</div>
714
+ </article>`
715
+ : "";
716
+ return `
717
+ <header class="detail-header">
718
+ <div class="detail-kicker">${badge(item.status)}<span class="detail-path">${escapeHtml(
719
+ item.path,
720
+ )}</span></div>
721
+ <div class="detail-title-row">
722
+ <div><h2>${escapeHtml(item.campaignId)}</h2><p class="detail-subtitle">${escapeHtml(
723
+ `${item.mode} · ${fullDate(item.startedAt)} · ${item.experimentId}`,
724
+ )}</p></div>
725
+ <div class="score-block"><strong>${escapeHtml(
726
+ decision?.outcome ? statusLabel(decision.outcome) : item.candidateCount,
727
+ )}</strong><small>${decision?.outcome ? "verified decision" : "candidates"}</small></div>
728
+ </div>
729
+ </header>
730
+ <div class="detail-body">
731
+ ${
732
+ payload.integrity.verified
733
+ ? '<div class="integrity-banner">✓ Candidate ledger and accounting verified</div>'
734
+ : `<div class="attention-banner">Integrity unavailable: ${escapeHtml(
735
+ payload.integrity.errors.join(" · "),
736
+ )}</div>`
737
+ }
738
+ <dl class="detail-stats">
739
+ <div><dt>Champion</dt><dd>${escapeHtml(experiment.championCandidateId ?? "—")}</dd></div>
740
+ <div><dt>Active time</dt><dd>${escapeHtml(duration(experiment.activeTimeMs))}</dd></div>
741
+ <div><dt>Judge calls</dt><dd>${experiment.budgetUsage.judgeCalls}</dd></div>
742
+ <div><dt>Spend</dt><dd>$${experiment.budgetUsage.costUsd.toFixed(2)}</dd></div>
743
+ </dl>
744
+ <div class="experiment-visuals">
745
+ ${candidateLandscape(experiment.evaluations, baseline?.id, targetId)}
746
+ ${budgetUtilization(decision)}
747
+ </div>
748
+ ${
749
+ decision
750
+ ? `<section class="detail-section">
751
+ <h3>Promotion gates <span>${decision.gates.filter((gate) => gate.passed).length}/${decision.gates.length} passed</span></h3>
752
+ <div class="gate-list">${gates || '<div class="artifact"><span>No completed gates</span><small>incomplete</small></div>'}</div>
753
+ <div class="decision-meta">
754
+ <span>Ranking</span><strong>${escapeHtml(decision.ranking.join(" → ") || "—")}</strong>
755
+ <span>Eligible</span><strong>${escapeHtml(
756
+ decision.eligibleCandidateIds.join(", ") || "none",
757
+ )}</strong>
758
+ </div>
759
+ </section>`
760
+ : ""
761
+ }
762
+ <section class="detail-section">
763
+ <div class="section-heading">
764
+ <h3>Candidate comparison <span>baseline → target</span></h3>
765
+ ${
766
+ options
767
+ ? `<label class="candidate-picker">Target <select data-candidate-target>${options}</select></label>`
768
+ : ""
769
+ }
770
+ </div>
771
+ ${lineage}
772
+ <div class="comparison-scopes">
773
+ ${experimentScopes(baselineEvaluations, targetEvaluations) || '<div class="artifact"><span>No comparable evaluations</span><small>empty</small></div>'}
774
+ </div>
775
+ </section>
776
+ ${
777
+ failedEvidence(targetEvaluations)
778
+ ? `<section class="detail-section"><h3>Failed evidence <span>judge feedback</span></h3><div class="failure-list">${failedEvidence(
779
+ targetEvaluations,
780
+ )}</div></section>`
781
+ : ""
782
+ }
783
+ </div>`;
784
+ };
785
+
786
+ const detailAssertionMap = (detail) =>
787
+ new Map(
788
+ (detail.grading?.assertions ?? []).map((assertion) => [
789
+ assertion.id ?? assertion.name,
790
+ assertion,
791
+ ]),
792
+ );
793
+
794
+ const assertionDeltaRows = (payload) => {
795
+ const before = detailAssertionMap(payload.before);
796
+ const after = detailAssertionMap(payload.after);
797
+ return payload.comparison.grading.deltas
798
+ .map((delta) => {
799
+ const beforeAssertion = before.get(delta.id);
800
+ const afterAssertion = after.get(delta.id);
801
+ return `<tr>
802
+ <td><strong>${escapeHtml(delta.name)}</strong><small>${escapeHtml(delta.type)}</small></td>
803
+ <td>${escapeHtml(percentage(delta.before?.score))}</td>
804
+ <td>${escapeHtml(percentage(delta.after?.score))}</td>
805
+ <td><span class="delta-badge ${escapeHtml(delta.status)}">${escapeHtml(
806
+ delta.status,
807
+ )}</span></td>
808
+ <td class="rationale-cell">
809
+ <span>${escapeHtml(beforeAssertion?.comment ?? "—")}</span>
810
+ <b>→</b>
811
+ <span>${escapeHtml(afterAssertion?.comment ?? "—")}</span>
812
+ </td>
813
+ </tr>`;
814
+ })
815
+ .join("");
816
+ };
817
+
818
+ const artifactDelta = (payload) => {
819
+ const groups = [
820
+ ["changed", payload.comparison.artifactsDelta.changed],
821
+ ["added", payload.comparison.artifactsDelta.added],
822
+ ["removed", payload.comparison.artifactsDelta.removed],
823
+ ];
824
+ return groups
825
+ .flatMap(([status, paths]) =>
826
+ paths.map((path) => {
827
+ const clickable = status === "changed" || status === "added" || status === "removed";
828
+ const content = `<span>${escapeHtml(path)}</span><small>${escapeHtml(status)}${
829
+ clickable ? " →" : ""
830
+ }</small>`;
831
+ return clickable
832
+ ? `<button class="artifact artifact-button ${escapeHtml(
833
+ status,
834
+ )}" type="button" data-compare-artifact="${escapeHtml(path)}">${content}</button>`
835
+ : `<div class="artifact">${content}</div>`;
836
+ }),
837
+ )
838
+ .join("");
839
+ };
840
+
841
+ const signedMetricValue = (value, unit) => {
842
+ if (typeof value !== "number") return "—";
843
+ if (unit === "milliseconds") return signedDuration(value);
844
+ if (unit === "ratio" || unit === "score") return signedPercentage(value);
845
+ return `${value > 0 ? "+" : ""}${metricValue(value, unit)}`;
846
+ };
847
+
848
+ const driverMetricComparisons = (runDelta) => {
849
+ const before = runDelta?.driverMetrics?.a ?? {};
850
+ const after = runDelta?.driverMetrics?.b ?? {};
851
+ const names = [...new Set([...Object.keys(before), ...Object.keys(after)])].sort();
852
+ return names
853
+ .map((name) => {
854
+ const a = before[name];
855
+ const b = after[name];
856
+ const unit = a?.unit ?? b?.unit ?? "number";
857
+ const comparable = a && b && a.unit === b.unit;
858
+ const delta = comparable ? b.value - a.value : null;
859
+ const better = b?.better ?? a?.better ?? "neutral";
860
+ const tone =
861
+ better === "neutral" ? "metric-neutral" : toneForDelta(delta, better === "lower");
862
+ return comparisonBars({
863
+ title: metricName(name, b?.label ?? a?.label),
864
+ before: a?.value,
865
+ after: b?.value,
866
+ beforeText: metricValue(a?.value, unit),
867
+ afterText: metricValue(b?.value, unit),
868
+ deltaText: comparable ? signedMetricValue(delta, unit) : "not paired",
869
+ deltaTone: tone,
870
+ });
871
+ })
872
+ .join("");
873
+ };
874
+
875
+ const subagentTotals = (activities) => {
876
+ const subagents = (activities ?? []).filter((activity) => activity.agent?.role === "subagent");
877
+ return {
878
+ count: subagents.length,
879
+ toolCalls: subagents.reduce((sum, activity) => sum + (activity.toolCalls?.total ?? 0), 0),
880
+ tokens: subagents.reduce((sum, activity) => sum + (activity.usage?.total ?? 0), 0),
881
+ durationMs: subagents.reduce((sum, activity) => sum + (activity.durationMs ?? 0), 0),
882
+ };
883
+ };
884
+
885
+ export const renderRunComparison = (payload, labels) => {
886
+ const comparison = payload.comparison;
887
+ const runDelta = comparison.runDelta;
888
+ const durationDelta =
889
+ typeof runDelta?.durationMsA === "number" && typeof runDelta?.durationMsB === "number"
890
+ ? runDelta.durationMsB - runDelta.durationMsA
891
+ : null;
892
+ const mutationA = runDelta?.actionTiming?.a?.timeToFirstMutationMs;
893
+ const mutationB = runDelta?.actionTiming?.b?.timeToFirstMutationMs;
894
+ const mutationDelta =
895
+ typeof mutationA === "number" && typeof mutationB === "number" ? mutationB - mutationA : null;
896
+ const tokenA =
897
+ runDelta?.agentUsageAvailable?.a === false ? null : totalTokens(runDelta?.usage?.a?.agent);
898
+ const tokenB =
899
+ runDelta?.agentUsageAvailable?.b === false ? null : totalTokens(runDelta?.usage?.b?.agent);
900
+ const tokenDelta = tokenA === null || tokenB === null ? null : tokenB - tokenA;
901
+ const scoreDelta =
902
+ typeof comparison.grading.caseScoreA === "number" &&
903
+ typeof comparison.grading.caseScoreB === "number"
904
+ ? comparison.grading.caseScoreB - comparison.grading.caseScoreA
905
+ : null;
906
+ const driverMetrics = driverMetricComparisons(runDelta);
907
+ const subagentsA = subagentTotals(runDelta?.agentActivity?.a);
908
+ const subagentsB = subagentTotals(runDelta?.agentActivity?.b);
909
+ return `
910
+ <header class="detail-header comparison-header">
911
+ <div class="detail-kicker">${badge(
912
+ comparison.grading.regressions ? "failed" : "passed",
913
+ )}<span>run comparison</span></div>
914
+ <div class="detail-title-row">
915
+ <div><h2>${escapeHtml(labels.before)} → ${escapeHtml(
916
+ labels.after,
917
+ )}</h2><p class="detail-subtitle">Baseline A compared with candidate B</p></div>
918
+ <button class="secondary-button" type="button" data-close-compare>Done</button>
919
+ </div>
920
+ </header>
921
+ <div class="detail-body">
922
+ ${
923
+ comparison.caseMismatch.length
924
+ ? `<div class="attention-banner">Evidence identity differs: ${escapeHtml(
925
+ comparison.caseMismatch.map((entry) => entry.field).join(", "),
926
+ )}. Treat deltas as potentially incomparable.</div>`
927
+ : '<div class="integrity-banner">✓ Case, evaluator, runtime, and driver identities are comparable</div>'
928
+ }
929
+ <dl class="comparison-stats">
930
+ <div><dt>Case score</dt><dd>${escapeHtml(
931
+ `${percentage(comparison.grading.caseScoreA)} → ${percentage(
932
+ comparison.grading.caseScoreB,
933
+ )}`,
934
+ )}</dd><small class="${toneForDelta(scoreDelta)}">${escapeHtml(
935
+ signedPercentage(scoreDelta),
936
+ )}</small></div>
937
+ <div><dt>Duration</dt><dd>${escapeHtml(
938
+ `${duration(runDelta?.durationMsA)} → ${duration(runDelta?.durationMsB)}`,
939
+ )}</dd><small class="${toneForDelta(durationDelta, true)}">${escapeHtml(
940
+ signedDuration(durationDelta),
941
+ )}</small></div>
942
+ <div><dt>First mutation</dt><dd>${escapeHtml(
943
+ `${duration(mutationA)} → ${duration(mutationB)}`,
944
+ )}</dd><small class="${toneForDelta(mutationDelta, true)}">${escapeHtml(
945
+ signedDuration(mutationDelta),
946
+ )}</small></div>
947
+ <div><dt>Agent tokens</dt><dd>${escapeHtml(
948
+ `${compactNumber(tokenA)} → ${compactNumber(tokenB)}`,
949
+ )}</dd><small class="${toneForDelta(tokenDelta, true)}">${tokenDelta > 0 ? "+" : ""}${escapeHtml(
950
+ compactNumber(tokenDelta),
951
+ )}</small></div>
952
+ <div><dt>Assertions</dt><dd>${comparison.grading.improvements} ↑ / ${comparison.grading.regressions} ↓</dd><small>${comparison.grading.deltas.length} compared</small></div>
953
+ </dl>
954
+ <section class="detail-section">
955
+ <h3>Comparison profile <span>shared scales within each measure</span></h3>
956
+ <div class="comparison-plot-grid">
957
+ ${comparisonBars({
958
+ title: "Case score",
959
+ before: comparison.grading.caseScoreA,
960
+ after: comparison.grading.caseScoreB,
961
+ maximum: 1,
962
+ beforeText: percentage(comparison.grading.caseScoreA),
963
+ afterText: percentage(comparison.grading.caseScoreB),
964
+ deltaText: signedPercentage(scoreDelta),
965
+ deltaTone: toneForDelta(scoreDelta),
966
+ })}
967
+ ${comparisonBars({
968
+ title: "Duration",
969
+ before: runDelta?.durationMsA,
970
+ after: runDelta?.durationMsB,
971
+ beforeText: duration(runDelta?.durationMsA),
972
+ afterText: duration(runDelta?.durationMsB),
973
+ deltaText: signedDuration(durationDelta),
974
+ deltaTone: toneForDelta(durationDelta, true),
975
+ })}
976
+ ${comparisonBars({
977
+ title: "Time to first mutation",
978
+ before: mutationA,
979
+ after: mutationB,
980
+ beforeText: duration(mutationA),
981
+ afterText: duration(mutationB),
982
+ deltaText: signedDuration(mutationDelta),
983
+ deltaTone: toneForDelta(mutationDelta, true),
984
+ })}
985
+ ${comparisonBars({
986
+ title: "Agent tokens",
987
+ before: tokenA,
988
+ after: tokenB,
989
+ beforeText: compactNumber(tokenA),
990
+ afterText: compactNumber(tokenB),
991
+ deltaText: `${tokenDelta > 0 ? "+" : ""}${compactNumber(tokenDelta)}`,
992
+ deltaTone: toneForDelta(tokenDelta, true),
993
+ })}
994
+ ${comparisonBars({
995
+ title: "Subagents spawned",
996
+ before: subagentsA.count,
997
+ after: subagentsB.count,
998
+ beforeText: compactNumber(subagentsA.count),
999
+ afterText: compactNumber(subagentsB.count),
1000
+ deltaText: `${subagentsB.count - subagentsA.count > 0 ? "+" : ""}${compactNumber(
1001
+ subagentsB.count - subagentsA.count,
1002
+ )}`,
1003
+ deltaTone: "metric-neutral",
1004
+ })}
1005
+ ${comparisonBars({
1006
+ title: "Subagent tool calls",
1007
+ before: subagentsA.toolCalls,
1008
+ after: subagentsB.toolCalls,
1009
+ beforeText: compactNumber(subagentsA.toolCalls),
1010
+ afterText: compactNumber(subagentsB.toolCalls),
1011
+ deltaText: `${subagentsB.toolCalls - subagentsA.toolCalls > 0 ? "+" : ""}${compactNumber(
1012
+ subagentsB.toolCalls - subagentsA.toolCalls,
1013
+ )}`,
1014
+ deltaTone: "metric-neutral",
1015
+ })}
1016
+ ${comparisonBars({
1017
+ title: "Total subagent time",
1018
+ before: subagentsA.durationMs,
1019
+ after: subagentsB.durationMs,
1020
+ beforeText: duration(subagentsA.durationMs),
1021
+ afterText: duration(subagentsB.durationMs),
1022
+ deltaText: signedDuration(subagentsB.durationMs - subagentsA.durationMs),
1023
+ deltaTone: "metric-neutral",
1024
+ })}
1025
+ ${comparisonBars({
1026
+ title: "Subagent tokens",
1027
+ before: subagentsA.tokens,
1028
+ after: subagentsB.tokens,
1029
+ beforeText: compactNumber(subagentsA.tokens),
1030
+ afterText: compactNumber(subagentsB.tokens),
1031
+ deltaText: `${subagentsB.tokens - subagentsA.tokens > 0 ? "+" : ""}${compactNumber(
1032
+ subagentsB.tokens - subagentsA.tokens,
1033
+ )}`,
1034
+ deltaTone: toneForDelta(subagentsB.tokens - subagentsA.tokens, true),
1035
+ })}
1036
+ </div>
1037
+ <div class="comparison-distributions">
1038
+ ${compositionFigure(
1039
+ "Assertion movement",
1040
+ [
1041
+ { key: "improved", label: "Improved", value: comparison.grading.improvements },
1042
+ { key: "regressed", label: "Regressed", value: comparison.grading.regressions },
1043
+ {
1044
+ key: "added",
1045
+ label: "Added",
1046
+ value: comparison.grading.deltas.filter((delta) => delta.status === "added").length,
1047
+ },
1048
+ {
1049
+ key: "removed",
1050
+ label: "Removed",
1051
+ value: comparison.grading.deltas.filter((delta) => delta.status === "removed")
1052
+ .length,
1053
+ },
1054
+ {
1055
+ key: "unchanged",
1056
+ label: "Unchanged",
1057
+ value: comparison.grading.deltas.filter((delta) => delta.status === "unchanged")
1058
+ .length,
1059
+ },
1060
+ ],
1061
+ "Distribution of assertion changes",
1062
+ )}
1063
+ ${compositionFigure(
1064
+ "Artifact movement",
1065
+ [
1066
+ {
1067
+ key: "improved",
1068
+ label: "Added",
1069
+ value: comparison.artifactsDelta.added.length,
1070
+ },
1071
+ {
1072
+ key: "regressed",
1073
+ label: "Removed",
1074
+ value: comparison.artifactsDelta.removed.length,
1075
+ },
1076
+ {
1077
+ key: "changed",
1078
+ label: "Changed",
1079
+ value: comparison.artifactsDelta.changed.length,
1080
+ },
1081
+ {
1082
+ key: "unchanged",
1083
+ label: "Unchanged",
1084
+ value: comparison.artifactsDelta.unchanged.length,
1085
+ },
1086
+ ],
1087
+ "Distribution of artifact changes",
1088
+ )}
1089
+ </div>
1090
+ </section>
1091
+ ${
1092
+ driverMetrics
1093
+ ? `<section class="detail-section"><h3>Driver metric comparison <span>shared scale per metric</span></h3><div class="comparison-plot-grid">${driverMetrics}</div></section>`
1094
+ : ""
1095
+ }
1096
+ <section class="detail-section">
1097
+ <h3>Judgment delta <span>rationale side by side</span></h3>
1098
+ <div class="matrix-wrap"><table class="matrix comparison-matrix">
1099
+ <thead><tr><th>Assertion</th><th>A</th><th>B</th><th>Change</th><th>Judge rationale A → B</th></tr></thead>
1100
+ <tbody>${assertionDeltaRows(payload) || '<tr><td colspan="5">No assertions.</td></tr>'}</tbody>
1101
+ </table></div>
1102
+ </section>
1103
+ <section class="detail-section">
1104
+ <h3>Artifacts <span>${comparison.artifactsDelta.changed.length} changed · ${comparison.artifactsDelta.added.length} added · ${comparison.artifactsDelta.removed.length} removed</span></h3>
1105
+ <div class="artifact-list">${artifactDelta(payload) || '<div class="artifact"><span>No artifact changes</span><small>unchanged</small></div>'}</div>
1106
+ </section>
1107
+ </div>`;
1108
+ };
1109
+
1110
+ const metricDeltaSummary = (metrics) =>
1111
+ Object.entries(metrics)
1112
+ .map(
1113
+ ([name, summary]) =>
1114
+ `<span><b>${escapeHtml(name)}</b> ${escapeHtml(
1115
+ metricValue(summary.mean, inferredMetricUnit(name, summary.unit)),
1116
+ )}</span>`,
1117
+ )
1118
+ .join("");
1119
+
1120
+ export const renderSuiteComparison = (payload, labels) => {
1121
+ const comparison = payload.comparison;
1122
+ const movement = { improved: 0, regressed: 0, unchanged: 0, missing: 0 };
1123
+ for (const entry of comparison.cases) {
1124
+ if (!entry.before || !entry.after) movement.missing++;
1125
+ else if (entry.regressed) movement.regressed++;
1126
+ else if ((entry.passRateDelta ?? 0) > 0) movement.improved++;
1127
+ else movement.unchanged++;
1128
+ }
1129
+ return `
1130
+ <header class="detail-header comparison-header">
1131
+ <div class="detail-kicker">${badge(
1132
+ comparison.regressions || comparison.identityMismatches.length ? "failed" : "passed",
1133
+ )}<span>suite comparison</span></div>
1134
+ <div class="detail-title-row">
1135
+ <div><h2>${escapeHtml(labels.before)} → ${escapeHtml(
1136
+ labels.after,
1137
+ )}</h2><p class="detail-subtitle">Repeated evidence with paired case metrics</p></div>
1138
+ <button class="secondary-button" type="button" data-close-compare>Done</button>
1139
+ </div>
1140
+ </header>
1141
+ <div class="detail-body">
1142
+ ${
1143
+ comparison.identityMismatches.length
1144
+ ? `<div class="attention-banner">${comparison.identityMismatches.length} repetition identities differ; those pairs are not directly comparable.</div>`
1145
+ : '<div class="integrity-banner">✓ All paired repetition identities match</div>'
1146
+ }
1147
+ <dl class="comparison-stats">
1148
+ <div><dt>Pass rate</dt><dd>${escapeHtml(
1149
+ `${percentage(comparison.before.passRate)} → ${percentage(comparison.after.passRate)}`,
1150
+ )}</dd><small class="${toneForDelta(comparison.passRateDelta)}">${escapeHtml(
1151
+ signedPercentage(comparison.passRateDelta),
1152
+ )}</small></div>
1153
+ <div><dt>Attempts</dt><dd>${comparison.before.attempts} → ${comparison.after.attempts}</dd><small>paired populations</small></div>
1154
+ <div><dt>Regressions</dt><dd>${comparison.regressions}</dd><small>case-level</small></div>
1155
+ <div><dt>Identity drift</dt><dd>${comparison.identityMismatches.length}</dd><small>repetitions</small></div>
1156
+ </dl>
1157
+ <section class="detail-section">
1158
+ <h3>Outcome movement <span>shared 0–100% scale</span></h3>
1159
+ <div class="suite-outcome-visual">
1160
+ ${comparisonBars({
1161
+ title: "Suite pass rate",
1162
+ before: comparison.before.passRate,
1163
+ after: comparison.after.passRate,
1164
+ maximum: 1,
1165
+ beforeText: percentage(comparison.before.passRate),
1166
+ afterText: percentage(comparison.after.passRate),
1167
+ deltaText: signedPercentage(comparison.passRateDelta),
1168
+ deltaTone: toneForDelta(comparison.passRateDelta),
1169
+ })}
1170
+ ${compositionFigure(
1171
+ "Case movement",
1172
+ [
1173
+ {
1174
+ key: "improved",
1175
+ label: "Improved",
1176
+ value: movement.improved,
1177
+ },
1178
+ {
1179
+ key: "regressed",
1180
+ label: "Regressed",
1181
+ value: movement.regressed,
1182
+ },
1183
+ {
1184
+ key: "unchanged",
1185
+ label: "Unchanged",
1186
+ value: movement.unchanged,
1187
+ },
1188
+ {
1189
+ key: "pending",
1190
+ label: "Missing side",
1191
+ value: movement.missing,
1192
+ },
1193
+ ],
1194
+ "Distribution of per-case suite movement",
1195
+ )}
1196
+ </div>
1197
+ </section>
1198
+ <section class="detail-section">
1199
+ <h3>Per-case delta <span>after − before</span></h3>
1200
+ <div class="matrix-wrap"><table class="matrix comparison-matrix">
1201
+ <thead><tr><th>Case</th><th>Before</th><th>After</th><th>Δ</th><th>Paired metric means</th></tr></thead>
1202
+ <tbody>${comparison.cases
1203
+ .map(
1204
+ (entry) => `<tr class="${entry.regressed ? "row-regressed" : ""}">
1205
+ <td>${escapeHtml(entry.canonicalCaseId)}</td>
1206
+ <td class="bar-table-cell">${quantizedBar(
1207
+ entry.before?.passRate,
1208
+ "tone-before",
1209
+ `${entry.canonicalCaseId} baseline pass rate ${percentage(
1210
+ entry.before?.passRate,
1211
+ )}`,
1212
+ )}<small>${escapeHtml(percentage(entry.before?.passRate))} · n=${
1213
+ entry.before?.attempts ?? 0
1214
+ }</small></td>
1215
+ <td class="bar-table-cell">${quantizedBar(
1216
+ entry.after?.passRate,
1217
+ "tone-after",
1218
+ `${entry.canonicalCaseId} candidate pass rate ${percentage(
1219
+ entry.after?.passRate,
1220
+ )}`,
1221
+ )}<small>${escapeHtml(percentage(entry.after?.passRate))} · n=${
1222
+ entry.after?.attempts ?? 0
1223
+ }</small></td>
1224
+ <td class="${toneForDelta(entry.passRateDelta)}">${escapeHtml(
1225
+ signedPercentage(entry.passRateDelta),
1226
+ )}</td>
1227
+ <td class="metric-deltas">${metricDeltaSummary(entry.pairedMetricDeltas)}</td>
1228
+ </tr>`,
1229
+ )
1230
+ .join("")}</tbody>
1231
+ </table></div>
1232
+ </section>
1233
+ </div>`;
1234
+ };
1235
+
1236
+ export { renderArtifactComparison, renderArtifactPreview } from "./artifact-view.js";
1237
+ export { detailEmpty };