toolroll 0.5.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 (626) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1113 -0
  3. package/THIRD_PARTY_NOTICES.md +449 -0
  4. package/dist/accent-colors.d.ts +48 -0
  5. package/dist/accent-colors.js +122 -0
  6. package/dist/action-ledger.d.ts +26 -0
  7. package/dist/action-ledger.js +75 -0
  8. package/dist/agent-fence.d.ts +42 -0
  9. package/dist/agent-fence.js +183 -0
  10. package/dist/agentconfig.d.ts +216 -0
  11. package/dist/agentconfig.js +454 -0
  12. package/dist/api-tokens.d.ts +16 -0
  13. package/dist/api-tokens.js +27 -0
  14. package/dist/approval-policy.d.ts +69 -0
  15. package/dist/approval-policy.js +137 -0
  16. package/dist/approval-rules-ui.d.ts +25 -0
  17. package/dist/approval-rules-ui.js +39 -0
  18. package/dist/assignment-adapters.d.ts +180 -0
  19. package/dist/assignment-adapters.js +239 -0
  20. package/dist/assignment-brief.d.ts +71 -0
  21. package/dist/assignment-brief.js +129 -0
  22. package/dist/assignment-delivery.d.ts +56 -0
  23. package/dist/assignment-delivery.js +160 -0
  24. package/dist/assignment-presentation.d.ts +26 -0
  25. package/dist/assignment-presentation.js +80 -0
  26. package/dist/assignment-status.d.ts +62 -0
  27. package/dist/assignment-status.js +152 -0
  28. package/dist/assignment-ui.d.ts +71 -0
  29. package/dist/assignment-ui.js +103 -0
  30. package/dist/assignment.d.ts +222 -0
  31. package/dist/assignment.js +399 -0
  32. package/dist/attest.d.ts +56 -0
  33. package/dist/attest.js +153 -0
  34. package/dist/backend.d.ts +97 -0
  35. package/dist/backend.js +166 -0
  36. package/dist/backup-ui.d.ts +22 -0
  37. package/dist/backup-ui.js +59 -0
  38. package/dist/backup.d.ts +84 -0
  39. package/dist/backup.js +421 -0
  40. package/dist/beads.d.ts +34 -0
  41. package/dist/beads.js +135 -0
  42. package/dist/bin.d.ts +2 -0
  43. package/dist/bin.js +22 -0
  44. package/dist/board.d.ts +150 -0
  45. package/dist/board.js +210 -0
  46. package/dist/boot-identity.d.ts +63 -0
  47. package/dist/boot-identity.js +99 -0
  48. package/dist/browser/THIRD_PARTY_NOTICES.txt +2295 -0
  49. package/dist/browser/workspace.css +4 -0
  50. package/dist/browser/workspace.js +225 -0
  51. package/dist/browser-crew.d.ts +11 -0
  52. package/dist/browser-crew.js +50 -0
  53. package/dist/browser-shell.d.ts +8 -0
  54. package/dist/browser-shell.js +60 -0
  55. package/dist/browser-workspace.d.ts +780 -0
  56. package/dist/browser-workspace.js +47 -0
  57. package/dist/budget-alerts.d.ts +19 -0
  58. package/dist/budget-alerts.js +46 -0
  59. package/dist/builder.d.ts +370 -0
  60. package/dist/builder.js +3262 -0
  61. package/dist/capscan.d.ts +35 -0
  62. package/dist/capscan.js +113 -0
  63. package/dist/chat-acceptance.d.ts +71 -0
  64. package/dist/chat-acceptance.js +120 -0
  65. package/dist/chat-actions.d.ts +265 -0
  66. package/dist/chat-actions.js +1273 -0
  67. package/dist/chat-channel.d.ts +109 -0
  68. package/dist/chat-channel.js +652 -0
  69. package/dist/chat-continuity.d.ts +32 -0
  70. package/dist/chat-continuity.js +317 -0
  71. package/dist/chat-controls.d.ts +113 -0
  72. package/dist/chat-controls.js +53 -0
  73. package/dist/chat-delivery-state.d.ts +159 -0
  74. package/dist/chat-delivery-state.js +309 -0
  75. package/dist/chat-delivery.d.ts +38 -0
  76. package/dist/chat-delivery.js +693 -0
  77. package/dist/chat-display.d.ts +1 -0
  78. package/dist/chat-display.js +18 -0
  79. package/dist/chat-evidence.d.ts +123 -0
  80. package/dist/chat-evidence.js +207 -0
  81. package/dist/chat-flow.d.ts +44 -0
  82. package/dist/chat-flow.js +215 -0
  83. package/dist/chat-inbox.d.ts +57 -0
  84. package/dist/chat-inbox.js +123 -0
  85. package/dist/chat-polish.d.ts +16 -0
  86. package/dist/chat-polish.js +135 -0
  87. package/dist/chat-review.d.ts +46 -0
  88. package/dist/chat-review.js +98 -0
  89. package/dist/chat-rooms.d.ts +123 -0
  90. package/dist/chat-rooms.js +216 -0
  91. package/dist/chat-task-actions.d.ts +44 -0
  92. package/dist/chat-task-actions.js +79 -0
  93. package/dist/check-progress.d.ts +51 -0
  94. package/dist/check-progress.js +290 -0
  95. package/dist/child-database.d.ts +12 -0
  96. package/dist/child-database.js +30 -0
  97. package/dist/claim.d.ts +688 -0
  98. package/dist/claim.js +1740 -0
  99. package/dist/cli.d.ts +137 -0
  100. package/dist/cli.js +1461 -0
  101. package/dist/codex-limits.d.ts +15 -0
  102. package/dist/codex-limits.js +145 -0
  103. package/dist/coding-context.d.ts +55 -0
  104. package/dist/coding-context.js +92 -0
  105. package/dist/coding-handoff.d.ts +57 -0
  106. package/dist/coding-handoff.js +277 -0
  107. package/dist/coding-provider.d.ts +72 -0
  108. package/dist/coding-provider.js +424 -0
  109. package/dist/coding-shipping-ui.d.ts +4 -0
  110. package/dist/coding-shipping-ui.js +12 -0
  111. package/dist/coding-types.d.ts +62 -0
  112. package/dist/coding-types.js +1 -0
  113. package/dist/coding-ui.d.ts +17 -0
  114. package/dist/coding-ui.js +364 -0
  115. package/dist/coding-update.d.ts +15 -0
  116. package/dist/coding-update.js +149 -0
  117. package/dist/coding-workspace.d.ts +124 -0
  118. package/dist/coding-workspace.js +768 -0
  119. package/dist/container-state.d.ts +5 -0
  120. package/dist/container-state.js +62 -0
  121. package/dist/containment.d.ts +197 -0
  122. package/dist/containment.js +559 -0
  123. package/dist/contest.d.ts +318 -0
  124. package/dist/contest.js +753 -0
  125. package/dist/control-setup.d.ts +35 -0
  126. package/dist/control-setup.js +40 -0
  127. package/dist/control-ui.d.ts +29 -0
  128. package/dist/control-ui.js +53 -0
  129. package/dist/controller-service.d.ts +1 -0
  130. package/dist/controller-service.js +19 -0
  131. package/dist/controller-supervisor.d.ts +30 -0
  132. package/dist/controller-supervisor.js +104 -0
  133. package/dist/converse.d.ts +304 -0
  134. package/dist/converse.js +849 -0
  135. package/dist/coordinator-proposals.d.ts +29 -0
  136. package/dist/coordinator-proposals.js +137 -0
  137. package/dist/coordinator.d.ts +186 -0
  138. package/dist/coordinator.js +447 -0
  139. package/dist/credentials-ui.d.ts +25 -0
  140. package/dist/credentials-ui.js +48 -0
  141. package/dist/daemon.d.ts +209 -0
  142. package/dist/daemon.js +604 -0
  143. package/dist/decision.d.ts +120 -0
  144. package/dist/decision.js +388 -0
  145. package/dist/demo.d.ts +55 -0
  146. package/dist/demo.js +992 -0
  147. package/dist/desktop-access.d.ts +28 -0
  148. package/dist/desktop-access.js +88 -0
  149. package/dist/desktop-bundle.d.ts +16 -0
  150. package/dist/desktop-bundle.js +74 -0
  151. package/dist/desktop-host.d.ts +54 -0
  152. package/dist/desktop-host.js +508 -0
  153. package/dist/desktop-update-gate.d.ts +12 -0
  154. package/dist/desktop-update-gate.js +91 -0
  155. package/dist/desktop-update-recovery.d.ts +14 -0
  156. package/dist/desktop-update-recovery.js +243 -0
  157. package/dist/desktop-update.d.ts +110 -0
  158. package/dist/desktop-update.js +740 -0
  159. package/dist/discord-api.d.ts +20 -0
  160. package/dist/discord-api.js +138 -0
  161. package/dist/discord-chat.d.ts +16 -0
  162. package/dist/discord-chat.js +381 -0
  163. package/dist/discord-settings.d.ts +7 -0
  164. package/dist/discord-settings.js +75 -0
  165. package/dist/discord.d.ts +10 -0
  166. package/dist/discord.js +169 -0
  167. package/dist/discover.d.ts +75 -0
  168. package/dist/discover.js +150 -0
  169. package/dist/dispatch.d.ts +100 -0
  170. package/dist/dispatch.js +311 -0
  171. package/dist/dispose.d.ts +161 -0
  172. package/dist/dispose.js +803 -0
  173. package/dist/email-settings.d.ts +34 -0
  174. package/dist/email-settings.js +50 -0
  175. package/dist/envelope.d.ts +47 -0
  176. package/dist/envelope.js +105 -0
  177. package/dist/evidence-pack.d.ts +185 -0
  178. package/dist/evidence-pack.js +258 -0
  179. package/dist/evidence.d.ts +345 -0
  180. package/dist/evidence.js +782 -0
  181. package/dist/exec.d.ts +285 -0
  182. package/dist/exec.js +1841 -0
  183. package/dist/exhaustion.d.ts +85 -0
  184. package/dist/exhaustion.js +141 -0
  185. package/dist/export-ui.d.ts +4 -0
  186. package/dist/export-ui.js +17 -0
  187. package/dist/export.d.ts +48 -0
  188. package/dist/export.js +305 -0
  189. package/dist/flow-actions.d.ts +77 -0
  190. package/dist/flow-actions.js +203 -0
  191. package/dist/flow-code.d.ts +50 -0
  192. package/dist/flow-code.js +159 -0
  193. package/dist/flow-draft.d.ts +38 -0
  194. package/dist/flow-draft.js +80 -0
  195. package/dist/flow-engine.d.ts +66 -0
  196. package/dist/flow-engine.js +416 -0
  197. package/dist/flow-insights.d.ts +95 -0
  198. package/dist/flow-insights.js +149 -0
  199. package/dist/flow-live.d.ts +24 -0
  200. package/dist/flow-live.js +150 -0
  201. package/dist/flow-people.d.ts +24 -0
  202. package/dist/flow-people.js +91 -0
  203. package/dist/flow-replies.d.ts +48 -0
  204. package/dist/flow-replies.js +113 -0
  205. package/dist/flow-scripts.d.ts +38 -0
  206. package/dist/flow-scripts.js +73 -0
  207. package/dist/flow-secrets.d.ts +13 -0
  208. package/dist/flow-secrets.js +45 -0
  209. package/dist/flow-sort.d.ts +82 -0
  210. package/dist/flow-sort.js +153 -0
  211. package/dist/flow-steps.d.ts +40 -0
  212. package/dist/flow-steps.js +408 -0
  213. package/dist/flow-triggers.d.ts +244 -0
  214. package/dist/flow-triggers.js +959 -0
  215. package/dist/flows-ui.d.ts +20 -0
  216. package/dist/flows-ui.js +175 -0
  217. package/dist/flows.d.ts +229 -0
  218. package/dist/flows.js +968 -0
  219. package/dist/fonts.d.ts +19 -0
  220. package/dist/fonts.js +19 -0
  221. package/dist/gaps.d.ts +35 -0
  222. package/dist/gaps.js +101 -0
  223. package/dist/gate-failure.d.ts +40 -0
  224. package/dist/gate-failure.js +66 -0
  225. package/dist/git.d.ts +54 -0
  226. package/dist/git.js +94 -0
  227. package/dist/google-mail.d.ts +58 -0
  228. package/dist/google-mail.js +151 -0
  229. package/dist/grant.d.ts +136 -0
  230. package/dist/grant.js +238 -0
  231. package/dist/graph.d.ts +164 -0
  232. package/dist/graph.js +383 -0
  233. package/dist/guides.d.ts +22 -0
  234. package/dist/guides.js +363 -0
  235. package/dist/held.d.ts +150 -0
  236. package/dist/held.js +799 -0
  237. package/dist/invoke.d.ts +112 -0
  238. package/dist/invoke.js +780 -0
  239. package/dist/issues.d.ts +41 -0
  240. package/dist/issues.js +131 -0
  241. package/dist/job-object-helper.ps1 +252 -0
  242. package/dist/jsonl-discriminants.d.ts +11 -0
  243. package/dist/jsonl-discriminants.js +93 -0
  244. package/dist/keys.d.ts +104 -0
  245. package/dist/keys.js +229 -0
  246. package/dist/kits-ui.d.ts +17 -0
  247. package/dist/kits-ui.js +46 -0
  248. package/dist/kits.d.ts +86 -0
  249. package/dist/kits.js +215 -0
  250. package/dist/knowledge-cli.d.ts +101 -0
  251. package/dist/knowledge-cli.js +92 -0
  252. package/dist/knowledge-ui.d.ts +24 -0
  253. package/dist/knowledge-ui.js +40 -0
  254. package/dist/lead-context.d.ts +8 -0
  255. package/dist/lead-context.js +56 -0
  256. package/dist/lead-follow.d.ts +22 -0
  257. package/dist/lead-follow.js +167 -0
  258. package/dist/lead-status.d.ts +75 -0
  259. package/dist/lead-status.js +285 -0
  260. package/dist/ledger-chain.d.ts +46 -0
  261. package/dist/ledger-chain.js +187 -0
  262. package/dist/ledger-csv.d.ts +4 -0
  263. package/dist/ledger-csv.js +11 -0
  264. package/dist/ledger-view.d.ts +24 -0
  265. package/dist/ledger-view.js +58 -0
  266. package/dist/limits-ui.d.ts +26 -0
  267. package/dist/limits-ui.js +67 -0
  268. package/dist/link.d.ts +72 -0
  269. package/dist/link.js +217 -0
  270. package/dist/live.d.ts +96 -0
  271. package/dist/live.js +366 -0
  272. package/dist/liveness.d.ts +30 -0
  273. package/dist/liveness.js +42 -0
  274. package/dist/log.d.ts +7 -0
  275. package/dist/log.js +25 -0
  276. package/dist/mailbox.d.ts +57 -0
  277. package/dist/mailbox.js +150 -0
  278. package/dist/maintenance.d.ts +11 -0
  279. package/dist/maintenance.js +35 -0
  280. package/dist/mate-cli.d.ts +52 -0
  281. package/dist/mate-cli.js +345 -0
  282. package/dist/mate-contract.d.ts +10 -0
  283. package/dist/mate-contract.js +30 -0
  284. package/dist/mate-doors.d.ts +69 -0
  285. package/dist/mate-doors.js +548 -0
  286. package/dist/mate-progress.d.ts +29 -0
  287. package/dist/mate-progress.js +121 -0
  288. package/dist/mate-tools.d.ts +94 -0
  289. package/dist/mate-tools.js +1878 -0
  290. package/dist/mate.d.ts +92 -0
  291. package/dist/mate.js +435 -0
  292. package/dist/mcp-connect.d.ts +103 -0
  293. package/dist/mcp-connect.js +252 -0
  294. package/dist/mcp.d.ts +27 -0
  295. package/dist/mcp.js +651 -0
  296. package/dist/memory-cli.d.ts +466 -0
  297. package/dist/memory-cli.js +111 -0
  298. package/dist/memory-pass.d.ts +160 -0
  299. package/dist/memory-pass.js +409 -0
  300. package/dist/metrics.d.ts +3 -0
  301. package/dist/metrics.js +56 -0
  302. package/dist/mobile-viewport.d.ts +3 -0
  303. package/dist/mobile-viewport.js +37 -0
  304. package/dist/model-catalog.d.ts +118 -0
  305. package/dist/model-catalog.js +376 -0
  306. package/dist/models-cli.d.ts +102 -0
  307. package/dist/models-cli.js +66 -0
  308. package/dist/models-ui.d.ts +34 -0
  309. package/dist/models-ui.js +68 -0
  310. package/dist/modes.d.ts +80 -0
  311. package/dist/modes.js +182 -0
  312. package/dist/monitoring-settings.d.ts +44 -0
  313. package/dist/monitoring-settings.js +173 -0
  314. package/dist/monitoring-ui.d.ts +20 -0
  315. package/dist/monitoring-ui.js +51 -0
  316. package/dist/monitoring.d.ts +34 -0
  317. package/dist/monitoring.js +221 -0
  318. package/dist/names.d.ts +67 -0
  319. package/dist/names.js +102 -0
  320. package/dist/observations.d.ts +40 -0
  321. package/dist/observations.js +211 -0
  322. package/dist/oidc.d.ts +71 -0
  323. package/dist/oidc.js +182 -0
  324. package/dist/onboard.d.ts +108 -0
  325. package/dist/onboard.js +325 -0
  326. package/dist/openrouter-models.d.ts +30 -0
  327. package/dist/openrouter-models.js +120 -0
  328. package/dist/operate.d.ts +118 -0
  329. package/dist/operate.js +11321 -0
  330. package/dist/peek-cli.d.ts +97 -0
  331. package/dist/peek-cli.js +215 -0
  332. package/dist/peek.d.ts +134 -0
  333. package/dist/peek.js +578 -0
  334. package/dist/phase-routing.d.ts +307 -0
  335. package/dist/phase-routing.js +658 -0
  336. package/dist/plan-auto.d.ts +14 -0
  337. package/dist/plan-auto.js +92 -0
  338. package/dist/plan.d.ts +186 -0
  339. package/dist/plan.js +401 -0
  340. package/dist/planner-source.d.ts +251 -0
  341. package/dist/planner-source.js +460 -0
  342. package/dist/planner.d.ts +75 -0
  343. package/dist/planner.js +992 -0
  344. package/dist/policy-ui.d.ts +20 -0
  345. package/dist/policy-ui.js +41 -0
  346. package/dist/policy.d.ts +111 -0
  347. package/dist/policy.js +231 -0
  348. package/dist/prepared-evidence.d.ts +16 -0
  349. package/dist/prepared-evidence.js +96 -0
  350. package/dist/pricing.d.ts +45 -0
  351. package/dist/pricing.js +77 -0
  352. package/dist/principal.d.ts +42 -0
  353. package/dist/principal.js +82 -0
  354. package/dist/probe.d.ts +46 -0
  355. package/dist/probe.js +79 -0
  356. package/dist/process-custody.d.ts +17 -0
  357. package/dist/process-custody.js +91 -0
  358. package/dist/process-liveness.d.ts +10 -0
  359. package/dist/process-liveness.js +81 -0
  360. package/dist/process-recovery-anchor.d.ts +75 -0
  361. package/dist/process-recovery-anchor.js +188 -0
  362. package/dist/process-recovery-coalition.d.ts +25 -0
  363. package/dist/process-recovery-coalition.js +105 -0
  364. package/dist/process-recovery-eligibility.d.ts +178 -0
  365. package/dist/process-recovery-eligibility.js +170 -0
  366. package/dist/process-recovery-native.d.ts +172 -0
  367. package/dist/process-recovery-native.js +592 -0
  368. package/dist/process-recovery-provenance.d.ts +163 -0
  369. package/dist/process-recovery-provenance.js +292 -0
  370. package/dist/process-recovery-services.d.ts +56 -0
  371. package/dist/process-recovery-services.js +191 -0
  372. package/dist/process-recovery-settlement.d.ts +32 -0
  373. package/dist/process-recovery-settlement.js +132 -0
  374. package/dist/process-recovery.d.ts +41 -0
  375. package/dist/process-recovery.js +91 -0
  376. package/dist/process-tree.d.ts +41 -0
  377. package/dist/process-tree.js +264 -0
  378. package/dist/project-access.d.ts +14 -0
  379. package/dist/project-access.js +34 -0
  380. package/dist/project-cli.d.ts +18 -0
  381. package/dist/project-cli.js +104 -0
  382. package/dist/project-delete-ui.d.ts +22 -0
  383. package/dist/project-delete-ui.js +48 -0
  384. package/dist/project-delete.d.ts +55 -0
  385. package/dist/project-delete.js +413 -0
  386. package/dist/project-knowledge.d.ts +111 -0
  387. package/dist/project-knowledge.js +241 -0
  388. package/dist/project-learning.d.ts +100 -0
  389. package/dist/project-learning.js +438 -0
  390. package/dist/project-memory.d.ts +81 -0
  391. package/dist/project-memory.js +264 -0
  392. package/dist/project-skills.d.ts +152 -0
  393. package/dist/project-skills.js +660 -0
  394. package/dist/project-tools.d.ts +219 -0
  395. package/dist/project-tools.js +796 -0
  396. package/dist/project.d.ts +61 -0
  397. package/dist/project.js +125 -0
  398. package/dist/prompt.d.ts +22 -0
  399. package/dist/prompt.js +72 -0
  400. package/dist/proof.d.ts +513 -0
  401. package/dist/proof.js +1140 -0
  402. package/dist/proposal.d.ts +82 -0
  403. package/dist/proposal.js +210 -0
  404. package/dist/provider-connection.d.ts +22 -0
  405. package/dist/provider-connection.js +98 -0
  406. package/dist/provider-limits.d.ts +35 -0
  407. package/dist/provider-limits.js +95 -0
  408. package/dist/provider.d.ts +279 -0
  409. package/dist/provider.js +935 -0
  410. package/dist/publish.d.ts +107 -0
  411. package/dist/publish.js +650 -0
  412. package/dist/pulls.d.ts +118 -0
  413. package/dist/pulls.js +240 -0
  414. package/dist/push.d.ts +95 -0
  415. package/dist/push.js +353 -0
  416. package/dist/quality.d.ts +8 -0
  417. package/dist/quality.js +6 -0
  418. package/dist/recipe-ui.d.ts +18 -0
  419. package/dist/recipe-ui.js +152 -0
  420. package/dist/recipes.d.ts +81 -0
  421. package/dist/recipes.js +332 -0
  422. package/dist/remote.d.ts +67 -0
  423. package/dist/remote.js +122 -0
  424. package/dist/render.d.ts +64 -0
  425. package/dist/render.js +587 -0
  426. package/dist/report-summary.d.ts +13 -0
  427. package/dist/report-summary.js +15 -0
  428. package/dist/repos.d.ts +65 -0
  429. package/dist/repos.js +277 -0
  430. package/dist/repository-context-ui.d.ts +2 -0
  431. package/dist/repository-context-ui.js +10 -0
  432. package/dist/repository-context.d.ts +67 -0
  433. package/dist/repository-context.js +376 -0
  434. package/dist/restart-certification.d.ts +134 -0
  435. package/dist/restart-certification.js +226 -0
  436. package/dist/result-actions.d.ts +28 -0
  437. package/dist/result-actions.js +270 -0
  438. package/dist/result-completion.d.ts +18 -0
  439. package/dist/result-completion.js +66 -0
  440. package/dist/result-review.d.ts +213 -0
  441. package/dist/result-review.js +430 -0
  442. package/dist/retention-ui.d.ts +12 -0
  443. package/dist/retention-ui.js +39 -0
  444. package/dist/retention.d.ts +72 -0
  445. package/dist/retention.js +288 -0
  446. package/dist/review-context.d.ts +243 -0
  447. package/dist/review-context.js +1043 -0
  448. package/dist/review-evidence.d.ts +33 -0
  449. package/dist/review-evidence.js +56 -0
  450. package/dist/reviewer.d.ts +97 -0
  451. package/dist/reviewer.js +202 -0
  452. package/dist/routine.d.ts +228 -0
  453. package/dist/routine.js +764 -0
  454. package/dist/runner.d.ts +266 -0
  455. package/dist/runner.js +399 -0
  456. package/dist/scan.d.ts +32 -0
  457. package/dist/scan.js +92 -0
  458. package/dist/scope.d.ts +695 -0
  459. package/dist/scope.js +1176 -0
  460. package/dist/scout-report.d.ts +42 -0
  461. package/dist/scout-report.js +108 -0
  462. package/dist/scout.d.ts +69 -0
  463. package/dist/scout.js +351 -0
  464. package/dist/serve.d.ts +313 -0
  465. package/dist/serve.js +21776 -0
  466. package/dist/session-brief.d.ts +8 -0
  467. package/dist/session-brief.js +24 -0
  468. package/dist/session-cli.d.ts +13 -0
  469. package/dist/session-cli.js +284 -0
  470. package/dist/session-contract.d.ts +183 -0
  471. package/dist/session-contract.js +122 -0
  472. package/dist/session-http.d.ts +19 -0
  473. package/dist/session-http.js +154 -0
  474. package/dist/session-server.d.ts +12 -0
  475. package/dist/session-server.js +37 -0
  476. package/dist/session-service.d.ts +22 -0
  477. package/dist/session-service.js +157 -0
  478. package/dist/setup-guide.d.ts +39 -0
  479. package/dist/setup-guide.js +93 -0
  480. package/dist/sign-in-guard.d.ts +45 -0
  481. package/dist/sign-in-guard.js +74 -0
  482. package/dist/skills-ui.d.ts +11 -0
  483. package/dist/skills-ui.js +58 -0
  484. package/dist/skills.d.ts +97 -0
  485. package/dist/skills.js +276 -0
  486. package/dist/slack-api.d.ts +55 -0
  487. package/dist/slack-api.js +229 -0
  488. package/dist/slack-chat.d.ts +28 -0
  489. package/dist/slack-chat.js +413 -0
  490. package/dist/slack-settings.d.ts +7 -0
  491. package/dist/slack-settings.js +75 -0
  492. package/dist/slack-state.d.ts +13 -0
  493. package/dist/slack-state.js +9 -0
  494. package/dist/slack.d.ts +13 -0
  495. package/dist/slack.js +167 -0
  496. package/dist/spend-ui.d.ts +28 -0
  497. package/dist/spend-ui.js +95 -0
  498. package/dist/spend.d.ts +142 -0
  499. package/dist/spend.js +313 -0
  500. package/dist/sqlite-runtime.d.ts +3 -0
  501. package/dist/sqlite-runtime.js +6 -0
  502. package/dist/sso-settings.d.ts +33 -0
  503. package/dist/sso-settings.js +103 -0
  504. package/dist/sso-ui.d.ts +22 -0
  505. package/dist/sso-ui.js +39 -0
  506. package/dist/storage.d.ts +15 -0
  507. package/dist/storage.js +84 -0
  508. package/dist/store.d.ts +7754 -0
  509. package/dist/store.js +23279 -0
  510. package/dist/structured-output.d.ts +58 -0
  511. package/dist/structured-output.js +103 -0
  512. package/dist/style-asset.d.ts +7 -0
  513. package/dist/style-asset.js +41 -0
  514. package/dist/subscription-chat.d.ts +46 -0
  515. package/dist/subscription-chat.js +275 -0
  516. package/dist/summary.d.ts +33 -0
  517. package/dist/summary.js +71 -0
  518. package/dist/supervisor.mjs +297 -0
  519. package/dist/surface.d.ts +63 -0
  520. package/dist/surface.js +352 -0
  521. package/dist/sync.d.ts +94 -0
  522. package/dist/sync.js +279 -0
  523. package/dist/task-composer.d.ts +13 -0
  524. package/dist/task-composer.js +52 -0
  525. package/dist/task-control.d.ts +165 -0
  526. package/dist/task-control.js +214 -0
  527. package/dist/task-outcome-cli.d.ts +10 -0
  528. package/dist/task-outcome-cli.js +92 -0
  529. package/dist/task-text.d.ts +30 -0
  530. package/dist/task-text.js +41 -0
  531. package/dist/team-cli.d.ts +15 -0
  532. package/dist/team-cli.js +618 -0
  533. package/dist/team-contract.d.ts +92 -0
  534. package/dist/team-contract.js +1 -0
  535. package/dist/team-http.d.ts +17 -0
  536. package/dist/team-http.js +144 -0
  537. package/dist/team-leads.d.ts +81 -0
  538. package/dist/team-leads.js +565 -0
  539. package/dist/team-runtime.d.ts +27 -0
  540. package/dist/team-runtime.js +264 -0
  541. package/dist/team-ui.d.ts +3 -0
  542. package/dist/team-ui.js +9 -0
  543. package/dist/team-updates.d.ts +8 -0
  544. package/dist/team-updates.js +57 -0
  545. package/dist/teammate-admin.d.ts +53 -0
  546. package/dist/teammate-admin.js +111 -0
  547. package/dist/teammate-desk.d.ts +60 -0
  548. package/dist/teammate-desk.js +154 -0
  549. package/dist/teammate-memory.d.ts +64 -0
  550. package/dist/teammate-memory.js +196 -0
  551. package/dist/teammate-question.d.ts +59 -0
  552. package/dist/teammate-question.js +175 -0
  553. package/dist/teammate-tools.d.ts +108 -0
  554. package/dist/teammate-tools.js +271 -0
  555. package/dist/teammate-week.d.ts +68 -0
  556. package/dist/teammate-week.js +132 -0
  557. package/dist/teammate-work.d.ts +76 -0
  558. package/dist/teammate-work.js +280 -0
  559. package/dist/teammates-ui.d.ts +20 -0
  560. package/dist/teammates-ui.js +166 -0
  561. package/dist/teammates.d.ts +180 -0
  562. package/dist/teammates.js +253 -0
  563. package/dist/teams-api.d.ts +33 -0
  564. package/dist/teams-api.js +204 -0
  565. package/dist/teams-chat.d.ts +26 -0
  566. package/dist/teams-chat.js +215 -0
  567. package/dist/teams-settings.d.ts +12 -0
  568. package/dist/teams-settings.js +57 -0
  569. package/dist/teams.d.ts +21 -0
  570. package/dist/teams.js +124 -0
  571. package/dist/telegram-flow.d.ts +49 -0
  572. package/dist/telegram-flow.js +110 -0
  573. package/dist/telegram-mate.d.ts +137 -0
  574. package/dist/telegram-mate.js +707 -0
  575. package/dist/telegram-progress.d.ts +14 -0
  576. package/dist/telegram-progress.js +129 -0
  577. package/dist/telegram-settings.d.ts +9 -0
  578. package/dist/telegram-settings.js +36 -0
  579. package/dist/telegram-status.d.ts +75 -0
  580. package/dist/telegram-status.js +265 -0
  581. package/dist/telegram-team.d.ts +85 -0
  582. package/dist/telegram-team.js +359 -0
  583. package/dist/telegram.d.ts +230 -0
  584. package/dist/telegram.js +1931 -0
  585. package/dist/templates.d.ts +65 -0
  586. package/dist/templates.js +118 -0
  587. package/dist/tool-launcher.d.ts +1 -0
  588. package/dist/tool-launcher.js +101 -0
  589. package/dist/tools-ui.d.ts +30 -0
  590. package/dist/tools-ui.js +40 -0
  591. package/dist/transitions-recipes.d.ts +1 -0
  592. package/dist/transitions-recipes.js +203 -0
  593. package/dist/tree-proof.d.ts +34 -0
  594. package/dist/tree-proof.js +58 -0
  595. package/dist/verification-evidence.d.ts +44 -0
  596. package/dist/verification-evidence.js +238 -0
  597. package/dist/version.d.ts +2 -0
  598. package/dist/version.js +10 -0
  599. package/dist/webhooks.d.ts +91 -0
  600. package/dist/webhooks.js +283 -0
  601. package/dist/work-index.d.ts +68 -0
  602. package/dist/work-index.js +384 -0
  603. package/dist/work-summary.d.ts +57 -0
  604. package/dist/work-summary.js +116 -0
  605. package/dist/workspace-motion.d.ts +4 -0
  606. package/dist/workspace-motion.js +133 -0
  607. package/dist/workspace-revision.d.ts +19 -0
  608. package/dist/workspace-revision.js +92 -0
  609. package/dist/workspace-ui.d.ts +204 -0
  610. package/dist/workspace-ui.js +411 -0
  611. package/dist/worktree-notices.d.ts +13 -0
  612. package/dist/worktree-notices.js +17 -0
  613. package/dist/worktree.d.ts +249 -0
  614. package/dist/worktree.js +720 -0
  615. package/package.json +122 -0
  616. package/scripts/canary-assertions.mjs +42 -0
  617. package/scripts/crash-canary.mjs +253 -0
  618. package/scripts/fixtures/crash-process.mjs +74 -0
  619. package/scripts/fixtures/pilot-scenarios.mjs +85 -0
  620. package/scripts/fixtures/restart-service.mjs +27 -0
  621. package/scripts/launchd-certification.mjs +143 -0
  622. package/scripts/pilot.mjs +121 -0
  623. package/scripts/proof-preflight.mjs +140 -0
  624. package/scripts/provider-canary.mjs +379 -0
  625. package/scripts/recovery-canary.mjs +135 -0
  626. package/scripts/restart-certification.mjs +98 -0
