@xoxoai/checkmate 0.6.6 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (382) hide show
  1. package/dist/checkmate.js +13375 -0
  2. package/package.json +14 -100
  3. package/plugin/.claude-plugin/plugin.json +10 -0
  4. package/plugin/bin/checkmate.ts +3 -0
  5. package/plugin/contracts/finding.schema.json +78 -0
  6. package/plugin/contracts/gap.schema.json +56 -0
  7. package/plugin/contracts/plan-candidate.schema.json +103 -0
  8. package/plugin/contracts/plan-delta.schema.json +63 -0
  9. package/plugin/contracts/plan-patch.schema.json +116 -0
  10. package/plugin/contracts/plan-request.schema.json +108 -0
  11. package/plugin/contracts/plan.schema.json +313 -0
  12. package/plugin/contracts/profile.schema.json +154 -0
  13. package/plugin/contracts/response.schema.json +25 -0
  14. package/plugin/contracts/result.schema.json +410 -0
  15. package/plugin/dist/checkmate.js +13012 -0
  16. package/plugin/package.json +4 -0
  17. package/plugin/references/browser.md +35 -0
  18. package/plugin/references/http.md +30 -0
  19. package/plugin/references/workflow.md +122 -0
  20. package/plugin/skills/execute/SKILL.md +92 -0
  21. package/plugin/skills/plan/SKILL.md +94 -0
  22. package/plugin/skills/report/SKILL.md +28 -0
  23. package/plugin/skills/revise/SKILL.md +43 -0
  24. package/plugin/skills/setup/SKILL.md +82 -0
  25. package/plugin/skills/status/SKILL.md +20 -0
  26. package/plugin/skills/ui/SKILL.md +26 -0
  27. package/plugin/src/actor.ts +21 -0
  28. package/plugin/src/cli.ts +1133 -0
  29. package/plugin/src/contracts.ts +342 -0
  30. package/plugin/src/index.ts +52 -0
  31. package/plugin/src/plans.ts +1028 -0
  32. package/plugin/src/render.ts +544 -0
  33. package/plugin/src/runs.ts +868 -0
  34. package/plugin/src/store.ts +384 -0
  35. package/plugin/src/targets.ts +205 -0
  36. package/plugin/src/types.ts +319 -0
  37. package/plugin/src/ui/assets/client.js +501 -0
  38. package/plugin/src/ui/assets/favicon.svg +1 -0
  39. package/plugin/src/ui/assets/fonts/inter-OFL.txt +93 -0
  40. package/plugin/src/ui/assets/fonts/inter.woff2 +0 -0
  41. package/plugin/src/ui/assets/fonts/jetbrains-mono-OFL.txt +93 -0
  42. package/plugin/src/ui/assets/fonts/jetbrains-mono.woff2 +0 -0
  43. package/plugin/src/ui/assets/fonts/space-grotesk-OFL.txt +93 -0
  44. package/plugin/src/ui/assets/fonts/space-grotesk.woff2 +0 -0
  45. package/plugin/src/ui/assets/style.css +1926 -0
  46. package/plugin/src/ui/edit.ts +305 -0
  47. package/plugin/src/ui/forms.ts +209 -0
  48. package/plugin/src/ui/html.ts +130 -0
  49. package/plugin/src/ui/layout.ts +198 -0
  50. package/plugin/src/ui/lists.ts +645 -0
  51. package/plugin/src/ui/plan.ts +577 -0
  52. package/plugin/src/ui/run.ts +489 -0
  53. package/plugin/src/ui/server.ts +684 -0
  54. package/plugin/src/validators.d.ts +13 -0
  55. package/plugin/src/validators.js +1 -0
  56. package/plugin/src/views.ts +839 -0
  57. package/LICENSE +0 -21
  58. package/README.md +0 -294
  59. package/bin/checkmate.js +0 -13
  60. package/dist/ai/client.d.ts +0 -64
  61. package/dist/ai/client.d.ts.map +0 -1
  62. package/dist/ai/client.js +0 -313
  63. package/dist/ai/client.js.map +0 -1
  64. package/dist/ai/message-handler.d.ts +0 -9
  65. package/dist/ai/message-handler.d.ts.map +0 -1
  66. package/dist/ai/message-handler.js +0 -50
  67. package/dist/ai/message-handler.js.map +0 -1
  68. package/dist/ai/message-history.d.ts +0 -17
  69. package/dist/ai/message-history.d.ts.map +0 -1
  70. package/dist/ai/message-history.js +0 -45
  71. package/dist/ai/message-history.js.map +0 -1
  72. package/dist/ai/prompts.d.ts +0 -4
  73. package/dist/ai/prompts.d.ts.map +0 -1
  74. package/dist/ai/prompts.js +0 -24
  75. package/dist/ai/prompts.js.map +0 -1
  76. package/dist/ai/rate-limit-policy.d.ts +0 -9
  77. package/dist/ai/rate-limit-policy.d.ts.map +0 -1
  78. package/dist/ai/rate-limit-policy.js +0 -34
  79. package/dist/ai/rate-limit-policy.js.map +0 -1
  80. package/dist/ai/tool-response-handler.d.ts +0 -16
  81. package/dist/ai/tool-response-handler.d.ts.map +0 -1
  82. package/dist/ai/tool-response-handler.js +0 -85
  83. package/dist/ai/tool-response-handler.js.map +0 -1
  84. package/dist/ai/turn-processor.d.ts +0 -34
  85. package/dist/ai/turn-processor.d.ts.map +0 -1
  86. package/dist/ai/turn-processor.js +0 -107
  87. package/dist/ai/turn-processor.js.map +0 -1
  88. package/dist/api/allocate-run-identity.d.ts +0 -8
  89. package/dist/api/allocate-run-identity.d.ts.map +0 -1
  90. package/dist/api/allocate-run-identity.js +0 -10
  91. package/dist/api/allocate-run-identity.js.map +0 -1
  92. package/dist/api/execute-prepared-run.d.ts +0 -22
  93. package/dist/api/execute-prepared-run.d.ts.map +0 -1
  94. package/dist/api/execute-prepared-run.js +0 -184
  95. package/dist/api/execute-prepared-run.js.map +0 -1
  96. package/dist/api/index.d.ts +0 -25
  97. package/dist/api/index.d.ts.map +0 -1
  98. package/dist/api/index.js +0 -69
  99. package/dist/api/index.js.map +0 -1
  100. package/dist/api/prepare-run.d.ts +0 -44
  101. package/dist/api/prepare-run.d.ts.map +0 -1
  102. package/dist/api/prepare-run.js +0 -261
  103. package/dist/api/prepare-run.js.map +0 -1
  104. package/dist/cli/diagnostic-relay.d.ts +0 -6
  105. package/dist/cli/diagnostic-relay.d.ts.map +0 -1
  106. package/dist/cli/diagnostic-relay.js +0 -6
  107. package/dist/cli/diagnostic-relay.js.map +0 -1
  108. package/dist/cli/main.d.ts +0 -21
  109. package/dist/cli/main.d.ts.map +0 -1
  110. package/dist/cli/main.js +0 -166
  111. package/dist/cli/main.js.map +0 -1
  112. package/dist/cli/protocol.d.ts +0 -57
  113. package/dist/cli/protocol.d.ts.map +0 -1
  114. package/dist/cli/protocol.js +0 -116
  115. package/dist/cli/protocol.js.map +0 -1
  116. package/dist/cli/reconcile-result.d.ts +0 -13
  117. package/dist/cli/reconcile-result.d.ts.map +0 -1
  118. package/dist/cli/reconcile-result.js +0 -300
  119. package/dist/cli/reconcile-result.js.map +0 -1
  120. package/dist/cli/run-parent.d.ts +0 -37
  121. package/dist/cli/run-parent.d.ts.map +0 -1
  122. package/dist/cli/run-parent.js +0 -491
  123. package/dist/cli/run-parent.js.map +0 -1
  124. package/dist/cli/signals.d.ts +0 -36
  125. package/dist/cli/signals.d.ts.map +0 -1
  126. package/dist/cli/signals.js +0 -85
  127. package/dist/cli/signals.js.map +0 -1
  128. package/dist/cli/static-commands.d.ts +0 -11
  129. package/dist/cli/static-commands.d.ts.map +0 -1
  130. package/dist/cli/static-commands.js +0 -83
  131. package/dist/cli/static-commands.js.map +0 -1
  132. package/dist/cli/worker-stream-guard.d.ts +0 -20
  133. package/dist/cli/worker-stream-guard.d.ts.map +0 -1
  134. package/dist/cli/worker-stream-guard.js +0 -41
  135. package/dist/cli/worker-stream-guard.js.map +0 -1
  136. package/dist/cli/worker.d.ts +0 -3
  137. package/dist/cli/worker.d.ts.map +0 -1
  138. package/dist/cli/worker.js +0 -111
  139. package/dist/cli/worker.js.map +0 -1
  140. package/dist/config/ingestion.d.ts +0 -58
  141. package/dist/config/ingestion.d.ts.map +0 -1
  142. package/dist/config/ingestion.js +0 -283
  143. package/dist/config/ingestion.js.map +0 -1
  144. package/dist/config/manifest.d.ts +0 -20
  145. package/dist/config/manifest.d.ts.map +0 -1
  146. package/dist/config/manifest.js +0 -91
  147. package/dist/config/manifest.js.map +0 -1
  148. package/dist/config/model-egress.d.ts +0 -10
  149. package/dist/config/model-egress.d.ts.map +0 -1
  150. package/dist/config/model-egress.js +0 -62
  151. package/dist/config/model-egress.js.map +0 -1
  152. package/dist/config/package-resolution.d.ts +0 -4
  153. package/dist/config/package-resolution.d.ts.map +0 -1
  154. package/dist/config/package-resolution.js +0 -24
  155. package/dist/config/package-resolution.js.map +0 -1
  156. package/dist/config/policy.d.ts +0 -44
  157. package/dist/config/policy.d.ts.map +0 -1
  158. package/dist/config/policy.js +0 -141
  159. package/dist/config/policy.js.map +0 -1
  160. package/dist/config/record.d.ts +0 -4
  161. package/dist/config/record.d.ts.map +0 -1
  162. package/dist/config/record.js +0 -10
  163. package/dist/config/record.js.map +0 -1
  164. package/dist/config/secrets.d.ts +0 -11
  165. package/dist/config/secrets.d.ts.map +0 -1
  166. package/dist/config/secrets.js +0 -42
  167. package/dist/config/secrets.js.map +0 -1
  168. package/dist/contracts/diagnostics.d.ts +0 -4
  169. package/dist/contracts/diagnostics.d.ts.map +0 -1
  170. package/dist/contracts/diagnostics.js +0 -31
  171. package/dist/contracts/diagnostics.js.map +0 -1
  172. package/dist/contracts/serialize.d.ts +0 -2
  173. package/dist/contracts/serialize.d.ts.map +0 -1
  174. package/dist/contracts/serialize.js +0 -90
  175. package/dist/contracts/serialize.js.map +0 -1
  176. package/dist/contracts/types.d.ts +0 -316
  177. package/dist/contracts/types.d.ts.map +0 -1
  178. package/dist/contracts/types.js +0 -74
  179. package/dist/contracts/types.js.map +0 -1
  180. package/dist/contracts/validator.d.ts +0 -8
  181. package/dist/contracts/validator.d.ts.map +0 -1
  182. package/dist/contracts/validator.js +0 -71
  183. package/dist/contracts/validator.js.map +0 -1
  184. package/dist/driver.d.ts +0 -97
  185. package/dist/driver.d.ts.map +0 -1
  186. package/dist/driver.js +0 -24
  187. package/dist/driver.js.map +0 -1
  188. package/dist/drivers/descriptor.d.ts +0 -15
  189. package/dist/drivers/descriptor.d.ts.map +0 -1
  190. package/dist/drivers/descriptor.js +0 -122
  191. package/dist/drivers/descriptor.js.map +0 -1
  192. package/dist/drivers/loader.d.ts +0 -18
  193. package/dist/drivers/loader.d.ts.map +0 -1
  194. package/dist/drivers/loader.js +0 -97
  195. package/dist/drivers/loader.js.map +0 -1
  196. package/dist/drivers/web/checkmate-driver.json +0 -47
  197. package/dist/drivers/web/index.d.ts +0 -6
  198. package/dist/drivers/web/index.d.ts.map +0 -1
  199. package/dist/drivers/web/index.js +0 -27
  200. package/dist/drivers/web/index.js.map +0 -1
  201. package/dist/drivers/web/session.d.ts +0 -25
  202. package/dist/drivers/web/session.d.ts.map +0 -1
  203. package/dist/drivers/web/session.js +0 -132
  204. package/dist/drivers/web/session.js.map +0 -1
  205. package/dist/drivers/web/tools/network-request-recorder.d.ts +0 -46
  206. package/dist/drivers/web/tools/network-request-recorder.d.ts.map +0 -1
  207. package/dist/drivers/web/tools/network-request-recorder.js +0 -164
  208. package/dist/drivers/web/tools/network-request-recorder.js.map +0 -1
  209. package/dist/drivers/web/tools/screenshot-service.d.ts +0 -10
  210. package/dist/drivers/web/tools/screenshot-service.d.ts.map +0 -1
  211. package/dist/drivers/web/tools/screenshot-service.js +0 -23
  212. package/dist/drivers/web/tools/screenshot-service.js.map +0 -1
  213. package/dist/drivers/web/tools/snapshot-filter/index.d.ts +0 -4
  214. package/dist/drivers/web/tools/snapshot-filter/index.d.ts.map +0 -1
  215. package/dist/drivers/web/tools/snapshot-filter/index.js +0 -4
  216. package/dist/drivers/web/tools/snapshot-filter/index.js.map +0 -1
  217. package/dist/drivers/web/tools/snapshot-filter/semantic-scorer.d.ts +0 -15
  218. package/dist/drivers/web/tools/snapshot-filter/semantic-scorer.d.ts.map +0 -1
  219. package/dist/drivers/web/tools/snapshot-filter/semantic-scorer.js +0 -99
  220. package/dist/drivers/web/tools/snapshot-filter/semantic-scorer.js.map +0 -1
  221. package/dist/drivers/web/tools/snapshot-filter/snapshot-filter.d.ts +0 -5
  222. package/dist/drivers/web/tools/snapshot-filter/snapshot-filter.d.ts.map +0 -1
  223. package/dist/drivers/web/tools/snapshot-filter/snapshot-filter.js +0 -56
  224. package/dist/drivers/web/tools/snapshot-filter/snapshot-filter.js.map +0 -1
  225. package/dist/drivers/web/tools/snapshot-filter/tree-reconstructor.d.ts +0 -3
  226. package/dist/drivers/web/tools/snapshot-filter/tree-reconstructor.d.ts.map +0 -1
  227. package/dist/drivers/web/tools/snapshot-filter/tree-reconstructor.js +0 -85
  228. package/dist/drivers/web/tools/snapshot-filter/tree-reconstructor.js.map +0 -1
  229. package/dist/drivers/web/tools/snapshot-service.d.ts +0 -31
  230. package/dist/drivers/web/tools/snapshot-service.d.ts.map +0 -1
  231. package/dist/drivers/web/tools/snapshot-service.js +0 -96
  232. package/dist/drivers/web/tools/snapshot-service.js.map +0 -1
  233. package/dist/drivers/web/tools/tool.d.ts +0 -76
  234. package/dist/drivers/web/tools/tool.d.ts.map +0 -1
  235. package/dist/drivers/web/tools/tool.js +0 -562
  236. package/dist/drivers/web/tools/tool.js.map +0 -1
  237. package/dist/drivers/web/tools/transient-state-tracker.d.ts +0 -26
  238. package/dist/drivers/web/tools/transient-state-tracker.d.ts.map +0 -1
  239. package/dist/drivers/web/tools/transient-state-tracker.js +0 -301
  240. package/dist/drivers/web/tools/transient-state-tracker.js.map +0 -1
  241. package/dist/drivers/web/tools/types.d.ts +0 -11
  242. package/dist/drivers/web/tools/types.d.ts.map +0 -1
  243. package/dist/drivers/web/tools/types.js +0 -2
  244. package/dist/drivers/web/tools/types.js.map +0 -1
  245. package/dist/evidence/atomic-file.d.ts +0 -23
  246. package/dist/evidence/atomic-file.d.ts.map +0 -1
  247. package/dist/evidence/atomic-file.js +0 -104
  248. package/dist/evidence/atomic-file.js.map +0 -1
  249. package/dist/evidence/layout.d.ts +0 -30
  250. package/dist/evidence/layout.d.ts.map +0 -1
  251. package/dist/evidence/layout.js +0 -152
  252. package/dist/evidence/layout.js.map +0 -1
  253. package/dist/evidence/retention.d.ts +0 -5
  254. package/dist/evidence/retention.d.ts.map +0 -1
  255. package/dist/evidence/retention.js +0 -10
  256. package/dist/evidence/retention.js.map +0 -1
  257. package/dist/evidence/store.d.ts +0 -101
  258. package/dist/evidence/store.d.ts.map +0 -1
  259. package/dist/evidence/store.js +0 -375
  260. package/dist/evidence/store.js.map +0 -1
  261. package/dist/evidence/terminal-finalizer.d.ts +0 -19
  262. package/dist/evidence/terminal-finalizer.d.ts.map +0 -1
  263. package/dist/evidence/terminal-finalizer.js +0 -56
  264. package/dist/evidence/terminal-finalizer.js.map +0 -1
  265. package/dist/index.d.ts +0 -9
  266. package/dist/index.d.ts.map +0 -1
  267. package/dist/index.js +0 -12
  268. package/dist/index.js.map +0 -1
  269. package/dist/logging/invocation-logger.d.ts +0 -4
  270. package/dist/logging/invocation-logger.d.ts.map +0 -1
  271. package/dist/logging/invocation-logger.js +0 -25
  272. package/dist/logging/invocation-logger.js.map +0 -1
  273. package/dist/logging/level-logger.d.ts +0 -11
  274. package/dist/logging/level-logger.d.ts.map +0 -1
  275. package/dist/logging/level-logger.js +0 -82
  276. package/dist/logging/level-logger.js.map +0 -1
  277. package/dist/logging/types.d.ts +0 -8
  278. package/dist/logging/types.d.ts.map +0 -1
  279. package/dist/logging/types.js +0 -7
  280. package/dist/logging/types.js.map +0 -1
  281. package/dist/redaction/diagnostic-sanitizer.d.ts +0 -7
  282. package/dist/redaction/diagnostic-sanitizer.d.ts.map +0 -1
  283. package/dist/redaction/diagnostic-sanitizer.js +0 -42
  284. package/dist/redaction/diagnostic-sanitizer.js.map +0 -1
  285. package/dist/redaction/redactor.d.ts +0 -40
  286. package/dist/redaction/redactor.d.ts.map +0 -1
  287. package/dist/redaction/redactor.js +0 -176
  288. package/dist/redaction/redactor.js.map +0 -1
  289. package/dist/redaction/scrub.d.ts +0 -29
  290. package/dist/redaction/scrub.d.ts.map +0 -1
  291. package/dist/redaction/scrub.js +0 -54
  292. package/dist/redaction/scrub.js.map +0 -1
  293. package/dist/runtime/config.d.ts +0 -16
  294. package/dist/runtime/config.d.ts.map +0 -1
  295. package/dist/runtime/config.js +0 -2
  296. package/dist/runtime/config.js.map +0 -1
  297. package/dist/runtime/driver-boundary.d.ts +0 -16
  298. package/dist/runtime/driver-boundary.d.ts.map +0 -1
  299. package/dist/runtime/driver-boundary.js +0 -71
  300. package/dist/runtime/driver-boundary.js.map +0 -1
  301. package/dist/runtime/driver-session.d.ts +0 -9
  302. package/dist/runtime/driver-session.d.ts.map +0 -1
  303. package/dist/runtime/driver-session.js +0 -64
  304. package/dist/runtime/driver-session.js.map +0 -1
  305. package/dist/runtime/internal-step-evidence.d.ts +0 -30
  306. package/dist/runtime/internal-step-evidence.d.ts.map +0 -1
  307. package/dist/runtime/internal-step-evidence.js +0 -82
  308. package/dist/runtime/internal-step-evidence.js.map +0 -1
  309. package/dist/runtime/result-adapter.d.ts +0 -9
  310. package/dist/runtime/result-adapter.d.ts.map +0 -1
  311. package/dist/runtime/result-adapter.js +0 -77
  312. package/dist/runtime/result-adapter.js.map +0 -1
  313. package/dist/runtime/runner.d.ts +0 -43
  314. package/dist/runtime/runner.d.ts.map +0 -1
  315. package/dist/runtime/runner.js +0 -72
  316. package/dist/runtime/runner.js.map +0 -1
  317. package/dist/runtime/scenario-control.d.ts +0 -40
  318. package/dist/runtime/scenario-control.d.ts.map +0 -1
  319. package/dist/runtime/scenario-control.js +0 -94
  320. package/dist/runtime/scenario-control.js.map +0 -1
  321. package/dist/runtime/scenario-runner.d.ts +0 -41
  322. package/dist/runtime/scenario-runner.d.ts.map +0 -1
  323. package/dist/runtime/scenario-runner.js +0 -274
  324. package/dist/runtime/scenario-runner.js.map +0 -1
  325. package/dist/runtime/scenario-state.d.ts +0 -55
  326. package/dist/runtime/scenario-state.d.ts.map +0 -1
  327. package/dist/runtime/scenario-state.js +0 -128
  328. package/dist/runtime/scenario-state.js.map +0 -1
  329. package/dist/runtime/step-execution.d.ts +0 -27
  330. package/dist/runtime/step-execution.d.ts.map +0 -1
  331. package/dist/runtime/step-execution.js +0 -143
  332. package/dist/runtime/step-execution.js.map +0 -1
  333. package/dist/runtime/text.d.ts +0 -10
  334. package/dist/runtime/text.d.ts.map +0 -1
  335. package/dist/runtime/text.js +0 -25
  336. package/dist/runtime/text.js.map +0 -1
  337. package/dist/runtime/types.d.ts +0 -53
  338. package/dist/runtime/types.d.ts.map +0 -1
  339. package/dist/runtime/types.js +0 -2
  340. package/dist/runtime/types.js.map +0 -1
  341. package/dist/runtime/usage-tracker.d.ts +0 -37
  342. package/dist/runtime/usage-tracker.d.ts.map +0 -1
  343. package/dist/runtime/usage-tracker.js +0 -110
  344. package/dist/runtime/usage-tracker.js.map +0 -1
  345. package/dist/tools/define-agent-tool.d.ts +0 -20
  346. package/dist/tools/define-agent-tool.d.ts.map +0 -1
  347. package/dist/tools/define-agent-tool.js +0 -30
  348. package/dist/tools/define-agent-tool.js.map +0 -1
  349. package/dist/tools/dispatcher.d.ts +0 -19
  350. package/dist/tools/dispatcher.d.ts.map +0 -1
  351. package/dist/tools/dispatcher.js +0 -91
  352. package/dist/tools/dispatcher.js.map +0 -1
  353. package/dist/tools/loop-detector.d.ts +0 -27
  354. package/dist/tools/loop-detector.d.ts.map +0 -1
  355. package/dist/tools/loop-detector.js +0 -76
  356. package/dist/tools/loop-detector.js.map +0 -1
  357. package/dist/tools/registry.d.ts +0 -19
  358. package/dist/tools/registry.d.ts.map +0 -1
  359. package/dist/tools/registry.js +0 -54
  360. package/dist/tools/registry.js.map +0 -1
  361. package/dist/tools/step/result-tool.d.ts +0 -7
  362. package/dist/tools/step/result-tool.d.ts.map +0 -1
  363. package/dist/tools/step/result-tool.js +0 -34
  364. package/dist/tools/step/result-tool.js.map +0 -1
  365. package/dist/tools/tool-contract.d.ts +0 -3
  366. package/dist/tools/tool-contract.d.ts.map +0 -1
  367. package/dist/tools/tool-contract.js +0 -2
  368. package/dist/tools/tool-contract.js.map +0 -1
  369. package/dist/tools/types.d.ts +0 -210
  370. package/dist/tools/types.d.ts.map +0 -1
  371. package/dist/tools/types.js +0 -4
  372. package/dist/tools/types.js.map +0 -1
  373. package/docs/CLI.md +0 -59
  374. package/docs/CONFIGURATION.md +0 -105
  375. package/docs/DRIVERS.md +0 -79
  376. package/docs/EVIDENCE.md +0 -64
  377. package/schemas/checkmate-config.v1.json +0 -176
  378. package/schemas/describe-result.v1.json +0 -414
  379. package/schemas/driver-descriptor.v1.json +0 -66
  380. package/schemas/run-request.v1.json +0 -67
  381. package/schemas/run-result.v1.json +0 -333
  382. package/schemas/validation-result.v1.json +0 -86
