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

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