package/dist/proof.js ADDED
@@ -0,0 +1,1140 @@
1
+ /**
2
+ * The build's own claim, adjudicated against evidence the plane captured
3
+ * itself (`docs/PRIORITIES.md` Priority 2). Modelled line-for-line on the
4
+ * scout's report (`scout-report.ts`): fail closed, every problem reported at
5
+ * once, stable reasons, byte caps, control-character rejection on every
6
+ * string.
7
+ *
8
+ * The proof is a distinct deliverable from the terminal handoff every
9
+ * attempt writes (including `failed` ones, whose lists are deliberately
10
+ * compacted prose) — only a `completed` attempt owes one, and the plane
11
+ * judges it. A missing or malformed proof never fails the attempt or
12
+ * touches the commit; it only keeps the verdict off "verified".
13
+ *
14
+ * `adjudicate` is pure and does no I/O: every fact it reasons over — whether
15
+ * the proof artifact exists, whether the handoff and terminal diff exist,
16
+ * what the sealed diff-stat says, whether a screenshot's bytes actually are
17
+ * a PNG or JPEG, whether an approved verification command ran and what it
18
+ * returned — is gathered by the caller (`builder.ts`) and handed in as data,
19
+ * so the review itself stays testable without a filesystem or a database.
20
+ */
21
+ import { hasForbiddenControls } from "./decision.js";
22
+ import { EVIDENCE_KINDS } from "./scope.js";
23
+ /** Shared by preflight and final adjudication; presentation text is never a rubric. */
24
+ function criterionContractProblems(approved, answer) {
25
+ if (answer === undefined)
26
+ return [{ kind: "missing", message: `the proof does not answer approved criterion "${approved.id}"` }];
27
+ const problems = [];
28
+ if (answer.statement.trim() !== approved.statement.trim()) {
29
+ problems.push({ kind: "statement", message: `criterion "${approved.id}" was signed as "${approved.statement}" and the proof restates it as "${answer.statement}"` });
30
+ }
31
+ for (const kind of approved.evidence) {
32
+ if (!answer.evidence.some(ref => ref.kind === kind)) {
33
+ problems.push({ kind: "evidence", message: `criterion "${approved.id}" requires ${kind} evidence, which the proof does not reference` });
34
+ }
35
+ }
36
+ return problems;
37
+ }
38
+ /** Only submission defects. A truthful not-met answer is not malformed. */
39
+ export function proofSubmissionProblems(proof, rubric) {
40
+ const answers = new Map(proof.criteria.map(one => [one.id, one]));
41
+ const problems = rubric.flatMap(one => criterionContractProblems(one, answers.get(one.id)).map(problem => problem.message));
42
+ const checks = new Set(proof.checks.map(one => one.command));
43
+ const changed = new Set(proof.changed);
44
+ const shots = new Set(proof.screenshots.map(one => one.path));
45
+ for (const criterion of proof.criteria) {
46
+ if (criterion.verdict === "pending-verification" && !rubric.some(one => one.id === criterion.id && one.evidence.includes("check"))) {
47
+ problems.push(`criterion ${criterion.id} can wait for the final check only when its signed requirements include check evidence`);
48
+ }
49
+ for (const evidence of criterion.evidence) {
50
+ const resolves = evidence.kind === "check" ? checks.has(evidence.ref)
51
+ : evidence.kind === "changed-path" ? changed.has(evidence.ref)
52
+ : evidence.kind === "screenshot" ? shots.has(evidence.ref) : true;
53
+ if (!resolves)
54
+ problems.push(`criterion ${criterion.id} names a ${evidence.kind} ref that resolves to nothing — ${JSON.stringify(evidence.ref)}`);
55
+ }
56
+ }
57
+ problems.push(...failedMetChecks(proof).map(failedCheckWords));
58
+ return problems;
59
+ }
60
+ /** Caps are BYTES of UTF-8, matching the scout report's rule: a payload is
61
+ * the same size whatever script it is written in. */
62
+ export const PROOF_LIMITS = {
63
+ payload: 64 * 1024,
64
+ criteria: 12,
65
+ criterionId: 40,
66
+ criterionStatement: 300,
67
+ criterionHow: 500,
68
+ evidencePerCriterion: 4,
69
+ evidenceRef: 300,
70
+ checks: 12,
71
+ checkCommand: 300,
72
+ checkSummary: 300,
73
+ changed: 64,
74
+ changedPath: 300,
75
+ caveats: 8,
76
+ caveat: 300,
77
+ screenshots: 8,
78
+ screenshotPath: 300,
79
+ screenshotCaption: 300,
80
+ };
81
+ function refuse(reason, message) {
82
+ return { ok: false, problems: [{ reason, message }] };
83
+ }
84
+ function describe(value) {
85
+ if (value === undefined)
86
+ return "nothing";
87
+ if (value === null)
88
+ return "null";
89
+ if (typeof value === "string")
90
+ return `a ${value.length}-char string`;
91
+ return `a ${Array.isArray(value) ? "array" : typeof value}`;
92
+ }
93
+ function prose(value, field, cap, problems) {
94
+ if (value === undefined || value === null || (typeof value === "string" && value.trim() === "")) {
95
+ problems.push({ reason: `missing-${field}`, message: `${field} is required` });
96
+ return null;
97
+ }
98
+ if (typeof value !== "string") {
99
+ problems.push({ reason: `bad-${field}`, message: `${field} must be a string (got ${describe(value)})` });
100
+ return null;
101
+ }
102
+ if (Buffer.byteLength(value, "utf8") > cap) {
103
+ problems.push({ reason: `${field}-too-long`, message: `${field} is over ${cap} bytes` });
104
+ return null;
105
+ }
106
+ if (hasForbiddenControls(value)) {
107
+ problems.push({ reason: `${field}-controls`, message: `${field} carries control characters that could become terminal escapes` });
108
+ return null;
109
+ }
110
+ return value;
111
+ }
112
+ /** A repository-relative path: no leading slash, no drive letter, no `.`/`..`
113
+ * segment, no backslash, no control character. The same shape
114
+ * `readVerifiedArtifact` enforces on an evidence key, applied here to a
115
+ * path an agent claims rather than one the machine already wrote. */
116
+ function relativePath(value, field, cap, problems) {
117
+ const raw = prose(value, field, cap, problems);
118
+ if (raw === null)
119
+ return null;
120
+ const segments = raw.split("/");
121
+ const wellFormed = !raw.startsWith("/") &&
122
+ segments.every(segment => segment.length > 0 && segment !== "." && segment !== ".." && !segment.includes("\\"));
123
+ if (!wellFormed) {
124
+ problems.push({ reason: `${field}-not-relative`, message: `${field} must be a normalized repository-relative path (got ${describe(value)})` });
125
+ return null;
126
+ }
127
+ return raw;
128
+ }
129
+ /** `evidence` is optional on a criterion (absent → `[]`, exactly like every
130
+ * other list here) — the mandatory PRESENCE of an answer for a SIGNED
131
+ * criterion is `adjudicate`'s concern, not the parser's; this only proves
132
+ * the shape of what is there. */
133
+ function parseEvidenceRefs(value, field, problems) {
134
+ if (value === undefined || value === null)
135
+ return [];
136
+ if (!Array.isArray(value)) {
137
+ problems.push({ reason: `bad-${field}`, message: `${field} must be an array (got ${describe(value)})` });
138
+ return null;
139
+ }
140
+ if (value.length > PROOF_LIMITS.evidencePerCriterion) {
141
+ problems.push({ reason: `${field}-too-many`, message: `${field} lists ${value.length} — cap is ${PROOF_LIMITS.evidencePerCriterion}` });
142
+ return null;
143
+ }
144
+ const refs = [];
145
+ let bad = false;
146
+ for (const [index, entry] of value.entries()) {
147
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
148
+ problems.push({ reason: `${field}[${index}]-shape`, message: `${field}[${index}] must be an object` });
149
+ bad = true;
150
+ continue;
151
+ }
152
+ const one = entry;
153
+ const kind = one["kind"];
154
+ if (typeof kind !== "string" || !EVIDENCE_KINDS.includes(kind)) {
155
+ problems.push({
156
+ reason: `${field}[${index}]-bad-kind`,
157
+ message: `${field}[${index}].kind must draw from ${EVIDENCE_KINDS.join(", ")} (got ${describe(kind)})`,
158
+ });
159
+ bad = true;
160
+ continue;
161
+ }
162
+ const ref = prose(one["ref"], `${field}[${index}].ref`, PROOF_LIMITS.evidenceRef, problems);
163
+ if (ref === null) {
164
+ bad = true;
165
+ continue;
166
+ }
167
+ refs.push({ kind: kind, ref });
168
+ }
169
+ return bad ? null : refs;
170
+ }
171
+ function parseCriteria(value, problems) {
172
+ if (value === undefined || value === null)
173
+ return [];
174
+ if (!Array.isArray(value)) {
175
+ problems.push({ reason: "bad-criteria", message: `criteria must be an array (got ${describe(value)})` });
176
+ return [];
177
+ }
178
+ if (value.length > PROOF_LIMITS.criteria) {
179
+ problems.push({ reason: "criteria-too-many", message: `criteria lists ${value.length} — cap is ${PROOF_LIMITS.criteria}` });
180
+ return [];
181
+ }
182
+ const criteria = [];
183
+ const seen = new Set();
184
+ for (const [index, entry] of value.entries()) {
185
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
186
+ problems.push({ reason: `criteria[${index}]-shape`, message: `criteria[${index}] must be an object` });
187
+ continue;
188
+ }
189
+ const one = entry;
190
+ const id = prose(one["id"], `criteria[${index}].id`, PROOF_LIMITS.criterionId, problems);
191
+ if (id !== null && seen.has(id)) {
192
+ problems.push({ reason: `criteria[${index}]-duplicate-id`, message: `criterion id "${id}" appears twice` });
193
+ }
194
+ else if (id !== null) {
195
+ seen.add(id);
196
+ }
197
+ const statement = prose(one["statement"], `criteria[${index}].statement`, PROOF_LIMITS.criterionStatement, problems);
198
+ const how = prose(one["how"], `criteria[${index}].how`, PROOF_LIMITS.criterionHow, problems);
199
+ const verdict = one["verdict"];
200
+ if (verdict !== "met" && verdict !== "not-met" && verdict !== "not-checked" && verdict !== "pending-verification") {
201
+ problems.push({
202
+ reason: `criteria[${index}]-bad-verdict`,
203
+ message: `criteria[${index}].verdict must be "met", "not-met", "not-checked", or "pending-verification" (got ${describe(verdict)})`,
204
+ });
205
+ continue;
206
+ }
207
+ const evidence = parseEvidenceRefs(one["evidence"], `criteria[${index}].evidence`, problems);
208
+ if (id === null || statement === null || how === null || evidence === null)
209
+ continue;
210
+ criteria.push({ id, statement, how, verdict, evidence });
211
+ }
212
+ return criteria;
213
+ }
214
+ function parseChecks(value, problems) {
215
+ if (value === undefined || value === null)
216
+ return [];
217
+ if (!Array.isArray(value)) {
218
+ problems.push({ reason: "bad-checks", message: `checks must be an array (got ${describe(value)})` });
219
+ return [];
220
+ }
221
+ if (value.length > PROOF_LIMITS.checks) {
222
+ problems.push({ reason: "checks-too-many", message: `checks lists ${value.length} — cap is ${PROOF_LIMITS.checks}` });
223
+ return [];
224
+ }
225
+ const checks = [];
226
+ for (const [index, entry] of value.entries()) {
227
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
228
+ problems.push({ reason: `checks[${index}]-shape`, message: `checks[${index}] must be an object` });
229
+ continue;
230
+ }
231
+ const one = entry;
232
+ const command = prose(one["command"], `checks[${index}].command`, PROOF_LIMITS.checkCommand, problems);
233
+ const summary = prose(one["summary"], `checks[${index}].summary`, PROOF_LIMITS.checkSummary, problems);
234
+ const exitCode = one["exitCode"];
235
+ if (typeof exitCode !== "number" || !Number.isInteger(exitCode) || exitCode < 0 || exitCode > 255) {
236
+ problems.push({
237
+ reason: `checks[${index}]-bad-exit-code`,
238
+ message: `checks[${index}].exitCode must be an integer 0-255 (got ${describe(exitCode)})`,
239
+ });
240
+ continue;
241
+ }
242
+ if (command === null || summary === null)
243
+ continue;
244
+ checks.push({ command, summary, exitCode });
245
+ }
246
+ return checks;
247
+ }
248
+ function parseStringList(value, field, cap, itemCap, problems) {
249
+ if (value === undefined || value === null)
250
+ return [];
251
+ if (!Array.isArray(value)) {
252
+ problems.push({ reason: `bad-${field}`, message: `${field} must be an array (got ${describe(value)})` });
253
+ return [];
254
+ }
255
+ if (value.length > cap) {
256
+ problems.push({ reason: `${field}-too-many`, message: `${field} lists ${value.length} — cap is ${cap}` });
257
+ return [];
258
+ }
259
+ const out = [];
260
+ for (const [index, entry] of value.entries()) {
261
+ const item = prose(entry, `${field}[${index}]`, itemCap, problems);
262
+ if (item !== null)
263
+ out.push(item);
264
+ }
265
+ return out;
266
+ }
267
+ function parseScreenshots(value, problems) {
268
+ if (value === undefined || value === null)
269
+ return [];
270
+ if (!Array.isArray(value)) {
271
+ problems.push({ reason: "bad-screenshots", message: `screenshots must be an array (got ${describe(value)})` });
272
+ return [];
273
+ }
274
+ if (value.length > PROOF_LIMITS.screenshots) {
275
+ problems.push({ reason: "screenshots-too-many", message: `screenshots lists ${value.length} — cap is ${PROOF_LIMITS.screenshots}` });
276
+ return [];
277
+ }
278
+ const screenshots = [];
279
+ const seen = new Set();
280
+ for (const [index, entry] of value.entries()) {
281
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
282
+ problems.push({ reason: `screenshots[${index}]-shape`, message: `screenshots[${index}] must be an object` });
283
+ continue;
284
+ }
285
+ const one = entry;
286
+ const path = relativePath(one["path"], `screenshots[${index}].path`, PROOF_LIMITS.screenshotPath, problems);
287
+ const caption = prose(one["caption"], `screenshots[${index}].caption`, PROOF_LIMITS.screenshotCaption, problems);
288
+ if (path !== null && seen.has(path)) {
289
+ problems.push({ reason: `screenshots[${index}]-duplicate-path`, message: `screenshot path "${path}" appears twice` });
290
+ continue;
291
+ }
292
+ if (path === null || caption === null)
293
+ continue;
294
+ seen.add(path);
295
+ screenshots.push({ path, caption });
296
+ }
297
+ return screenshots;
298
+ }
299
+ export function parseProof(raw) {
300
+ if (Buffer.byteLength(raw, "utf8") > PROOF_LIMITS.payload) {
301
+ return refuse("too-large", `the payload is over ${PROOF_LIMITS.payload} bytes`);
302
+ }
303
+ let parsed;
304
+ try {
305
+ parsed = JSON.parse(raw);
306
+ }
307
+ catch (error) {
308
+ return refuse("not-json", `the payload is not JSON: ${error instanceof Error ? error.message : String(error)}`);
309
+ }
310
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
311
+ return refuse("not-an-object", "the payload must be one JSON object");
312
+ }
313
+ const body = parsed;
314
+ const problems = [];
315
+ if (body["version"] !== 1) {
316
+ problems.push({ reason: "bad-version", message: `version must be 1 (got ${describe(body["version"])})` });
317
+ }
318
+ const criteria = parseCriteria(body["criteria"], problems);
319
+ const checks = parseChecks(body["checks"], problems);
320
+ const changed = parseStringList(body["changed"], "changed", PROOF_LIMITS.changed, PROOF_LIMITS.changedPath, problems);
321
+ const caveats = parseStringList(body["caveats"], "caveats", PROOF_LIMITS.caveats, PROOF_LIMITS.caveat, problems);
322
+ const screenshots = parseScreenshots(body["screenshots"], problems);
323
+ if (problems.length > 0)
324
+ return { ok: false, problems };
325
+ return {
326
+ ok: true,
327
+ proof: { version: 1, criteria, checks, changed, caveats, screenshots },
328
+ };
329
+ }
330
+ /** Re-serialize the validated shape, never the agent's raw bytes (the
331
+ * scout report's rule, `scout.ts`): what is stored and later hash-verified
332
+ * is exactly what this parser admitted, key order and all. */
333
+ export function serializeProof(proof) {
334
+ return JSON.stringify(proof, null, 2);
335
+ }
336
+ function caveatNames(caveat, id) {
337
+ const escaped = id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
338
+ return new RegExp(`(^|[^\\p{L}\\p{N}_-])${escaped}(?![\\p{L}\\p{N}_-])`, "u").test(caveat);
339
+ }
340
+ /** Every (caveat, criterion) pair where the caveat names a criterion the
341
+ * proof marks `met` — the proof contradicts itself and cannot verify. In
342
+ * caveat order, then criterion order; `[]` for a proof at peace with its
343
+ * own caveats. */
344
+ export function blockingCaveats(proof) {
345
+ const met = proof.criteria.filter(one => one.verdict === "met" || one.verdict === "pending-verification");
346
+ const out = [];
347
+ proof.caveats.forEach((caveat, index) => {
348
+ for (const criterion of met)
349
+ if (caveatNames(caveat, criterion.id))
350
+ out.push({ caveat, index, criterionId: criterion.id });
351
+ });
352
+ return out;
353
+ }
354
+ /**
355
+ * Every check a criterion marked `met` cites that the proof's own `checks`
356
+ * list says exited nonzero (proof preflight closure): a met criterion
357
+ * resting on a failing command is not evidence, it is a contradiction the
358
+ * adjudicator fails the row for. A not-met or not-checked criterion may
359
+ * cite a failed check honestly — it is reporting, not claiming — so only
360
+ * `met` and `pending-verification` count here: pending asserts the agent's
361
+ * own checks already passed. A ref that resolves to no check is a different
362
+ * problem (an unresolved ref), not this one. In criterion order, then
363
+ * evidence order.
364
+ */
365
+ export function failedMetChecks(proof) {
366
+ const checksByCommand = new Map(proof.checks.map(c => [c.command, c]));
367
+ const out = [];
368
+ for (const criterion of proof.criteria) {
369
+ if (criterion.verdict !== "met" && criterion.verdict !== "pending-verification")
370
+ continue;
371
+ for (const evidence of criterion.evidence) {
372
+ if (evidence.kind !== "check")
373
+ continue;
374
+ const check = checksByCommand.get(evidence.ref);
375
+ if (check !== undefined && check.exitCode !== 0)
376
+ out.push({ criterionId: criterion.id, ref: evidence.ref, exitCode: check.exitCode });
377
+ }
378
+ }
379
+ return out;
380
+ }
381
+ /** The one actionable fact a failed check citation is reported as — the
382
+ * adjudicator's matrix detail and the agent's exit preflight say exactly
383
+ * this, so what the preflight refuses is what the plane would have said. */
384
+ export function failedCheckWords(one) {
385
+ return `criterion "${one.criterionId}"'s check "${one.ref}" exited ${one.exitCode}`;
386
+ }
387
+ /** The leading tag list of a caveat — `c1: …`, `c1, c4: …`, `(c2) …` — as
388
+ * the tokens it names before its first colon or after its parentheses.
389
+ * A caveat with no such prefix has no tags; its attribution rests on any
390
+ * known id appearing as a standalone token anywhere in it. */
391
+ function leadingTags(caveat) {
392
+ const separator = String.raw `(?:\s*[,/&+]\s*(?:and\s+)?|\s+and\s+)`;
393
+ const prefixed = new RegExp(String.raw `^\s*\(?\s*([\p{L}\p{N}_-]+(?:${separator}[\p{L}\p{N}_-]+)*)\s*\)?\s*:`, "u").exec(caveat);
394
+ if (prefixed === null)
395
+ return [];
396
+ return prefixed[1].split(new RegExp(separator, "u")).map(one => one.trim()).filter(one => one !== "");
397
+ }
398
+ export function caveatAttributionProblems(proof, signedIds = []) {
399
+ const own = new Set(proof.criteria.map(one => one.id));
400
+ const known = new Set(signedIds.length > 0 ? signedIds : [...own]);
401
+ const out = [];
402
+ proof.caveats.forEach((caveat, index) => {
403
+ const named = [...known].filter(id => caveatNames(caveat, id));
404
+ const tags = leadingTags(caveat);
405
+ const unknown = tags.filter(tag => !known.has(tag));
406
+ const answered = signedIds.length > 0 ? unknown.filter(tag => own.has(tag)) : [];
407
+ if (unknown.length > 0)
408
+ out.push({ caveat, index, kind: "unknown", tags: unknown, ...(answered.length > 0 ? { answered } : {}) });
409
+ else if (named.length === 0)
410
+ out.push({ caveat, index, kind: "unassigned", tags: [] });
411
+ });
412
+ return out;
413
+ }
414
+ /** The words one attribution problem refuses in — shared by the plane's
415
+ * adjudication and the agent's exit preflight so both say the same thing. */
416
+ export function caveatAttributionWords(problem) {
417
+ if (problem.kind !== "unknown") {
418
+ return `caveat ${problem.index + 1} names no criterion — every caveat is an exception to exactly one signed criterion, named by its exact id (an unrelated idea belongs in the handoff's follow-ups): ${problem.caveat}`;
419
+ }
420
+ const quoted = problem.tags.map(tag => `"${tag}"`).join(", ");
421
+ const answered = problem.answered ?? [];
422
+ return answered.length > 0 && answered.length === problem.tags.length
423
+ ? `caveat ${problem.index + 1} names ${quoted}, a criterion the proof authored for itself that nobody signed — a proof-only id is no signed authority; every caveat names a signed criterion's exact id: ${problem.caveat}`
424
+ : `caveat ${problem.index + 1} names ${quoted}, which is no signed or answered criterion — every caveat names an exact criterion id: ${problem.caveat}`;
425
+ }
426
+ /** v39: the floor a criterion's screenshot evidence must clear — "real,
427
+ * not placeholder-sized" in exact numbers (scope: "meaningful byte size
428
+ * and at least 320 by 200 dimensions"). */
429
+ export const SCREENSHOT_EVIDENCE_MIN_BYTES = 1024;
430
+ export const SCREENSHOT_EVIDENCE_MIN_WIDTH = 320;
431
+ export const SCREENSHOT_EVIDENCE_MIN_HEIGHT = 200;
432
+ export function changedListProblems(changed, stat) {
433
+ if (stat === null || !stat.captured || stat.truncated)
434
+ return { problems: [], recoverable: false, sealed: null };
435
+ const claimed = new Set(changed);
436
+ const problems = [];
437
+ let unexplained = false;
438
+ for (const path of changed) {
439
+ if (stat.paths.has(path))
440
+ continue;
441
+ const destination = stat.renames?.get(path);
442
+ if (destination !== undefined && stat.paths.has(destination)) {
443
+ problems.push(`changed lists ${JSON.stringify(path)}, the old name of a move the sealed diff records once as ${JSON.stringify(destination)} — list the destination only`);
444
+ }
445
+ else {
446
+ unexplained = true;
447
+ problems.push(`changed lists ${JSON.stringify(path)}, which the sealed diff does not contain`);
448
+ }
449
+ }
450
+ for (const path of stat.paths) {
451
+ if (!claimed.has(path))
452
+ problems.push(`changed omits ${JSON.stringify(path)}, which the sealed diff contains`);
453
+ }
454
+ const sealed = [...stat.paths].sort();
455
+ return { problems, recoverable: problems.length > 0 && !unexplained, sealed };
456
+ }
457
+ /** What a receipt-only correction may never touch: the exact criterion
458
+ * id/verdict pairs the agent submitted (comment 397, run 1648). A
459
+ * correction restates a signed statement or repairs an evidence reference
460
+ * against the exact rubric; it cannot move an answer (not-met or
461
+ * not-checked becoming pending-verification, or any other change of
462
+ * word), drop a criterion — an extra negative answer is a finding, not a
463
+ * formatting defect — or add one. One line per frozen pair broken. */
464
+ export function frozenCriterionProblems(submitted, corrected) {
465
+ const problems = [];
466
+ const after = new Map(corrected.criteria.map(one => [one.id, one.verdict]));
467
+ for (const prior of submitted.criteria) {
468
+ const verdict = after.get(prior.id);
469
+ if (verdict === undefined) {
470
+ problems.push(`criterion ${prior.id} (${prior.verdict}) was dropped; every submitted criterion and its verdict are frozen by a receipt-only correction`);
471
+ }
472
+ else if (verdict !== prior.verdict) {
473
+ problems.push(`criterion ${prior.id} was submitted as ${prior.verdict}; a receipt-only correction cannot change it to ${verdict}`);
474
+ }
475
+ }
476
+ const before = new Set(submitted.criteria.map(one => one.id));
477
+ for (const one of corrected.criteria) {
478
+ if (!before.has(one.id))
479
+ problems.push(`criterion ${one.id} was added; a receipt-only correction answers exactly the submitted criteria`);
480
+ }
481
+ return problems;
482
+ }
483
+ /** Whether two readings of the sealed diff-stat state the same facts:
484
+ * the same capture standing, the same paths and the same rename
485
+ * provenance. Settlement reads the stat once before the receipt
486
+ * correction and re-reads it after that correction and after the final
487
+ * gate; a reading that differs is never adjudicated from the cached one. */
488
+ export function sameDiffStatFacts(a, b) {
489
+ if (a === null || b === null)
490
+ return a === b;
491
+ const restate = (one) => JSON.stringify({
492
+ captured: one.captured,
493
+ truncated: one.truncated,
494
+ paths: [...one.paths].sort(),
495
+ renames: [...(one.renames ?? [])].sort(),
496
+ });
497
+ return restate(a) === restate(b);
498
+ }
499
+ export const GOAL_ASSESSMENT_PENDING = "The saved evidence is awaiting independent goal assessment.";
500
+ /** Legacy folds could only lower a verdict. Direct assessment also records an
501
+ * ordinary wait between green machine checks and the goal judgement. */
502
+ export function reviewConflict(matrix, machine, verdict) {
503
+ return matrix.some(row => row.assessment !== undefined) ? matrix.some(row => row.review?.judgement === "contradicts")
504
+ : machine !== null && machine !== verdict;
505
+ }
506
+ /** Human sign-off is owed, with no other recorded evidence failure. This is
507
+ * presentation only; it never changes a verdict or grants acceptance. */
508
+ export function manualReviewOnly(proof) {
509
+ return proof?.verdict === "short" && (proof.matrix === undefined || (proof.matrix.some(row => row.state === "manual-review")
510
+ && proof.matrix.every(row => row.state === "pass" || row.state === "manual-review")))
511
+ && proof.reasons.length > 0
512
+ && proof.reasons.every(reason => /^criterion "[^"]+" requires manual-review evidence — an operator must accept it before this can verify$/.test(reason));
513
+ }
514
+ /** Set equality, exactly — the "must equal the complete sealed git diff
515
+ * exactly" rule (v39) needs both directions, unlike the legacy subset
516
+ * check. */
517
+ function setEquals(a, b) {
518
+ if (a.size !== b.size)
519
+ return false;
520
+ for (const one of a)
521
+ if (!b.has(one))
522
+ return false;
523
+ return true;
524
+ }
525
+ /**
526
+ * The criterion-to-evidence matrix, computed once and shared by every
527
+ * surface (v39) — best-effort from whatever is available: `proof` is null
528
+ * when no proof parsed at all, in which case every approved criterion is
529
+ * simply unanswered. `[]` whenever no rubric was signed, so a
530
+ * grandfathered run renders no matrix at all rather than a wall of
531
+ * "missing" rows nobody signed up for.
532
+ */
533
+ function criterionMatrix(approvedCriteria, proof, diffStat, screenshots) {
534
+ if (approvedCriteria.length === 0)
535
+ return [];
536
+ const answers = new Map((proof?.criteria ?? []).map(c => [c.id, c]));
537
+ const screenshotsByPath = new Map(screenshots.map(s => [s.path, s]));
538
+ const checksByCommand = new Map((proof?.checks ?? []).map(c => [c.command, c]));
539
+ const changedSet = new Set(proof?.changed ?? []);
540
+ // null = cannot know either way (diff unavailable or truncated) — never
541
+ // treated as agreement, per "an unavailable or truncated diff cannot verify".
542
+ const diffExact = diffStat !== null && diffStat.captured && !diffStat.truncated ? setEquals(changedSet, diffStat.paths) : null;
543
+ return approvedCriteria.map((approved) => {
544
+ const base = { id: approved.id, statement: approved.statement, requiredEvidence: approved.evidence, review: null };
545
+ const answer = answers.get(approved.id);
546
+ const contract = criterionContractProblems(approved, answer);
547
+ if (answer === undefined) {
548
+ return { ...base, state: "missing", detail: contract.map(one => one.message), answered: [] };
549
+ }
550
+ if (answer.statement.trim() !== approved.statement.trim()) {
551
+ return {
552
+ ...base,
553
+ state: "failed",
554
+ detail: contract.filter(one => one.kind === "statement").map(one => one.message),
555
+ answered: answer.evidence,
556
+ };
557
+ }
558
+ const detail = [];
559
+ let anyMissing = false;
560
+ let anyFailed = false;
561
+ let anyManual = false;
562
+ // Only adjudicate's trusted successful final check resolves this state.
563
+ // Until then the shared row must not misleadingly read as passed.
564
+ if (answer.verdict === "pending-verification") {
565
+ anyMissing = true;
566
+ detail.push(`criterion "${approved.id}" is waiting for the final check`);
567
+ }
568
+ // The proof's own caveats outrank its verdict word: a criterion marked
569
+ // `met` that a caveat names is not met by the proof's own admission.
570
+ for (const blocking of proof === null ? [] : blockingCaveats(proof)) {
571
+ if (blocking.criterionId !== approved.id)
572
+ continue;
573
+ anyFailed = true;
574
+ detail.push(`criterion "${approved.id}" is marked met, but caveat ${blocking.index + 1} admits an exception to it: ${blocking.caveat}`);
575
+ }
576
+ for (const kind of approved.evidence) {
577
+ const ref = answer.evidence.find(e => e.kind === kind);
578
+ if (ref === undefined) {
579
+ anyMissing = true;
580
+ detail.push(...contract.filter(one => one.kind === "evidence" && one.message.includes(`requires ${kind} evidence`)).map(one => one.message));
581
+ continue;
582
+ }
583
+ if (kind === "manual-review") {
584
+ anyManual = true;
585
+ detail.push(`criterion "${approved.id}" requires manual-review evidence — an operator must accept it before this can verify`);
586
+ continue;
587
+ }
588
+ if (kind === "check") {
589
+ const check = checksByCommand.get(ref.ref);
590
+ if (check === undefined) {
591
+ anyFailed = true;
592
+ detail.push(`criterion "${approved.id}"'s check evidence "${ref.ref}" does not match any reported check`);
593
+ }
594
+ else if (check.exitCode !== 0) {
595
+ anyFailed = true;
596
+ detail.push(failedCheckWords({ criterionId: approved.id, ref: ref.ref, exitCode: check.exitCode }));
597
+ }
598
+ continue;
599
+ }
600
+ if (kind === "screenshot") {
601
+ const shot = screenshotsByPath.get(ref.ref);
602
+ if (shot === undefined || !shot.ok) {
603
+ anyFailed = true;
604
+ detail.push(`criterion "${approved.id}"'s screenshot evidence "${ref.ref}" could not be verified${shot?.problem ? `: ${shot.problem}` : ""}`);
605
+ }
606
+ else if ((shot.bytes ?? 0) < SCREENSHOT_EVIDENCE_MIN_BYTES ||
607
+ shot.dims == null ||
608
+ shot.dims.width < SCREENSHOT_EVIDENCE_MIN_WIDTH ||
609
+ shot.dims.height < SCREENSHOT_EVIDENCE_MIN_HEIGHT) {
610
+ anyFailed = true;
611
+ detail.push(`criterion "${approved.id}"'s screenshot "${ref.ref}" is missing or placeholder-sized (needs a real PNG or JPEG of at least ${SCREENSHOT_EVIDENCE_MIN_BYTES} bytes and ${SCREENSHOT_EVIDENCE_MIN_WIDTH}x${SCREENSHOT_EVIDENCE_MIN_HEIGHT})`);
612
+ }
613
+ continue;
614
+ }
615
+ // changed-path
616
+ if (!changedSet.has(ref.ref)) {
617
+ anyFailed = true;
618
+ detail.push(`criterion "${approved.id}"'s changed-path evidence "${ref.ref}" is not among the proof's claimed changed paths`);
619
+ }
620
+ else if (diffExact !== true) {
621
+ anyFailed = true;
622
+ detail.push(diffExact === null
623
+ ? `criterion "${approved.id}"'s changed-path evidence cannot verify — the sealed diff is unavailable or truncated`
624
+ : `criterion "${approved.id}"'s changed-path evidence cannot verify — the proof's changed paths do not exactly match the sealed diff`);
625
+ }
626
+ }
627
+ const state = anyMissing ? "missing" : anyFailed ? "failed" : anyManual ? "manual-review" : "pass";
628
+ return { ...base, state, detail, answered: answer.evidence };
629
+ });
630
+ }
631
+ /** Evidence readiness only. The signed criteria are copied from the scope,
632
+ * never restated by the builder; an independent assessment must settle them. */
633
+ function assessCapturedEvidence(input) {
634
+ const completeDiff = input.diffStat !== null && input.diffStat.captured && !input.diffStat.truncated;
635
+ const check = input.verifyCommand;
636
+ const checked = check.configured && check.ran && check.exitCode === 0;
637
+ let machineVerdict = checked ? "verified" : "attested";
638
+ const problems = [];
639
+ if (!input.handoffPresent)
640
+ problems.push("the terminal handoff is missing");
641
+ if (!input.terminalDiffPresent || input.terminalDiffCaptureStatus !== "ok")
642
+ problems.push("the machine-captured diff is missing or failed to capture");
643
+ if (!completeDiff)
644
+ problems.push("the sealed diff is unavailable or truncated; the exact candidate cannot be assessed");
645
+ if (check.configured && !check.ran)
646
+ problems.push(verificationFailureWords(check.failure));
647
+ if (problems.length > 0)
648
+ machineVerdict = "short";
649
+ if (check.configured && check.ran && check.exitCode !== 0) {
650
+ machineVerdict = "refuted";
651
+ problems.push(`the repository's approved verification command exited ${check.exitCode}`);
652
+ }
653
+ const matrix = (input.approvedCriteria ?? []).map((criterion) => {
654
+ const detail = [];
655
+ const answered = [];
656
+ let missing = false, failed = false, manual = false;
657
+ for (const kind of criterion.evidence) {
658
+ if (kind === "check") {
659
+ if (checked)
660
+ answered.push({ kind, ref: input.verificationCommand ?? "approved project check" });
661
+ else {
662
+ failed ||= check.configured && check.ran && check.exitCode !== 0;
663
+ missing ||= !check.configured || !check.ran;
664
+ detail.push(`criterion "${criterion.id}" needs a passing approved project check`);
665
+ }
666
+ }
667
+ else if (kind === "changed-path") {
668
+ if (completeDiff) {
669
+ for (const path of input.diffStat.paths)
670
+ answered.push({ kind, ref: path });
671
+ }
672
+ else {
673
+ missing = true;
674
+ detail.push(`criterion "${criterion.id}" needs the complete saved source inventory`);
675
+ }
676
+ }
677
+ else if (kind === "screenshot") {
678
+ // Screenshots must have been explicitly captured and validated, never
679
+ // guessed from arbitrary files or inferred from a passing test.
680
+ const shots = input.screenshots.filter(shot => shot.ok && (shot.bytes ?? 0) >= SCREENSHOT_EVIDENCE_MIN_BYTES &&
681
+ shot.dims != null && shot.dims.width >= SCREENSHOT_EVIDENCE_MIN_WIDTH && shot.dims.height >= SCREENSHOT_EVIDENCE_MIN_HEIGHT);
682
+ if (shots.length === 0) {
683
+ missing = true;
684
+ detail.push(`criterion "${criterion.id}" needs a captured screenshot of the required behavior`);
685
+ }
686
+ else
687
+ for (const shot of shots)
688
+ answered.push({ kind, ref: shot.path });
689
+ }
690
+ else {
691
+ manual = true;
692
+ detail.push(`criterion "${criterion.id}" requires manual-review evidence — an operator must accept it before this can verify`);
693
+ }
694
+ }
695
+ const evidenceState = failed ? "failed" : missing ? "missing" : manual ? "manual-review" : "pass";
696
+ const coverage = input.reviewContext?.find(one => one.id === criterion.id);
697
+ return {
698
+ id: criterion.id, statement: criterion.statement, requiredEvidence: criterion.evidence,
699
+ assessment: { evidenceState, detail }, state: evidenceState === "pass" ? "missing" : evidenceState,
700
+ detail: [...detail, `criterion "${criterion.id}" awaits independent goal assessment`], answered, review: null,
701
+ ...(coverage === undefined ? {} : { coverage: { state: coverage.state, inherited: coverage.inherited, items: [...coverage.items], gaps: [...coverage.gaps], priorSupport: coverage.priorSupport } }),
702
+ };
703
+ });
704
+ return { machineVerdict, verdict: machineVerdict === "refuted" ? "refuted" : "short", matrix,
705
+ reasons: problems.length > 0 ? problems : [GOAL_ASSESSMENT_PENDING] };
706
+ }
707
+ /** Only direct assessments can settle a pending goal. Machine failures and
708
+ * required evidence remain authoritative; this never upgrades a legacy proof. */
709
+ function foldGoalAssessment(base, judgements) {
710
+ const byId = new Map(judgements.map(one => [one.id, one]));
711
+ const matrix = base.matrix.map((row) => {
712
+ const evidence = row.assessment;
713
+ const judgement = byId.get(row.id);
714
+ const review = judgement === undefined ? row.review : { judgement: judgement.judgement, note: judgement.note, author: judgement.author };
715
+ const contradicted = review?.judgement === "contradicts";
716
+ const upheld = review?.judgement === "upholds";
717
+ return { ...row, review,
718
+ state: contradicted ? "failed" : evidence.evidenceState === "pass" ? upheld ? "pass" : "missing" : evidence.evidenceState,
719
+ detail: [...evidence.detail, ...(upheld ? [] : [review === null
720
+ ? `criterion "${row.id}" awaits independent goal assessment`
721
+ : `${review.author} ${contradicted ? "contradicts" : "needs more evidence for"} criterion "${row.id}": ${review.note}`])],
722
+ };
723
+ });
724
+ const machineReady = base.machineVerdict === "verified" || base.machineVerdict === "attested";
725
+ const contradicted = matrix.some(row => row.review?.judgement === "contradicts");
726
+ const passed = machineReady && matrix.every(row => row.state === "pass" && row.review?.judgement === "upholds");
727
+ const verdict = base.machineVerdict === "refuted" || contradicted ? "refuted" : passed ? base.machineVerdict : "short";
728
+ return { ...base, verdict, matrix, reasons: passed ? ["The saved evidence and independent assessment satisfy the approved goal."]
729
+ : [...(!machineReady ? base.reasons : []), ...matrix.flatMap(row => row.detail)] };
730
+ }
731
+ function verificationFailureWords(failure) {
732
+ return (() => {
733
+ switch (failure) {
734
+ case "dependency-missing":
735
+ return "the approved verification command could not start because a required project executable was unavailable and no approved recovery was enabled";
736
+ case "setup-stale":
737
+ return "automatic recovery stopped because the project setup or check changed";
738
+ case "setup-failed":
739
+ return "the approved setup command failed during automatic recovery";
740
+ case "tracked-files-changed":
741
+ return "automatic recovery stopped because tracked files no longer matched the built result";
742
+ case "setup-changed-files":
743
+ return "automatic recovery stopped because the setup command changed tracked files after the build";
744
+ case "checkout-moved":
745
+ return "automatic recovery stopped because the checkout moved away from the built commit";
746
+ case "cleanliness-unavailable":
747
+ return "automatic recovery stopped because Toolroll could not confirm that the built checkout was unchanged";
748
+ case "dependency-still-missing":
749
+ return "the required project executable was still unavailable after replaying the approved setup command";
750
+ case "retry-spawn-failed":
751
+ return "the retried verification command could not be started after automatic recovery";
752
+ case "retry-timed-out":
753
+ return "the retried verification command timed out after automatic recovery";
754
+ case "custody-lost":
755
+ return "automatic recovery stopped because this worker no longer owned the build";
756
+ case "timed-out":
757
+ return "the approved verification command timed out before checks finished";
758
+ case "spawn-failed":
759
+ default:
760
+ return "the approved verification command could not be run";
761
+ }
762
+ })();
763
+ }
764
+ /**
765
+ * Ordered rules; the first that fires wins. Every reason is a sentence the
766
+ * surfaces print verbatim — this function is the only place that decides
767
+ * wording, so the task page, run page, board, CLI, and chat cards cannot
768
+ * drift from each other (Priority 2, the "one surface, six places" rule).
769
+ *
770
+ * v39: `input.approvedCriteria` empty runs the rules below byte for byte —
771
+ * the grandfathering promise. Non-empty inserts the rubric rules AFTER the
772
+ * diff-stat and verify-command rules and BEFORE the legacy unmet check,
773
+ * exactly as approved in the v2 plan: a proof that alters a signed
774
+ * criterion's statement is REFUTED (the same severity as any other
775
+ * altered term); an unanswered criterion, an evidence reference that does
776
+ * not resolve, or a criterion capped at manual-review is SHORT (a gap, or
777
+ * a review a machine cannot finish, never a lie). The legacy self-declared
778
+ * `verdict` field still runs afterward and can only downgrade further,
779
+ * never upgrade past what the evidence proved.
780
+ *
781
+ * The changed[] vs. sealed-diff exactness check (review finding, post-v39)
782
+ * runs GLOBALLY, ahead of the rubric rules and independent of whether any
783
+ * criterion cites changed-path evidence at all — a rubric with none of
784
+ * those must not let an untruthful or incomplete changed[] through
785
+ * unchecked. An unavailable or truncated diff-stat cannot prove either
786
+ * direction, so it can only ever produce SHORT, never REFUTED.
787
+ */
788
+ /** An optional screenshot inventory carries no builder-authored completion
789
+ * claims. The existing bounded parser still validates every supplied field. */
790
+ export function artifactManifestOnly(proof) {
791
+ return proof.criteria.length === 0 && proof.checks.length === 0 && proof.changed.length === 0 && proof.caveats.length === 0;
792
+ }
793
+ export function adjudicate(input) {
794
+ const approvedCriteria = input.approvedCriteria ?? [];
795
+ if (input.directAssessment && approvedCriteria.length > 0 && (!input.proofArtifactPresent ||
796
+ (input.proofParse?.ok && artifactManifestOnly(input.proofParse.proof))))
797
+ return assessCapturedEvidence(input);
798
+ const coverageById = new Map((input.reviewContext ?? []).map(one => [one.id, one]));
799
+ const matrixOf = (proof) => criterionMatrix(approvedCriteria, proof, input.diffStat, input.screenshots).map(row => {
800
+ if (input.reviewContext === undefined)
801
+ return row;
802
+ const found = coverageById.get(row.id);
803
+ // A criterion the inventory never covered is a gap the surfaces
804
+ // must show, never a silent "judged from the patch".
805
+ const coverage = found === undefined
806
+ ? { state: "gap", inherited: false, items: [], gaps: ["the sealed review context carries no coverage entry for this criterion"], priorSupport: "none" }
807
+ : { state: found.state, inherited: found.inherited, items: [...found.items], gaps: [...found.gaps], priorSupport: found.priorSupport };
808
+ return { ...row, coverage };
809
+ });
810
+ if (!input.proofArtifactPresent) {
811
+ return { verdict: "short", reasons: ["no proof was written"], matrix: matrixOf(null) };
812
+ }
813
+ if (input.proofParse === null || !input.proofParse.ok) {
814
+ const detail = input.proofParse !== null && !input.proofParse.ok
815
+ ? input.proofParse.problems.map(p => p.message).join("; ")
816
+ : "the proof could not be read";
817
+ return { verdict: "short", reasons: [`the proof is malformed: ${detail}`], matrix: matrixOf(null) };
818
+ }
819
+ const submitted = input.proofParse.proof;
820
+ const finalCheckPassed = input.verifyCommand.configured && input.verifyCommand.ran && input.verifyCommand.exitCode === 0;
821
+ const pendingContractValid = proofSubmissionProblems(submitted, approvedCriteria).length === 0;
822
+ // Resolve only an explicit conditional claim, against THIS run's machine
823
+ // facts. Never infer intent from prose, modify the sealed receipt, or turn
824
+ // a genuine not-met/not-checked answer into success. All evidence, caveat,
825
+ // manual-review and artifact checks below still apply to the resolved copy.
826
+ const proof = {
827
+ ...submitted,
828
+ criteria: submitted.criteria.map(one => one.verdict === "pending-verification" && finalCheckPassed && pendingContractValid &&
829
+ approvedCriteria.some(approved => approved.id === one.id && approved.evidence.includes("check") && approved.statement.trim() === one.statement.trim())
830
+ ? { ...one, verdict: "met" } : one),
831
+ };
832
+ const matrix = matrixOf(proof);
833
+ if (!input.handoffPresent) {
834
+ return { verdict: "short", reasons: ["the terminal handoff is missing"], matrix };
835
+ }
836
+ if (!input.terminalDiffPresent || input.terminalDiffCaptureStatus === "failed") {
837
+ return { verdict: "short", reasons: ["the machine-captured diff is missing or failed to capture"], matrix };
838
+ }
839
+ // The claimed changed[] must equal the COMPLETE sealed diff exactly —
840
+ // globally, whether or not any signed criterion cites changed-path
841
+ // evidence at all (a rubric with none of those would otherwise let an
842
+ // untruthful or incomplete changed[] through unchecked). An unavailable
843
+ // or truncated stat cannot prove either direction, so it cannot verify:
844
+ // short, never refuted — "we could not check" is not "the claim is
845
+ // false" (the same ruling the verify-command facts make below).
846
+ if (input.diffStat === null || !input.diffStat.captured || input.diffStat.truncated) {
847
+ return {
848
+ verdict: "short",
849
+ reasons: ["the sealed diff is unavailable or truncated; the claimed changed paths cannot be verified against it"],
850
+ matrix,
851
+ };
852
+ }
853
+ {
854
+ const diffPaths = input.diffStat.paths;
855
+ const claimed = new Set(proof.changed);
856
+ const overclaimed = proof.changed.filter(path => !diffPaths.has(path));
857
+ if (overclaimed.length > 0) {
858
+ return {
859
+ verdict: "refuted",
860
+ reasons: [`claimed changed path${overclaimed.length > 1 ? "s" : ""} not in the sealed diff: ${overclaimed.join(", ")}`],
861
+ matrix,
862
+ };
863
+ }
864
+ const underclaimed = [...diffPaths].filter(path => !claimed.has(path));
865
+ if (underclaimed.length > 0) {
866
+ return {
867
+ verdict: "short",
868
+ reasons: [`the sealed diff touched path${underclaimed.length > 1 ? "s" : ""} the proof never claimed as changed: ${underclaimed.join(", ")}`],
869
+ matrix,
870
+ };
871
+ }
872
+ }
873
+ // Altering a signed requirement is an integrity contradiction, not an
874
+ // evidence gap. It outranks an environment failure: an unavailable check
875
+ // must never downgrade changed authority from refuted to short.
876
+ if (approvedCriteria.length > 0) {
877
+ const restated = matrix.filter(row => row.detail.some(d => d.includes("was signed as")));
878
+ if (restated.length > 0) {
879
+ return { verdict: "refuted", reasons: restated.flatMap(row => row.detail), matrix };
880
+ }
881
+ }
882
+ // A proof whose caveat admits an exception to a criterion it marks met
883
+ // contradicts itself (atomic authority closure): the verdict must agree
884
+ // with the caveats, and a self-disagreeing proof is conflicting
885
+ // evidence — refuted, never verified, whether or not a rubric was
886
+ // signed. It ranks with the altered-statement rule above: an
887
+ // environment failure must never soften a contradiction to short.
888
+ {
889
+ const blocking = blockingCaveats(proof);
890
+ if (blocking.length > 0) {
891
+ return {
892
+ verdict: "refuted",
893
+ reasons: blocking.map(one => `criterion "${one.criterionId}" is marked met, but caveat ${one.index + 1} admits an exception to it: ${one.caveat}`),
894
+ matrix,
895
+ };
896
+ }
897
+ // EVERY caveat is attributed (final authority closure): one that names
898
+ // no known criterion, or a tag nobody signed, is an exception the
899
+ // verdict cannot be reconciled with — the proof's own words do not
900
+ // say which criterion they qualify, and the plane refutes rather than
901
+ // guesses. Known ids are the signed rubric's alone when one was
902
+ // signed (a proof-authored extra id is no authority); the proof's own
903
+ // only when nothing was signed.
904
+ const unattributed = caveatAttributionProblems(proof, approvedCriteria.map(one => one.id));
905
+ if (unattributed.length > 0) {
906
+ return { verdict: "refuted", reasons: unattributed.map(caveatAttributionWords), matrix };
907
+ }
908
+ }
909
+ // A command that could not exercise the product is missing evidence, not
910
+ // contradictory evidence. This precedes criterion-state checks so the
911
+ // actionable environment cause is never buried under a generic gap.
912
+ if (input.verifyCommand.configured && !input.verifyCommand.ran) {
913
+ const reason = verificationFailureWords(input.verifyCommand.failure);
914
+ return { verdict: "short", reasons: [reason], matrix };
915
+ }
916
+ if (input.verifyCommand.configured && input.verifyCommand.ran && input.verifyCommand.exitCode !== 0) {
917
+ return {
918
+ verdict: "refuted",
919
+ reasons: [
920
+ input.verifyCommand.setupReplayed === true
921
+ ? `the repository's approved verification command exited ${input.verifyCommand.exitCode} after the approved setup command was replayed`
922
+ : `the repository's approved verification command exited ${input.verifyCommand.exitCode}`,
923
+ ],
924
+ matrix,
925
+ };
926
+ }
927
+ if (approvedCriteria.length > 0) {
928
+ // manual-review folds in here too (v39 review finding): a row that
929
+ // needs a human's eyes is never machine-verifiable, so it must never
930
+ // reach "verified" or "attested" on its own — it stays "short" until
931
+ // an operator explicitly accepts the proof (the same act that already
932
+ // lets a short/refuted run read as done).
933
+ const unresolved = matrix.filter(row => row.state === "missing" || row.state === "failed" || row.state === "manual-review");
934
+ if (unresolved.length > 0) {
935
+ return { verdict: "short", reasons: unresolved.flatMap(row => row.detail), matrix };
936
+ }
937
+ }
938
+ const unmet = proof.criteria.filter(c => c.verdict !== "met");
939
+ if (unmet.length > 0) {
940
+ return {
941
+ verdict: "short",
942
+ reasons: unmet.map(c => `criterion "${c.statement}" is ${c.verdict === "not-met" ? "not met" : c.verdict === "pending-verification" ? "waiting for the final check" : "not checked"}`),
943
+ matrix,
944
+ };
945
+ }
946
+ const badScreenshots = input.screenshots.filter(s => !s.ok);
947
+ if (badScreenshots.length > 0) {
948
+ return {
949
+ verdict: "short",
950
+ reasons: badScreenshots.map(s => `claimed screenshot "${s.path}" could not be verified${s.problem ? `: ${s.problem}` : ""}`),
951
+ matrix,
952
+ };
953
+ }
954
+ if (input.verifyCommand.configured && input.verifyCommand.ran && input.verifyCommand.exitCode === 0) {
955
+ return {
956
+ verdict: "verified",
957
+ reasons: [
958
+ input.verifyCommand.setupReplayed === true
959
+ ? "the approved verification command passed after the approved setup command ran"
960
+ : "the approved verification command passed",
961
+ ],
962
+ matrix,
963
+ };
964
+ }
965
+ return { verdict: "attested", reasons: ["the proof agrees with the sealed diff; no verification command is configured to re-run"], matrix };
966
+ }
967
+ /**
968
+ * Folds an independent reviewer's per-criterion judgements into an ALREADY
969
+ * ADJUDICATED result (v40, evidence-review-v1) — pure, and never re-derives
970
+ * verdict facts that were not persisted (verify-command outcome, screenshot
971
+ * bytes): it folds over the STORED result, it does not re-run `adjudicate`.
972
+ *
973
+ * Three rules, and they are the whole contract:
974
+ * - `contradicts` -> refuted. A second reader saying a signed term is not
975
+ * met is the same severity as a proof that altered the term: the
976
+ * matching row becomes `failed`, with the reviewer's note as an
977
+ * attributed `detail` line.
978
+ * - `cannot-tell` changes nothing. Recorded and rendered, never moves the
979
+ * verdict — "we could not check" is not "the claim is false" (the same
980
+ * maxim `adjudicate` already lives by for an unavailable diff or a
981
+ * failed verify-command attempt).
982
+ * - `upholds` never upgrades. A `short` (or `attested`) run stays exactly
983
+ * what it was — this is the law that stops a second model laundering a
984
+ * bad proof, and the reason `foldReview` can only LOWER a verdict, never
985
+ * raise one.
986
+ *
987
+ * A judgement naming a criterion absent from `base.matrix` is ignored —
988
+ * `parseReview` already refuses a payload naming an unsigned id, so this is
989
+ * belt and suspenders, never a live path.
990
+ */
991
+ export function foldReview(base, judgements) {
992
+ if (judgements.length === 0)
993
+ return base;
994
+ if (base.matrix.length > 0 && base.matrix.every(row => row.assessment !== undefined)) {
995
+ return foldGoalAssessment(base, judgements);
996
+ }
997
+ const byId = new Map(judgements.map(j => [j.id, j]));
998
+ let anyContradiction = false;
999
+ const matrix = base.matrix.map((row) => {
1000
+ const judgement = byId.get(row.id);
1001
+ if (judgement === undefined)
1002
+ return row;
1003
+ const review = { judgement: judgement.judgement, note: judgement.note, author: judgement.author };
1004
+ if (judgement.judgement !== "contradicts")
1005
+ return { ...row, review };
1006
+ anyContradiction = true;
1007
+ return {
1008
+ ...row,
1009
+ state: "failed",
1010
+ detail: [...row.detail, `${judgement.author} contradicts criterion "${row.id}": ${judgement.note}`],
1011
+ review,
1012
+ };
1013
+ });
1014
+ if (!anyContradiction)
1015
+ return { ...base, matrix };
1016
+ const contradictions = matrix.filter(row => row.review?.judgement === "contradicts");
1017
+ return {
1018
+ verdict: "refuted",
1019
+ reasons: [...base.reasons, ...contradictions.flatMap(row => (row.review === null ? [] : [`${row.review.author} contradicts criterion "${row.id}": ${row.review.note}`]))],
1020
+ matrix,
1021
+ };
1022
+ }
1023
+ export function semanticCoverage(matrix, policy) {
1024
+ const judged = (word) => matrix.filter(row => row.review?.judgement === word).map(row => row.id);
1025
+ const upheld = judged("upholds");
1026
+ const contradicted = judged("contradicts");
1027
+ const uncertain = judged("cannot-tell");
1028
+ const unreviewed = matrix.filter(row => row.review === null).map(row => row.id);
1029
+ // Every named gap is listed, including one on a row the patch still
1030
+ // judges: what could not be sealed is said, whatever else was.
1031
+ const contextGaps = matrix.flatMap(row => (row.coverage !== undefined && row.coverage.gaps.length > 0 ? [{ id: row.id, gaps: [...row.coverage.gaps] }] : []));
1032
+ const reviewed = matrix.length - unreviewed.length;
1033
+ return {
1034
+ policy,
1035
+ required: policy === "strict" || matrix.some(row => row.assessment !== undefined),
1036
+ total: matrix.length,
1037
+ upheld,
1038
+ contradicted,
1039
+ uncertain,
1040
+ unreviewed,
1041
+ contextGaps,
1042
+ satisfied: matrix.length === 0 || reviewed === 0 ? null : upheld.length === matrix.length,
1043
+ };
1044
+ }
1045
+ /** The coverage in plain words — one line every surface prints the same
1046
+ * way. `[]` for a run with no rubric: nothing to cover. */
1047
+ export function coverageWords(coverage) {
1048
+ if (coverage.total === 0)
1049
+ return [];
1050
+ const lines = [];
1051
+ // No review has folded: nothing is waited for (model review is retired),
1052
+ // so only sealed context gaps are worth a line.
1053
+ if (coverage.satisfied === null) {
1054
+ for (const gap of coverage.contextGaps)
1055
+ lines.push(`context gap ${gap.id}: ${gap.gaps.join("; ")}`);
1056
+ return lines;
1057
+ }
1058
+ const standing = coverage.satisfied === null
1059
+ ? coverage.required ? "required under strict quality — no independent review has settled yet" : "independent review is optional under default quality — none has settled"
1060
+ : coverage.satisfied
1061
+ ? coverage.required ? "required under strict quality — satisfied" : "optional under default quality — every criterion upheld"
1062
+ : coverage.required
1063
+ ? `required under strict quality — NOT satisfied (${[...(coverage.uncertain.length > 0 ? [`cannot-tell never counts: ${coverage.uncertain.join(", ")}`] : []), ...(coverage.contradicted.length > 0 ? [`contradicted: ${coverage.contradicted.join(", ")}`] : []), ...(coverage.unreviewed.length > 0 ? [`unreviewed: ${coverage.unreviewed.join(", ")}`] : [])].join("; ")})`
1064
+ : `optional under default quality — ${coverage.upheld.length}/${coverage.total} upheld${coverage.uncertain.length > 0 ? `, cannot-tell: ${coverage.uncertain.join(", ")}` : ""}${coverage.contradicted.length > 0 ? `, contradicted: ${coverage.contradicted.join(", ")}` : ""}`;
1065
+ lines.push(`semantic coverage: ${coverage.upheld.length}/${coverage.total} upheld by an independent reviewer — ${coverage.policy === "default" && coverage.required ? standing.replaceAll("under strict quality", "for goal assessment") : standing}`);
1066
+ for (const gap of coverage.contextGaps)
1067
+ lines.push(`context gap ${gap.id}: ${gap.gaps.join("; ")}`);
1068
+ return lines;
1069
+ }
1070
+ /** The pass fraction of a criterion matrix — the one number every list
1071
+ * surface (board, chat) needs, shared so "N/M criteria" is computed in
1072
+ * exactly one place (v40 closes the two hand-rolled copies). */
1073
+ export function passFraction(matrix) {
1074
+ return { passed: matrix.filter(row => row.state === "pass").length, total: matrix.length };
1075
+ }
1076
+ /** The matrix in plain lines, for a text surface (the CLI, `brief`) — the
1077
+ * same shared vocabulary `criterionMatrixHtml` renders in the console, so
1078
+ * the words never drift between the two (Priority 2's rule, extended). */
1079
+ export function matrixWords(matrix) {
1080
+ if (matrix.length === 0)
1081
+ return [];
1082
+ const lines = [" acceptance matrix"];
1083
+ for (const row of matrix) {
1084
+ lines.push(` [${row.state}] ${row.id}: ${row.statement} (requires: ${row.requiredEvidence.join(", ")})`);
1085
+ if (row.answered.length > 0) {
1086
+ lines.push(` answered: ${row.answered.map(a => `${a.kind}: ${a.ref}`).join("; ")}`);
1087
+ }
1088
+ for (const detail of row.detail)
1089
+ lines.push(` ${detail}`);
1090
+ if (row.review !== null) {
1091
+ lines.push(` review (${row.review.author}): ${row.review.judgement} — ${row.review.note}`);
1092
+ }
1093
+ if (row.coverage !== undefined) {
1094
+ lines.push(` context: ${coverageStateWords(row.coverage)}`);
1095
+ }
1096
+ }
1097
+ return lines;
1098
+ }
1099
+ /** One criterion's context standing in words — shared by the CLI matrix
1100
+ * and the console badge title so the two never drift. */
1101
+ export function coverageStateWords(coverage) {
1102
+ const prior = coverage.priorSupport === "eligible" ? "; an earlier review of the source may be weighed as context (never as this run's verdict)" : coverage.priorSupport === "invalid" ? "; the earlier source review no longer supports it" : "";
1103
+ switch (coverage.state) {
1104
+ case "patch":
1105
+ return `judged from this run's own patch${coverage.gaps.length > 0 ? ` (${coverage.gaps.join("; ")})` : ""}${prior}`;
1106
+ case "context":
1107
+ return `inherited — sealed context ${coverage.items.join(", ")}${prior}`;
1108
+ case "gap":
1109
+ return `GAP — inherited context is missing: ${coverage.gaps.join("; ")}${coverage.items.length > 0 ? ` (partial: ${coverage.items.join(", ")})` : ""}${prior}`;
1110
+ }
1111
+ }
1112
+ /** Plain words for a verdict, shared by every surface (the `summary.ts`
1113
+ * pattern) so the task page, run page, board, CLI, and chat cannot say
1114
+ * three different things about the same run. */
1115
+ export function verdictWords(verdict, reasons) {
1116
+ const detail = reasons.length > 0 ? reasons.join("; ") : "";
1117
+ switch (verdict) {
1118
+ case "verified":
1119
+ return { word: "complete — verified", detail };
1120
+ case "attested":
1121
+ return { word: "complete — evidence attested", detail };
1122
+ case "short":
1123
+ return { word: "missing evidence", detail };
1124
+ case "refuted":
1125
+ return { word: "conflicting evidence", detail };
1126
+ }
1127
+ }
1128
+ /** The `data-dispatch-status` token a page's CSS and tests key off. */
1129
+ export function dispatchStatusToken(verdict) {
1130
+ switch (verdict) {
1131
+ case "verified":
1132
+ return "complete-verified";
1133
+ case "attested":
1134
+ return "complete-with-evidence";
1135
+ case "short":
1136
+ return "needs-verification";
1137
+ case "refuted":
1138
+ return "proof-refuted";
1139
+ }
1140
+ }