@@ -0,0 +1,489 @@
1
+ import type { Store } from '../store.ts'
2
+ import type { Html } from './html.ts'
3
+ import type { Context, Page } from './layout.ts'
4
+ import type { FormState } from './forms.ts'
5
+ import { evidenceBytes, planDetail, runDetail } from '../views.ts'
6
+ import { planRevision, planRunGaps } from '../plans.ts'
7
+ import { clock, html, icon, join, pill, plural, short, time, words } from './html.ts'
8
+ import { downloads, heading } from './layout.ts'
9
+ import { emptyState, recordPanel, verdictPanel } from './forms.ts'
10
+ import { counts } from './lists.ts'
11
+
12
+ type Detail = ReturnType<typeof runDetail>
13
+ type Evidence = Detail['tests'][number]['evidence'][number]
14
+ type RunTest = Detail['tests'][number]
15
+ type Assertion = RunTest['assertions'][number]
16
+
17
+ const imageTypes = ['image/png', 'image/jpeg', 'image/webp', 'image/gif']
18
+ const textTypes = ['text/plain', 'application/json', 'text/markdown']
19
+ const inlineLimit = 64 * 1024
20
+
21
+ export function size(bytes: number) {
22
+ if (bytes < 1024) return `${bytes} B`
23
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
24
+ return `${(bytes / 1024 / 1024).toFixed(1)} MB`
25
+ }
26
+
27
+ function evidenceItem(store: Store, item: Evidence) {
28
+ const href = `/evidence/${item.id}`
29
+ const caption = html`<span class="ev-name">${item.name}</span> <span class="muted">${size(item.size)}</span>`
30
+ if (imageTypes.includes(item.mediaType))
31
+ return html`<figure class="ev ev-image">
32
+ <a href="${href}" data-view-image data-name="${item.name}">
33
+ <img src="${href}" alt="Evidence ${item.name}" loading="lazy" />
34
+ </a>
35
+ <figcaption>${caption}</figcaption>
36
+ </figure>`
37
+ if (textTypes.includes(item.mediaType) && item.size <= inlineLimit) {
38
+ const text = new TextDecoder().decode(evidenceBytes(store, item.id).bytes)
39
+ return html`<figure class="ev ev-text">
40
+ <figcaption>${caption} <a href="${href}" download="${item.name}">Download</a></figcaption>
41
+ <pre tabindex="0">${text}</pre>
42
+ </figure>`
43
+ }
44
+ return html`<a class="chip" href="${href}" download="${item.name}">↓ ${item.name} · ${size(item.size)}</a>`
45
+ }
46
+
47
+ function evidenceChips(items: Evidence[]) {
48
+ if (!items.length) return ''
49
+ return html`<p class="evidence">
50
+ ${items.map(
51
+ (item) =>
52
+ html`<a
53
+ class="chip"
54
+ href="/evidence/${item.id}"
55
+ ${imageTypes.includes(item.mediaType) ? html`data-view-image data-name="${item.name}"` : html`download="${item.name}"`}
56
+ >${item.name} · ${size(item.size)}</a
57
+ >`
58
+ )}
59
+ </p>`
60
+ }
61
+
62
+ function evidenceList(store: Store, items: Evidence[]) {
63
+ if (!items.length) return ''
64
+ return html`<div class="evidence">${items.map((item) => evidenceItem(store, item))}</div>`
65
+ }
66
+
67
+ function tone(result: string | null) {
68
+ if (result === 'failed') return 'failed'
69
+ if (result === 'inconclusive') return 'unsure'
70
+ if (result === 'not_checked') return 'warn'
71
+ return ''
72
+ }
73
+
74
+ function findingRefs(detail: Detail, reference: string) {
75
+ const findings = detail.findings.filter((finding) => finding.assertionIds.includes(reference))
76
+ if (!findings.length) return ''
77
+ return html` ·
78
+ ${join(
79
+ findings.map((finding) => html`<a href="#${finding.id}">finding ${finding.id}</a>`),
80
+ ', '
81
+ )}`
82
+ }
83
+
84
+ function assertionRow(store: Store, detail: Detail, test: RunTest, assertion: Assertion) {
85
+ const reference = `${test.testId}.${assertion.assertionId}`
86
+ if (!assertion.result)
87
+ return html`<li class="asr waiting" id="${reference}" data-key="${reference}">
88
+ <div class="asr-row">
89
+ ${icon(null)}<span class="aid">${assertion.assertionId}</span>
90
+ <div class="asr-text">
91
+ ${assertion.check}
92
+ <div class="obs">No result yet.</div>
93
+ </div>
94
+ </div>
95
+ </li>`
96
+ const open = assertion.result !== 'passed'
97
+ return html`<li
98
+ class="asr ${tone(assertion.result)}"
99
+ id="${reference}"
100
+ data-key="${reference}"
101
+ data-announce="${reference} ${words(assertion.result)}"
102
+ >
103
+ <details ${open ? html`open` : ''} data-open-key="${reference}">
104
+ <summary class="asr-row">
105
+ ${icon(assertion.result)}<span class="aid">${assertion.assertionId}</span>
106
+ <div class="asr-text">
107
+ ${assertion.check}
108
+ ${
109
+ assertion.observed
110
+ ? html`<div class="obs">
111
+ Observed: <code>${assertion.observed}</code>${findingRefs(detail, reference)}
112
+ </div>`
113
+ : ''
114
+ }
115
+ </div>
116
+ </summary>
117
+ <div class="asr-body">
118
+ ${assertion.reason ? html`<p class="reason"><strong>Reason</strong> ${assertion.reason}</p>` : ''}
119
+ <p class="muted small">${words(assertion.checkKind)} · ${words(assertion.result)}</p>
120
+ ${evidenceList(store, assertion.evidence)}
121
+ </div>
122
+ </details>
123
+ </li>`
124
+ }
125
+
126
+ function testState(test: RunTest, detail: Detail) {
127
+ if (test.outcome) return pill(test.outcome)
128
+ if (detail.state === 'finalized') return pill('not_run')
129
+ return test.assertions.some((assertion) => assertion.result)
130
+ ? pill('started', 'running')
131
+ : pill('pending', 'waiting')
132
+ }
133
+
134
+ function testCard(store: Store, detail: Detail, test: RunTest) {
135
+ const passed = test.assertions.filter((assertion) => assertion.result === 'passed').length
136
+ const recorded = test.assertions.filter((assertion) => assertion.result).length
137
+ const announce = test.outcome ? `${test.testId} closed: ${words(test.outcome)}` : `${test.testId} running`
138
+ return html`<article class="test" id="${test.testId}" data-key="test:${test.testId}" data-announce="${announce}">
139
+ <header class="test-h">
140
+ ${testState(test, detail)}
141
+ <a class="id" href="#${test.testId}">${test.testId}</a>
142
+ <h3>${test.title}</h3>
143
+ <span class="count"
144
+ >${passed} of ${test.assertions.length}
145
+ passed${
146
+ recorded < test.assertions.length ? ` · ${test.assertions.length - recorded} without result` : ''
147
+ }</span
148
+ >
149
+ </header>
150
+ ${test.reason ? html`<p class="test-note">${test.reason}</p>` : ''}
151
+ ${test.evidence.length ? html`<div class="test-ev">${evidenceList(store, test.evidence)}</div>` : ''}
152
+ <ul class="asrs">
153
+ ${test.assertions.map((assertion) => assertionRow(store, detail, test, assertion))}
154
+ </ul>
155
+ </article>`
156
+ }
157
+
158
+ function outcomeCounts(detail: Detail) {
159
+ const values: Record<string, number> = {}
160
+ for (const test of detail.tests) if (test.outcome) values[test.outcome] = (values[test.outcome] ?? 0) + 1
161
+ return values
162
+ }
163
+
164
+ function strip(detail: Detail) {
165
+ const result = detail.result
166
+ return html`<div class="strip" data-region="summary">
167
+ <div><small>Completion</small><strong>${result ? words(result.completion) : 'running'}</strong></div>
168
+ <div><small>Coverage</small><strong>${result ? words(result.coverage.status) : '–'}</strong></div>
169
+ <div><small>Tests</small><strong>${counts(outcomeCounts(detail))}</strong></div>
170
+ <div><small>Cleanup</small><strong>${result ? words(result.cleanup.status) : '–'}</strong></div>
171
+ </div>`
172
+ }
173
+
174
+ function findings(store: Store, detail: Detail) {
175
+ if (!detail.findings.length) return html`<p class="empty-line">No findings.</p>`
176
+ const evidence = detail.tests.flatMap((test) => [
177
+ ...test.evidence,
178
+ ...test.assertions.flatMap((assertion) => assertion.evidence),
179
+ ])
180
+ return html`<div class="stack">
181
+ ${detail.findings.map(
182
+ (finding) =>
183
+ html`<article class="finding" id="${finding.id}" data-key="finding:${finding.id}">
184
+ <header class="test-h">
185
+ ${pill(finding.type)} <span class="id">${finding.id}</span>
186
+ <h3>${finding.title}</h3>
187
+ <span class="count">${pill(finding.severity)}</span>
188
+ </header>
189
+ <div class="test-b">
190
+ <div class="compare">
191
+ <div>
192
+ <small>Expected</small>
193
+ <p>${finding.expected}</p>
194
+ </div>
195
+ <div>
196
+ <small>Observed</small>
197
+ <p>${finding.observed}</p>
198
+ </div>
199
+ </div>
200
+ ${
201
+ finding.reproduction.length
202
+ ? html`<h4 class="minor">Reproduction</h4>
203
+ <ol class="steps">
204
+ ${finding.reproduction.map((step) => html`<li>${step}</li>`)}
205
+ </ol>`
206
+ : ''
207
+ }
208
+ <p class="muted small">
209
+ Assertions
210
+ ${join(
211
+ finding.assertionIds.map((id) => html`<a class="mono" href="#${id}">${id}</a>`),
212
+ ', '
213
+ )}${finding.requirementIds.length ? ` · requirements ${finding.requirementIds.join(', ')}` : ''}
214
+ </p>
215
+ ${evidenceChips(evidence.filter((item) => finding.evidenceIds.includes(item.id)))}
216
+ </div>
217
+ </article>`
218
+ )}
219
+ </div>`
220
+ }
221
+
222
+ export type AdoptError = { gapId: string; message: Html | string }
223
+
224
+ function adoptForm(context: Context, detail: Detail, gapId: string, sha256: string) {
225
+ return html`<form method="post" action="/runs/${detail.runId}/gaps/${gapId}" class="inline-form">
226
+ <input type="hidden" name="csrf" value="${context.csrf}" />
227
+ <input type="hidden" name="sha" value="${sha256}" />
228
+ ${
229
+ context.actor
230
+ ? ''
231
+ : html`<input
232
+ name="actor"
233
+ required
234
+ autocomplete="name"
235
+ placeholder="Your name"
236
+ aria-label="Your name"
237
+ />`
238
+ }
239
+ <button class="btn small">Bring into plan</button>
240
+ </form>`
241
+ }
242
+
243
+ function newGaps(store: Store, context: Context, detail: Detail, adoptError: AdoptError | null) {
244
+ if (!detail.newGaps.length) return html`<p class="empty-line">No new gaps.</p>`
245
+ const revision = planRevision(store, detail.planId)
246
+ const latest = `${detail.planId}@${revision.plan.revision}`
247
+ const adopted = planRunGaps(store, detail.planId)
248
+ .filter((item) => item.runId === detail.runId && item.adopted)
249
+ .map((item) => item.gap.id)
250
+ return html`<ul class="gaps">
251
+ ${detail.newGaps.map(
252
+ (gap) =>
253
+ html`<li class="gap" id="${gap.id}" data-key="gap:${gap.id}">
254
+ <div class="gap-h">
255
+ <span class="id mono">${gap.id}</span> ${pill(gap.severity)} ${pill(gap.status)}
256
+ <span class="gap-refs muted"
257
+ >affects ${[...gap.requirementIds, ...gap.testIds].join(', ') || 'the plan'}</span
258
+ >
259
+ </div>
260
+ <p class="gap-q">${gap.question}</p>
261
+ <p class="muted">${gap.consequence}</p>
262
+ <div class="actions">
263
+ ${
264
+ adopted.includes(gap.id)
265
+ ? html`${pill('resolved', 'in plan')}
266
+ <a href="/plans/${latest}#${gap.id}">${latest}</a>`
267
+ : html`<a class="btn small" href="/plans/${latest}/gaps/${gap.id}#answer">Answer</a>
268
+ ${adoptForm(context, detail, gap.id, revision.sha256)}`
269
+ }
270
+ </div>
271
+ ${adoptError?.gapId === gap.id ? html`<p class="form-error" role="alert">${adoptError.message}</p>` : ''}
272
+ </li>`
273
+ )}
274
+ </ul>`
275
+ }
276
+
277
+ function coverage(store: Store, detail: Detail) {
278
+ const result = detail.result
279
+ if (!result) return html`<p class="empty-line">Coverage is known when the run finishes.</p>`
280
+ const plan = planDetail(store, detail.planId, detail.revision).plan
281
+ return html`<div class="scroll">
282
+ <table>
283
+ <thead>
284
+ <tr>
285
+ <th scope="col">ID</th>
286
+ <th scope="col">Requirement</th>
287
+ <th scope="col">Tests</th>
288
+ <th scope="col">Coverage</th>
289
+ </tr>
290
+ </thead>
291
+ <tbody>
292
+ ${result.coverage.requirements.map(
293
+ (row) =>
294
+ html`<tr>
295
+ <td class="mono">${row.requirementId}</td>
296
+ <td>
297
+ ${plan.requirements.find((item) => item.id === row.requirementId)?.text ?? ''}
298
+ ${row.reason ? html`<div class="muted small">${row.reason}</div>` : ''}
299
+ </td>
300
+ <td class="mono">${row.testIds.join(', ') || '–'}</td>
301
+ <td>${pill(row.status)}</td>
302
+ </tr>`
303
+ )}
304
+ </tbody>
305
+ </table>
306
+ </div>`
307
+ }
308
+
309
+ function cleanup(detail: Detail) {
310
+ const result = detail.result
311
+ return html`<dl class="facts">
312
+ <div>
313
+ <dt>Cleanup</dt>
314
+ <dd>
315
+ ${result ? html`${pill(result.cleanup.status)} ${result.cleanup.reason}` : html`<span class="muted">Not finished.</span>`}
316
+ </dd>
317
+ </div>
318
+ <div>
319
+ <dt>Attempts</dt>
320
+ <dd>
321
+ <ul class="plain">
322
+ ${detail.attempts.map(
323
+ (attempt) =>
324
+ html`<li>Attempt ${attempt.attempt} · ${clock(attempt.startedAt)} · ${attempt.actor}</li>`
325
+ )}
326
+ </ul>
327
+ </dd>
328
+ </div>
329
+ <div>
330
+ <dt>Browser session</dt>
331
+ <dd class="mono">${detail.browserSession}</dd>
332
+ </div>
333
+ <div>
334
+ <dt>Target</dt>
335
+ <dd class="mono">${detail.target}</dd>
336
+ </div>
337
+ ${
338
+ detail.request.retestOf
339
+ ? html`<div>
340
+ <dt>Retest of</dt>
341
+ <dd><a class="mono" href="/runs/${detail.request.retestOf}">${detail.request.retestOf}</a></dd>
342
+ </div>`
343
+ : ''
344
+ }
345
+ </dl>`
346
+ }
347
+
348
+ function inconclusiveList(detail: Detail) {
349
+ const rows = detail.tests.flatMap((test) =>
350
+ test.assertions
351
+ .filter((assertion) => assertion.result === 'inconclusive')
352
+ .map((assertion) => ({
353
+ reference: `${test.testId}.${assertion.assertionId}`,
354
+ reason: assertion.reason ?? '',
355
+ }))
356
+ )
357
+ if (!rows.length) return ''
358
+ return html`<div class="field-group">
359
+ <p class="lab">Inconclusive · ${rows.length}</p>
360
+ <ul class="plist">
361
+ ${rows.map(
362
+ (row) =>
363
+ html`<li>
364
+ <a href="#${row.reference}"><code>${row.reference}</code></a> ${row.reason}
365
+ </li>`
366
+ )}
367
+ </ul>
368
+ </div>`
369
+ }
370
+
371
+ function decisionPanel(context: Context, detail: Detail, state: FormState | null) {
372
+ const result = detail.result
373
+ if (!result)
374
+ return recordPanel(
375
+ 'Decision',
376
+ html`<p class="subj">${detail.runId} is in progress.</p>
377
+ <p class="help">The decision opens when the run finishes. This page updates live.</p>`
378
+ )
379
+ if (result.acceptance === 'not_required')
380
+ return recordPanel('Decision', html`<p class="help">The target policy does not require result acceptance.</p>`)
381
+ const latest = result.latestDecision
382
+ if (latest && !state)
383
+ return recordPanel(
384
+ 'Decision',
385
+ html`<p class="subj">
386
+ ${pill(latest.verdict, latest.verdict === 'approved' ? 'accepted' : 'rejected')} ${detail.runId}
387
+ </p>
388
+ <p class="help">
389
+ By ${latest.actor} · ${time(latest.at)} · sha256 <code>${short(latest.sha256)}</code>
390
+ </p>
391
+ <blockquote>${latest.rationale}</blockquote>`
392
+ )
393
+ return verdictPanel(
394
+ context,
395
+ {
396
+ action: `/runs/${detail.runId}`,
397
+ subject: detail.runId,
398
+ question: `Accept ${detail.runId}?`,
399
+ sha256: result.sha256,
400
+ digestLabel: 'Result',
401
+ approve: {
402
+ value: 'accept',
403
+ label: `Accept ${detail.runId}`,
404
+ confirm: 'Confirm acceptance',
405
+ summary: `Accept ${detail.runId} as the record of build ${detail.build}.`,
406
+ },
407
+ reject: {
408
+ value: 'reject',
409
+ label: 'Reject',
410
+ confirm: 'Confirm rejection',
411
+ summary: `Reject ${detail.runId}. Dependent plans stay blocked.`,
412
+ },
413
+ staleMessage: `${detail.runId} has a new result. Review it before you decide.`,
414
+ intro: inconclusiveList(detail),
415
+ },
416
+ state ?? emptyState()
417
+ )
418
+ }
419
+
420
+ function runBadge(detail: Detail) {
421
+ const acceptance = detail.result?.acceptance
422
+ if (!acceptance) return pill('started', 'in progress')
423
+ if (acceptance === 'approved') return pill('approved', 'accepted')
424
+ if (acceptance === 'pending') return pill('pending', 'awaiting acceptance')
425
+ return pill(acceptance)
426
+ }
427
+
428
+ export function runPage(
429
+ store: Store,
430
+ context: Context,
431
+ runId: string,
432
+ state: FormState | null = null,
433
+ adoptError: AdoptError | null = null
434
+ ): Page {
435
+ const detail = runDetail(store, runId)
436
+ const sub = html`<a class="mono" href="/plans/${detail.planId}@${detail.revision}"
437
+ >${detail.planId}@${detail.revision}</a
438
+ >
439
+ · build <code>${detail.build}</code> · started ${clock(detail.startedAt)} ·
440
+ ${plural(detail.attempts.length, 'attempt')}
441
+ ${detail.result ? html`· finished ${time(detail.result.finishedAt)} ago` : ''}`
442
+ const badge = runBadge(detail)
443
+ const report = detail.result
444
+ ? downloads(
445
+ 'Download report',
446
+ `/runs/${runId}/report`,
447
+ 'The HTML report is self-contained and print-ready. Open it and print to PDF.'
448
+ )
449
+ : html`<span class="muted small">The report is ready when the run finishes.</span>`
450
+ const body: Html = html`${heading(
451
+ runId,
452
+ sub,
453
+ html`<div class="meta" data-region="state">${badge} <span class="head-actions">${report}</span></div>`
454
+ )}
455
+ <div class="split">
456
+ <div class="main-col">
457
+ ${strip(detail)}
458
+ <nav class="jump" aria-label="On this page">
459
+ <a href="#tests">Tests</a><a href="#findings">Findings</a><a href="#new-gaps">New gaps</a
460
+ ><a href="#coverage">Coverage</a><a href="#cleanup">Cleanup</a>
461
+ </nav>
462
+ <section aria-labelledby="tests" data-region="tests">
463
+ <h2 id="tests">Tests <span class="n">${detail.tests.length}</span></h2>
464
+ <div class="stack">${detail.tests.map((test) => testCard(store, detail, test))}</div>
465
+ </section>
466
+ <section aria-labelledby="findings" data-region="findings">
467
+ <h2 id="findings">Findings <span class="n">${detail.findings.length}</span></h2>
468
+ ${findings(store, detail)}
469
+ </section>
470
+ <section aria-labelledby="new-gaps" data-region="new-gaps">
471
+ <h2 id="new-gaps">New gaps <span class="n">${detail.newGaps.length}</span></h2>
472
+ ${newGaps(store, context, detail, adoptError)}
473
+ </section>
474
+ <section aria-labelledby="coverage" data-region="coverage">
475
+ <h2 id="coverage">Coverage</h2>
476
+ ${coverage(store, detail)}
477
+ </section>
478
+ <section aria-labelledby="cleanup" data-region="cleanup">
479
+ <h2 id="cleanup">Cleanup and attempts</h2>
480
+ ${cleanup(detail)}
481
+ </section>
482
+ </div>
483
+ <aside class="side" aria-label="Decision">
484
+ ${decisionPanel(context, detail, state)}
485
+ <p class="back"><a href="/">← Back to inbox</a></p>
486
+ </aside>
487
+ </div>`
488
+ return { title: runId, section: 'runs', subjects: [`run:${runId}`, 'inbox'], body }
489
+ }