@iowarp/clio-coder 0.4.3 → 0.4.4

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 (340) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +1 -1
  3. package/dist/{acp-H2NGRPWO.js → acp-WNAYYF4F.js} +4 -5
  4. package/dist/{agents-TL5LLUQP.js → agents-3OKXHLOI.js} +33 -31
  5. package/dist/assets/codewiki.json +1 -1
  6. package/dist/{auth-E5SW4HMS.js → auth-VKNNMGPU.js} +10 -11
  7. package/dist/{builtins-IA7V7FUC.js → builtins-WGALA46I.js} +4 -4
  8. package/dist/{chunk-F5JHEYZM.js → chunk-23L32XTI.js} +10 -7
  9. package/dist/{chunk-DWUOQKRU.js → chunk-25QBEXRS.js} +2 -2
  10. package/dist/{chunk-LLDJM5XK.js → chunk-26QSH3EJ.js} +2 -2
  11. package/dist/{chunk-N2Z7HLVY.js → chunk-2ASED4PZ.js} +10 -10
  12. package/dist/{chunk-MCEPRMZW.js → chunk-2CU2H6KE.js} +2 -2
  13. package/dist/chunk-2DSOYNFC.js +108 -0
  14. package/dist/{chunk-LDJG7DW3.js → chunk-2ZSONWVL.js} +4 -4
  15. package/dist/{chunk-GI7YYQ3F.js → chunk-36CT5VVL.js} +324 -41
  16. package/dist/{chunk-4UVU7BJ5.js → chunk-3GY4F45V.js} +2 -2
  17. package/dist/{chunk-PUVDKJ2Y.js → chunk-3ODX73FK.js} +3 -5
  18. package/dist/{chunk-IUE3Y34X.js → chunk-3UNOLWNZ.js} +2 -2
  19. package/dist/{chunk-6PTFB5VS.js → chunk-3UUXNFEX.js} +8 -4
  20. package/dist/{chunk-2APPQIER.js → chunk-4M6Z5QVF.js} +4 -4
  21. package/dist/{chunk-K6BSR66V.js → chunk-4NSRCOYP.js} +4 -1
  22. package/dist/{chunk-JGRC33J2.js → chunk-54X7T7DK.js} +12 -3
  23. package/dist/{chunk-YQWYVTMC.js → chunk-5636DCO5.js} +4 -4
  24. package/dist/{chunk-7E7I3WLS.js → chunk-57XXR6DR.js} +23 -22
  25. package/dist/{chunk-7ZYNNDKC.js → chunk-5TUB6SLS.js} +2 -2
  26. package/dist/{chunk-VW6DOEDG.js → chunk-6OSVSQL5.js} +11 -2
  27. package/dist/{chunk-L47TF46W.js → chunk-6PAZTBPA.js} +14 -2
  28. package/dist/{chunk-XIVNBFZS.js → chunk-6Q3CYFD3.js} +34 -16
  29. package/dist/{chunk-RSJ25QSL.js → chunk-6QOTUPRG.js} +155 -36
  30. package/dist/{chunk-JKKCYP3C.js → chunk-6UINWWS6.js} +10 -4
  31. package/dist/{chunk-WBKFA554.js → chunk-72YIHOZQ.js} +2 -2
  32. package/dist/{chunk-PAJQJ7BS.js → chunk-75W7L2E2.js} +246 -697
  33. package/dist/{chunk-RRNP2ANY.js → chunk-7UGL4MB5.js} +4 -4
  34. package/dist/{chunk-EKCHAPYA.js → chunk-AUPNRN7C.js} +2 -2
  35. package/dist/{chunk-ZDN3Y73Y.js → chunk-BJVFZO5U.js} +6 -48
  36. package/dist/{chunk-IWT4SF4R.js → chunk-CUSRQKPU.js} +64 -2
  37. package/dist/{chunk-SKHCAU7K.js → chunk-DQOVN6KV.js} +3 -2
  38. package/dist/{chunk-ZWPRK62N.js → chunk-DT3LWJOB.js} +4 -4
  39. package/dist/chunk-DXKJURES.js +671 -0
  40. package/dist/{chunk-BEPZRGGU.js → chunk-EL24TAU4.js} +2 -2
  41. package/dist/{chunk-OML5D5V5.js → chunk-ELWDPP3Y.js} +5 -5
  42. package/dist/{chunk-NDINPTJ4.js → chunk-ELZVTCGV.js} +5 -4
  43. package/dist/chunk-EXLD33WO.js +381 -0
  44. package/dist/{chunk-IJNZMHLA.js → chunk-FEAXX7B6.js} +2 -2
  45. package/dist/{chunk-TM6LQDI3.js → chunk-FFUPXJC4.js} +74 -323
  46. package/dist/chunk-GX5WYQO4.js +59 -0
  47. package/dist/chunk-I2DWJ4GM.js +390 -0
  48. package/dist/{chunk-TXOTCRLG.js → chunk-I5FWO7L5.js} +5 -5
  49. package/dist/chunk-IRXAATOX.js +539 -0
  50. package/dist/chunk-IXIY2H4R.js +44 -0
  51. package/dist/{chunk-KK4JZPBQ.js → chunk-IZXGRF7P.js} +76 -10
  52. package/dist/{chunk-5TSRNF4G.js → chunk-JCI2ROMZ.js} +164 -6
  53. package/dist/{chunk-INY6HTFL.js → chunk-JEIYHLOR.js} +2 -2
  54. package/dist/{chunk-MUW2BDDH.js → chunk-JQLNNIKT.js} +2 -2
  55. package/dist/{chunk-XPWWI35G.js → chunk-JSD46VO2.js} +19 -8
  56. package/dist/{chunk-VA5FNYMT.js → chunk-JT2RFCC5.js} +39 -2
  57. package/dist/{chunk-V2ANDPVT.js → chunk-K6T2ZAMZ.js} +168 -6
  58. package/dist/{chunk-B4OAX3SI.js → chunk-KFV5L5SK.js} +13 -6
  59. package/dist/{chunk-JEQ3XTHC.js → chunk-LLXSDWXS.js} +2 -2
  60. package/dist/{chunk-E3TPLWFX.js → chunk-LTIKRKFL.js} +3 -3
  61. package/dist/{chunk-NIQJ66N4.js → chunk-N56KALIC.js} +17 -17
  62. package/dist/{chunk-JDAY6FIL.js → chunk-NAI6ZFCY.js} +13 -9
  63. package/dist/{chunk-AF4YM7Z4.js → chunk-NRO2BJRH.js} +2490 -2182
  64. package/dist/{chunk-WRBAGUNF.js → chunk-NXIMQY5W.js} +2 -2
  65. package/dist/{chunk-I7ZPNEJM.js → chunk-NXYCB2VD.js} +5 -5
  66. package/dist/chunk-ODGTEFFI.js +50 -0
  67. package/dist/{chunk-2VIKGWFZ.js → chunk-OEJSLEPW.js} +2 -2
  68. package/dist/{chunk-KKOJXO6R.js → chunk-OMQNJVKW.js} +4 -2
  69. package/dist/{chunk-54CBCGIR.js → chunk-Q4WO54TA.js} +132 -77
  70. package/dist/{chunk-B4VEBZKF.js → chunk-QUFRYSWI.js} +12 -6
  71. package/dist/{chunk-42FMPA75.js → chunk-QZWQA4DE.js} +2 -2
  72. package/dist/{chunk-HEQY7ZFI.js → chunk-RAY4OVGZ.js} +2 -2
  73. package/dist/{chunk-G76U63X4.js → chunk-RQCKCSRL.js} +7 -7
  74. package/dist/{chunk-64I3JVYM.js → chunk-RXTN6AKH.js} +2 -2
  75. package/dist/{chunk-UPZU6GE4.js → chunk-RZDWV63N.js} +3 -3
  76. package/dist/{chunk-2UH2KFUP.js → chunk-S6PYF2XF.js} +2 -2
  77. package/dist/{chunk-JSC3U7TI.js → chunk-TOIVGRUX.js} +2 -2
  78. package/dist/{chunk-7DRAWPTZ.js → chunk-TQAHXW6Y.js} +1 -1
  79. package/dist/{chunk-JIEGK6UF.js → chunk-U6TMQNSI.js} +48 -4
  80. package/dist/{chunk-AX2THNSA.js → chunk-UEPWCCTY.js} +4 -4
  81. package/dist/{chunk-QWGDJJYJ.js → chunk-USR47QNF.js} +4 -4
  82. package/dist/{chunk-Y3CBHOR6.js → chunk-V6HJFQZE.js} +2 -2
  83. package/dist/chunk-V76WTFTW.js +318 -0
  84. package/dist/{chunk-NMJXSHBJ.js → chunk-W54I7H25.js} +2 -2
  85. package/dist/{chunk-2UG5F4C5.js → chunk-XULDXHTN.js} +24 -12
  86. package/dist/chunk-XXYSBZIQ.js +283 -0
  87. package/dist/{chunk-MWUZBSAQ.js → chunk-Y55JBDO5.js} +331 -51
  88. package/dist/{chunk-W6RRQCPQ.js → chunk-YD5GIKET.js} +4 -4
  89. package/dist/{chunk-CE5AX47J.js → chunk-YECAMM3D.js} +2 -2
  90. package/dist/{chunk-WCXUNS7U.js → chunk-YNFKXPEC.js} +6 -6
  91. package/dist/cli/index.js +36 -35
  92. package/dist/{clio-CMMK4KRR.js → clio-QLICPCF5.js} +2 -2
  93. package/dist/{code-nav-MDZNQS33.js → code-nav-IJR2DBPR.js} +6 -6
  94. package/dist/{components-UCUQ4QXW.js → components-2TGAI2RC.js} +3 -4
  95. package/dist/{config-SVM5P5YI.js → config-IUA6OYNS.js} +52 -47
  96. package/dist/{configure-LE3IK2TJ.js → configure-VEPX4NMX.js} +14 -15
  97. package/dist/{context-VNCR7KAG.js → context-2DKHWH2T.js} +23 -23
  98. package/dist/{context-2OHRKS42.js → context-4MPR7WKB.js} +48 -42
  99. package/dist/{context-E3VC7RX5.js → context-BOYF5EJM.js} +11 -11
  100. package/dist/{context-clear-BW4O37TG.js → context-clear-S4ZJCQUX.js} +48 -42
  101. package/dist/{context-working-set-VDS25HXZ.js → context-working-set-3I3FYX6Y.js} +8 -8
  102. package/dist/detail-A7JAVSIG.js +98 -0
  103. package/dist/{dispatch-runner-5AHT53RF.js → dispatch-runner-RJ5I2F2O.js} +71 -58
  104. package/dist/{docs-PD3EXDKU.js → docs-SPOV3BAN.js} +3 -5
  105. package/dist/{doctor-WNNVO6FY.js → doctor-DKICC2SN.js} +54 -31
  106. package/dist/{eval-7G7SGAYO.js → eval-OQOQUDHK.js} +47 -56
  107. package/dist/{evidence-VD6736FQ.js → evidence-4DQ25GUQ.js} +53 -150
  108. package/dist/evidence-4F5USFKH.js +208 -0
  109. package/dist/{evolve-AL3NGVRL.js → evolve-GSS52E5J.js} +44 -41
  110. package/dist/{extensions-MOVJ32NM.js → extensions-G7MFLYHT.js} +4 -5
  111. package/dist/{fleet-QZHUMAGI.js → fleet-Q37YHHAQ.js} +74 -68
  112. package/dist/{fleet-commands-BAYT5FJZ.js → fleet-commands-G7E4N7SM.js} +14 -11
  113. package/dist/{fleet-decisions-IREVMRU4.js → fleet-decisions-O7M6QBA2.js} +6 -6
  114. package/dist/{fleet-graph-YCTT3HTI.js → fleet-graph-TOUBW6OW.js} +13 -13
  115. package/dist/{fleet-inspect-QVJTDAVB.js → fleet-inspect-SW33JJNI.js} +43 -39
  116. package/dist/{fleet-preflight-25QAFPK4.js → fleet-preflight-CV2655TW.js} +2 -3
  117. package/dist/{fleet-validate-5O57AAJ7.js → fleet-validate-KESZX2YH.js} +16 -16
  118. package/dist/{fleet-verify-CPH2W2T6.js → fleet-verify-UQPMTVE3.js} +42 -38
  119. package/dist/{fleet-view-SWBR3VGQ.js → fleet-view-MN2VG4MR.js} +43 -39
  120. package/dist/{init-J477LKZH.js → init-PXEXQSBF.js} +60 -54
  121. package/dist/{interop-3FCM6XLG.js → interop-ZG5T62U3.js} +6 -7
  122. package/dist/inventory-C26CFDRR.js +101 -0
  123. package/dist/{library-QUQEIUG6.js → library-B2W4N74O.js} +16 -17
  124. package/dist/{memory-SGGSEP65.js → memory-YCANYS5A.js} +45 -42
  125. package/dist/{models-HEKUAXXK.js → models-GERTU3YI.js} +20 -22
  126. package/dist/{monitor-HKU57TYQ.js → monitor-CPNIUULB.js} +50 -44
  127. package/dist/{orchestrator-VDFAEFAI.js → orchestrator-J4BSH4WQ.js} +491 -1182
  128. package/dist/{panes-DN2SSFOH.js → panes-BOHAEGYC.js} +3 -3
  129. package/dist/{panes-TALGNPZT.js → panes-NXSLDQZ2.js} +5 -6
  130. package/dist/{paths-NBMFAIEZ.js → paths-VSUWNC22.js} +3 -4
  131. package/dist/{reset-EAJFFJVB.js → reset-TNWTB5LU.js} +5 -6
  132. package/dist/{resources-OVKSEFVE.js → resources-4PXNMD5G.js} +14 -14
  133. package/dist/{run-7DP7ZF2J.js → run-D6XJ34CN.js} +79 -83
  134. package/dist/{share-WML67FT3.js → share-2NWMJJEE.js} +14 -15
  135. package/dist/{skills-SG662R2K.js → skills-KR7WON5G.js} +18 -19
  136. package/dist/{skills-eval-VVZEUU46.js → skills-eval-O2ZNOLDS.js} +49 -46
  137. package/dist/{skills-inventory-I2E23GET.js → skills-inventory-ZZOUBK7O.js} +14 -14
  138. package/dist/{slash-commands-S7MBJDQK.js → slash-commands-ZXPJD64J.js} +32 -23
  139. package/dist/{steer-2LQOMCPB.js → steer-XA25PSCS.js} +3 -3
  140. package/dist/{support-CC2UJBJ6.js → support-7EMVWYG2.js} +2 -2
  141. package/dist/{targets-4QC3HIEW.js → targets-OMH2XCSN.js} +25 -27
  142. package/dist/tasks-IPAGMEIX.js +36 -0
  143. package/dist/{terminal-lease-TUHIJ6Y2.js → terminal-lease-C2J3JYRE.js} +4 -4
  144. package/dist/{tools-TFGJICCU.js → tools-EFFEAIDP.js} +5 -6
  145. package/dist/{uninstall-5PEVOE5B.js → uninstall-HALS6BLF.js} +7 -8
  146. package/dist/{upgrade-M4WXY6KN.js → upgrade-MS72RJEP.js} +14 -11
  147. package/dist/{usage-N7ZNVLEM.js → usage-NHG6MCJM.js} +60 -51
  148. package/dist/{verifiers-DJTP4XX6.js → verifiers-7AUNVXDY.js} +150 -17
  149. package/dist/{verify-RWE4PPEK.js → verify-FWYGPKMR.js} +12 -10
  150. package/dist/{web-fetch-MPARV2K7.js → web-fetch-V4FKSDAV.js} +4 -4
  151. package/dist/{wiki-generate-C7IQOXSP.js → wiki-generate-743CIGJW.js} +63 -56
  152. package/dist/{with-panes-4GCGSL7J.js → with-panes-BDQEWBRT.js} +2 -2
  153. package/dist/worker/entry.js +36 -33
  154. package/docs/README.md +3 -2
  155. package/docs/architecture/acp.md +17 -0
  156. package/docs/architecture/artifact-versions.md +1 -1
  157. package/docs/architecture/dispatch-typed-intent.md +1 -1
  158. package/docs/architecture/evidence-and-memory.md +1 -1
  159. package/docs/architecture/observability.md +7 -1
  160. package/docs/architecture/prompt-envelope-and-tools.md +15 -5
  161. package/docs/architecture/safety-model.md +10 -17
  162. package/docs/architecture/tui-design.md +1 -1
  163. package/docs/guide/built-in-agents.md +8 -8
  164. package/docs/guide/commands-and-modes.md +17 -2
  165. package/docs/guide/configuration-and-targets.md +3 -1
  166. package/docs/guide/configuration-reference.md +10 -5
  167. package/docs/guide/environment-variables.md +2 -2
  168. package/docs/guide/tool-usage.md +78 -3
  169. package/docs/history/config-knobs-audit.md +2 -2
  170. package/docs/process/development-pipeline.md +6 -1
  171. package/docs/process/git-commit-provenance.md +15 -0
  172. package/docs/process/release-cut-checklist.md +207 -0
  173. package/docs/process/scientific-validation.md +18 -17
  174. package/evals/behavioral-machinery-support.ts +1 -0
  175. package/evals/behavioral-machinery.yaml +1 -1
  176. package/package.json +1 -1
  177. package/skills/coding/coding-standards/SKILL.md +14 -4
  178. package/skills/context/context-handoff/SKILL.md +3 -3
  179. package/skills/context/context-prime/SKILL.md +3 -3
  180. package/skills/planning/architecture/SKILL.md +5 -5
  181. package/skills/planning/backlog/SKILL.md +4 -4
  182. package/skills/planning/prd/SKILL.md +4 -3
  183. package/skills/planning/product-intent/SKILL.md +8 -8
  184. package/skills/planning/tech-spec/SKILL.md +4 -4
  185. package/skills/registry.yaml +32 -32
  186. package/skills/research/arxiv-literature/SKILL.md +3 -3
  187. package/skills/research/experiment-protocol/SKILL.md +3 -3
  188. package/skills/research/scientific-debugging/SKILL.md +3 -2
  189. package/skills/research/scientific-modernization/SKILL.md +3 -3
  190. package/skills/skill-marketplace.json +16 -16
  191. package/skills/workflow/cut-it/SKILL.md +3 -4
  192. package/skills/workflow/design-council/SKILL.md +5 -10
  193. package/skills/workflow/grill-me/SKILL.md +14 -15
  194. package/skills/workflow/workflow-distiller/SKILL.md +4 -4
  195. package/src/cli/args.ts +0 -8
  196. package/src/cli/configure.ts +2 -1
  197. package/src/cli/doctor-state-size.ts +1 -12
  198. package/src/cli/doctor-validation-contract.ts +28 -0
  199. package/src/cli/doctor.ts +5 -0
  200. package/src/cli/evidence-detail.ts +1 -75
  201. package/src/cli/evidence-inventory.ts +1 -167
  202. package/src/cli/index.ts +2 -0
  203. package/src/cli/run.ts +0 -2
  204. package/src/cli/targets.ts +1 -1
  205. package/src/cli/tasks.ts +84 -0
  206. package/src/cli/upgrade.ts +10 -5
  207. package/src/cli/usage.ts +6 -0
  208. package/src/cli/verifiers.ts +147 -1
  209. package/src/cli/wiki-generate.ts +1 -0
  210. package/src/core/commit-attribution.ts +41 -1
  211. package/src/core/git-commit-attribution.ts +46 -3
  212. package/src/core/run-overrides.ts +0 -5
  213. package/src/core/skill-activation.ts +3 -0
  214. package/src/core/tool-names.ts +5 -2
  215. package/src/domains/agents/builtins/architect.md +1 -1
  216. package/src/domains/agents/builtins/coder.md +1 -1
  217. package/src/domains/agents/builtins/documenter.md +1 -1
  218. package/src/domains/agents/builtins/git-master.md +1 -1
  219. package/src/domains/agents/builtins/provenance.md +7 -7
  220. package/src/domains/agents/builtins/tester.md +1 -1
  221. package/src/domains/agents/builtins/verifier.md +2 -2
  222. package/src/domains/agents/builtins/wiki-writer.md +4 -3
  223. package/src/domains/context/extension.ts +31 -7
  224. package/src/domains/context/refresh.ts +3 -0
  225. package/src/domains/context/wiki/frontmatter.ts +5 -2
  226. package/src/domains/context/wiki/generate.ts +6 -0
  227. package/src/domains/context/wiki/prompts.ts +43 -0
  228. package/src/domains/dispatch/active-route-planner.ts +4 -0
  229. package/src/domains/dispatch/code-step.ts +11 -4
  230. package/src/domains/dispatch/contract.ts +31 -7
  231. package/src/domains/dispatch/execution-scheduler.ts +2 -0
  232. package/src/domains/dispatch/extension.ts +50 -27
  233. package/src/domains/dispatch/fleet-commit-attribution.ts +5 -0
  234. package/src/domains/dispatch/fleet-run.ts +1 -0
  235. package/src/domains/dispatch/host-verification.ts +114 -13
  236. package/src/domains/dispatch/intent.ts +28 -18
  237. package/src/domains/dispatch/orphan-recovery.ts +2 -0
  238. package/src/domains/dispatch/receipt-integrity.ts +4 -0
  239. package/src/domains/dispatch/reservation-store.ts +5 -3
  240. package/src/domains/dispatch/state.ts +15 -2
  241. package/src/domains/dispatch/types.ts +17 -2
  242. package/src/domains/eval/runners/clio-run.ts +12 -9
  243. package/src/domains/eval/runners/context-index.ts +2 -7
  244. package/src/domains/eval/runners/context-init.ts +3 -6
  245. package/src/domains/eval/runners/external-command.ts +29 -11
  246. package/src/domains/evidence/build.ts +102 -15
  247. package/src/domains/evidence/detail.ts +69 -0
  248. package/src/domains/evidence/eval.ts +13 -1
  249. package/src/domains/evidence/finish-contract-map.ts +5 -1
  250. package/src/domains/evidence/inventory.ts +167 -0
  251. package/src/domains/evidence/store.ts +16 -0
  252. package/src/domains/evidence/types.ts +12 -0
  253. package/src/domains/extensions/resources.ts +7 -0
  254. package/src/domains/middleware/marketplace-offer.ts +20 -1
  255. package/src/domains/middleware/runtime.ts +7 -3
  256. package/src/domains/mux/detect.ts +3 -6
  257. package/src/domains/observability/accountability.ts +15 -1
  258. package/src/domains/observability/contract.ts +52 -7
  259. package/src/domains/observability/evidence-index.ts +10 -0
  260. package/src/domains/observability/extension.ts +9 -5
  261. package/src/domains/observability/projection.ts +394 -45
  262. package/src/{interactive → domains/observability}/worker-progress.ts +3 -3
  263. package/src/domains/prompts/fragments/operating/contract.md +2 -0
  264. package/src/domains/prompts/fragments/wiki/page.md +8 -0
  265. package/src/domains/providers/model-discovery.ts +1 -4
  266. package/src/domains/providers/models/local-models/clio-coder-local-coding-targets.yaml +16 -14
  267. package/src/domains/providers/runtimes/claude/claude-code.ts +9 -0
  268. package/src/domains/providers/support.ts +1 -1
  269. package/src/domains/providers/types/runtime-descriptor.ts +1 -1
  270. package/src/domains/safety/action-classifier.ts +7 -0
  271. package/src/domains/safety/default-path-policy.ts +2 -0
  272. package/src/domains/safety/finish-contract-registration.ts +29 -14
  273. package/src/domains/safety/finish-contract.ts +252 -40
  274. package/src/domains/safety/index.ts +20 -1
  275. package/src/domains/safety/policy-engine.ts +26 -6
  276. package/src/domains/safety/rigor.ts +53 -39
  277. package/src/domains/safety/validation-contract.ts +388 -0
  278. package/src/domains/session/archive-readers.ts +10 -1
  279. package/src/domains/session/decision-board.ts +101 -2
  280. package/src/domains/session/entries.ts +44 -7
  281. package/src/domains/session/extension.ts +4 -4
  282. package/src/domains/session/handoff.ts +2 -1
  283. package/src/domains/session/manager.ts +2 -3
  284. package/src/domains/session/task-board.ts +14 -1
  285. package/src/domains/session/tree/fork.ts +1 -2
  286. package/src/domains/session/tree/navigator.ts +1 -1
  287. package/src/domains/user-tasks/acceptance.ts +56 -0
  288. package/src/domains/user-tasks/active-acceptance.ts +40 -0
  289. package/src/domains/user-tasks/store.ts +34 -3
  290. package/src/engine/acp/adapter.ts +24 -6
  291. package/src/engine/acp/server.ts +21 -4
  292. package/src/engine/acp/transport.ts +53 -8
  293. package/src/engine/acp/types.ts +4 -0
  294. package/src/engine/apis/ollama-native.ts +15 -0
  295. package/src/engine/claude/subprocess-runtime.ts +107 -60
  296. package/src/engine/external-subprocess.ts +12 -4
  297. package/src/entry/orchestrator.ts +11 -2
  298. package/src/interactive/dispatch-board.ts +46 -717
  299. package/src/interactive/fleet-run-preview.ts +2 -1
  300. package/src/interactive/interactive-application.ts +3 -2
  301. package/src/interactive/interactive-presentation.ts +55 -12
  302. package/src/interactive/interactive-slash-runtime.ts +2 -1
  303. package/src/interactive/oracle.ts +5 -2
  304. package/src/interactive/overlays/fleet-run-approval.ts +3 -2
  305. package/src/interactive/overlays/message-picker.ts +2 -2
  306. package/src/interactive/overlays/settings.ts +2 -2
  307. package/src/interactive/overlays/tree-selector.ts +2 -2
  308. package/src/interactive/renderers/branch-summary.ts +1 -1
  309. package/src/interactive/slash-autocomplete.ts +4 -6
  310. package/src/interactive/slash-commands.ts +25 -39
  311. package/src/interactive/slash-spec.ts +28 -0
  312. package/src/interactive/view/artifacts.ts +2 -0
  313. package/src/interactive/worker-stream.ts +7 -3
  314. package/src/tools/bootstrap.ts +4 -0
  315. package/src/tools/builtin-tool-catalog.ts +31 -0
  316. package/src/tools/compete-worktrees.ts +7 -1
  317. package/src/tools/core-bootstrap.ts +16 -0
  318. package/src/tools/decide.ts +136 -0
  319. package/src/tools/dispatch-admission.ts +21 -0
  320. package/src/tools/dispatch-plan.ts +5 -2
  321. package/src/tools/dispatch-runner.ts +15 -1
  322. package/src/tools/dispatch-types.ts +6 -0
  323. package/src/tools/evidence.ts +96 -0
  324. package/src/tools/limitation.ts +76 -0
  325. package/src/tools/policy.ts +9 -0
  326. package/src/tools/presentation.ts +3 -0
  327. package/src/tools/registry.ts +1 -1
  328. package/src/tools/result-shaping.ts +17 -5
  329. package/src/tools/task-worktree.ts +13 -3
  330. package/src/tools/tasks.ts +10 -1
  331. package/src/tools/verify/authoring.ts +170 -83
  332. package/src/tools/verify/catalog.ts +122 -5
  333. package/src/tools/verify/index.ts +2 -1
  334. package/src/tools/verify/numeric.ts +298 -0
  335. package/src/tools/verify/perf.ts +143 -0
  336. package/src/tools/verify/scripts.ts +229 -2
  337. package/dist/chunk-7DICMOS6.js +0 -313
  338. package/dist/chunk-RVG5JXAL.js +0 -41
  339. package/dist/chunk-T56WDKA5.js +0 -183
  340. package/dist/chunk-VPKWYKEY.js +0 -169
@@ -5,6 +5,7 @@ import { dirname, join, resolve } from "node:path";
5
5
  import { PATH_BOUNDARY_MAX_ENTRIES, pathBoundaryCovers, resolvePathBoundary } from "../../core/path-boundary.js";
6
6
  import { withStateFileLockSync } from "../../core/state-file-lock.js";
7
7
  import { clioStateDir } from "../../core/xdg.js";
8
+ import { judgeNumericTexts, judgePerfTexts } from "../../tools/verify/scripts.js";
8
9
  import { FLEET_COMMAND_BASE_ENV, type FleetCommand } from "../agents/fleet-commands.js";
9
10
  import { runCodeStep } from "./code-step.js";
10
11
  import type { DispatchRequest } from "./contract.js";
@@ -12,7 +13,7 @@ import type { RunHostVerification, RunHostVerificationAttribution, RunHostVerifi
12
13
  import { captureWorkspaceSnapshot } from "./write-boundary.js";
13
14
 
14
15
  const OUTPUT_TAIL_BYTES = 2_048;
15
- const MEMO_VERSION = 1;
16
+ const MEMO_VERSION = 2;
16
17
 
17
18
  type ResolvedCheck = NonNullable<DispatchRequest["resolvedVerification"]>[number];
18
19
 
@@ -37,7 +38,7 @@ interface MemoEntry {
37
38
  }
38
39
 
39
40
  interface MemoFile {
40
- version: 1;
41
+ version: typeof MEMO_VERSION;
41
42
  entries: MemoEntry[];
42
43
  }
43
44
 
@@ -108,10 +109,10 @@ function storeMemoEntry(stateDir: string, entry: MemoEntry): void {
108
109
  });
109
110
  }
110
111
 
111
- function memoKey(command: FleetCommand, fingerprint: string, env: NodeJS.ProcessEnv): string {
112
+ function memoKey(command: FleetCommand, judgment: string, fingerprint: string, env: NodeJS.ProcessEnv): string {
112
113
  const names = [...FLEET_COMMAND_BASE_ENV, ...command.env];
113
114
  const values = Object.fromEntries(names.map((name) => [name, env[name] ?? null]));
114
- return sha256(JSON.stringify({ fingerprint, argv: command.argv, cwd: command.cwd, env: values }));
115
+ return sha256(JSON.stringify({ fingerprint, judgment, env: values }));
115
116
  }
116
117
 
117
118
  export function hostVerificationRejection(
@@ -124,10 +125,63 @@ export function hostVerificationRejection(
124
125
  detail:
125
126
  failedCheck === undefined
126
127
  ? "host verification rejected"
127
- : `host verification check '${failedCheck.check}' rejected with exit code ${failedCheck.exitCode}`,
128
+ : failedCheck.report !== undefined
129
+ ? `host verification check '${failedCheck.check}' rejected: ${failedCheck.report.summary}`
130
+ : `host verification check '${failedCheck.check}' rejected with exit code ${failedCheck.exitCode}`,
128
131
  };
129
132
  }
130
133
 
134
+ function readSealedFile(absolutePath: string): string | undefined {
135
+ try {
136
+ return readFileSync(absolutePath, "utf8");
137
+ } catch {
138
+ return undefined;
139
+ }
140
+ }
141
+
142
+ /**
143
+ * The kind-specific verdict for a resolved check that already ran. A command
144
+ * check keeps its exit code. A numeric-compare check judges captured stdout
145
+ * against the sealed reference; a perf-budget check judges the measured
146
+ * duration against the sealed budget or baseline. Either judgement turns a
147
+ * clean exit into exit 1 when it fails, and a judgement that cannot be made
148
+ * (missing reference, unparseable output) fails with the reason in the tail.
149
+ */
150
+ function judgeResolvedCheck(
151
+ resolvedCheck: ResolvedCheck,
152
+ exitCode: number,
153
+ durationMs: number,
154
+ stdout: string,
155
+ ): { exitCode: number; report?: RunHostVerificationCheck["report"]; outputTail?: string } {
156
+ if (resolvedCheck.kind === undefined || resolvedCheck.kind === "command" || exitCode !== 0) return { exitCode };
157
+ if (resolvedCheck.kind === "numeric-compare" && resolvedCheck.numeric !== undefined) {
158
+ const referenceText = readSealedFile(resolvedCheck.numeric.reference);
159
+ if (referenceText === undefined) {
160
+ return { exitCode: 1, outputTail: `numeric-compare: reference '${resolvedCheck.numeric.reference}' cannot be read` };
161
+ }
162
+ const report = judgeNumericTexts(
163
+ stdout,
164
+ referenceText,
165
+ resolvedCheck.numeric.tolerance,
166
+ `reference '${resolvedCheck.numeric.reference}'`,
167
+ );
168
+ if (report instanceof Error) return { exitCode: 1, outputTail: `numeric-compare: ${report.message}` };
169
+ return { exitCode: report.passed ? 0 : 1, report, outputTail: outputTail(report.summary) };
170
+ }
171
+ if (resolvedCheck.kind === "perf-budget" && resolvedCheck.perf !== undefined) {
172
+ const baselinePath = resolvedCheck.perf.baseline;
173
+ const report = judgePerfTexts(
174
+ durationMs,
175
+ resolvedCheck.perf,
176
+ baselinePath === undefined ? undefined : readSealedFile(baselinePath),
177
+ `baseline '${baselinePath ?? ""}'`,
178
+ );
179
+ if (report instanceof Error) return { exitCode: 1, outputTail: `perf-budget: ${report.message}` };
180
+ return { exitCode: report.passed ? 0 : 1, report, outputTail: outputTail(report.summary) };
181
+ }
182
+ return { exitCode: 1, outputTail: `${resolvedCheck.kind}: sealed check carries no parameters` };
183
+ }
184
+
131
185
  async function runResolvedCheck(input: {
132
186
  runId: string;
133
187
  index: number;
@@ -145,9 +199,9 @@ async function runResolvedCheck(input: {
145
199
  env: [],
146
200
  description: `Host verification check ${resolvedCheck.check}.`,
147
201
  };
202
+ const judgmentKey = checkDedupeKey(resolvedCheck);
148
203
  const fingerprint = workspaceFingerprint(resolvedCheck.cwd);
149
- const key =
150
- fingerprint === null ? null : memoKey({ ...command, cwd: resolve(resolvedCheck.cwd) }, fingerprint, input.env);
204
+ const key = fingerprint === null ? null : memoKey(command, judgmentKey, fingerprint, input.env);
151
205
  const hit =
152
206
  key === null
153
207
  ? undefined
@@ -168,17 +222,24 @@ async function runResolvedCheck(input: {
168
222
  env: input.env,
169
223
  });
170
224
  const artifactPath = outcome.record.artifactPaths[0];
225
+ const judgement = judgeResolvedCheck(
226
+ resolvedCheck,
227
+ outcome.record.exitCode,
228
+ outcome.record.durationMs,
229
+ outcome.stdout,
230
+ );
171
231
  const check: RunHostVerificationCheck = {
172
232
  check: resolvedCheck.check,
173
233
  argv: [...outcome.record.argv],
174
234
  cwd: outcome.record.cwd,
175
- exitCode: outcome.record.exitCode,
235
+ exitCode: judgement.exitCode,
176
236
  durationMs: outcome.record.durationMs,
177
237
  memo: false,
178
- outputTail: outputTail(outcome.report.outputExcerpt),
238
+ outputTail: judgement.outputTail ?? outputTail(outcome.report.outputExcerpt),
179
239
  ...(artifactPath !== undefined ? { artifactPath } : {}),
240
+ ...(judgement.report !== undefined ? { report: judgement.report } : {}),
180
241
  };
181
- if (check.exitCode === 0 && key !== null) {
242
+ if (check.exitCode === 0 && key !== null && checkDedupeKey(resolvedCheck) === judgmentKey) {
182
243
  try {
183
244
  storeMemoEntry(input.stateDir, { key, runId: input.runId, check });
184
245
  } catch (error) {
@@ -229,14 +290,45 @@ function implicatedPaths(outputExcerpt: string, checkCwd: string): string[] {
229
290
  return [...paths].sort();
230
291
  }
231
292
 
232
- /** Identity of a resolved check across batch members: same command, same cwd, same bound. */
293
+ /** Identity shared by memo reuse and batch deduplication, including the declared judgment and its input files. */
233
294
  function checkDedupeKey(resolvedCheck: ResolvedCheck): string {
295
+ const numeric = resolvedCheck.numeric;
296
+ const perf = resolvedCheck.perf;
234
297
  return sha256(
235
298
  JSON.stringify({
236
299
  check: resolvedCheck.check,
237
300
  argv: [...resolvedCheck.argv],
238
301
  cwd: resolve(resolvedCheck.cwd),
239
302
  timeoutMs: resolvedCheck.timeoutMs,
303
+ kind: resolvedCheck.kind ?? "command",
304
+ numeric:
305
+ numeric === undefined
306
+ ? undefined
307
+ : {
308
+ reference: numeric.reference,
309
+ referenceDigest: sha256(JSON.stringify(readSealedFile(numeric.reference) ?? null)),
310
+ tolerance: {
311
+ absolute: numeric.tolerance.absolute,
312
+ relative: numeric.tolerance.relative,
313
+ ulp: numeric.tolerance.ulp,
314
+ },
315
+ },
316
+ perf:
317
+ perf === undefined
318
+ ? undefined
319
+ : {
320
+ budget:
321
+ perf.budget === undefined
322
+ ? undefined
323
+ : {
324
+ wallTimeMs: perf.budget.wallTimeMs,
325
+ tolerance: perf.budget.tolerance,
326
+ },
327
+ baseline: perf.baseline,
328
+ baselineDigest:
329
+ perf.baseline === undefined ? undefined : sha256(JSON.stringify(readSealedFile(perf.baseline) ?? null)),
330
+ tolerance: perf.tolerance,
331
+ },
240
332
  }),
241
333
  );
242
334
  }
@@ -304,6 +396,12 @@ function memberBoundary(participant: BatchVerificationParticipant): string[] {
304
396
  * the tree. `basis` is the weakest evidence behind the charge set, never the
305
397
  * strongest, so `write_roots` on a receipt means every charged run has a named
306
398
  * path inside its own declared boundary.
399
+ *
400
+ * A `worktree: true` member's write roots resolve against its own checkout
401
+ * (`request.cwd`) while the shared check ran in the parent frame, so none of
402
+ * its roots can cover a path the check named: attribution cannot exculpate
403
+ * anyone on its account and every declarer is charged, the conservative
404
+ * fallback those receipts sealed before batch settlement existed.
307
405
  */
308
406
  function attributeFailure(input: {
309
407
  implicated: ReadonlyArray<string>;
@@ -421,9 +519,11 @@ export function createBatchVerificationGate(
421
519
  string,
422
520
  { resolvedCheck: ResolvedCheck; owner: BatchVerificationParticipant; index: number }
423
521
  >();
522
+ const checkKeys = new Map<ResolvedCheck, string>();
424
523
  for (const member of contributors) {
425
524
  for (const resolvedCheck of member.request.resolvedVerification ?? []) {
426
525
  const key = checkDedupeKey(resolvedCheck);
526
+ checkKeys.set(resolvedCheck, key);
427
527
  if (!distinct.has(key)) distinct.set(key, { resolvedCheck, owner: member, index: distinct.size });
428
528
  }
429
529
  }
@@ -448,7 +548,7 @@ export function createBatchVerificationGate(
448
548
  for (const [key, entry] of ran) {
449
549
  if (entry.check.exitCode === 0) continue;
450
550
  const declarers = contributors.filter((member) =>
451
- (member.request.resolvedVerification ?? []).some((candidate) => checkDedupeKey(candidate) === key),
551
+ (member.request.resolvedVerification ?? []).some((candidate) => checkKeys.get(candidate) === key),
452
552
  );
453
553
  const implicated = implicatedPaths(entry.outputExcerpt, entry.check.cwd);
454
554
  const verdict = attributeFailure({ implicated, declarers, wave: members });
@@ -478,7 +578,8 @@ export function createBatchVerificationGate(
478
578
  let rejected = false;
479
579
  let unimplicated = false;
480
580
  for (const resolvedCheck of member.request.resolvedVerification ?? []) {
481
- const key = checkDedupeKey(resolvedCheck);
581
+ const key = checkKeys.get(resolvedCheck);
582
+ if (key === undefined) throw new Error("host verification: declared check has no batch identity");
482
583
  const shared = ran.get(key);
483
584
  if (shared === undefined) continue;
484
585
  own.push(
@@ -271,13 +271,34 @@ export function normalizeDispatchIntent(
271
271
  if (!Array.isArray(writeRoots)) return writeRoots;
272
272
  const relevantPaths = normalizePathList(raw.relevant_paths, "intent.relevant_paths");
273
273
  if (!Array.isArray(relevantPaths)) return relevantPaths;
274
- const expectedOutputs = normalizePathList(
275
- raw.expected_outputs,
276
- "intent.expected_outputs",
277
- normalizeExpectedOutputEntry,
278
- );
274
+ const expectedOutputs = normalizeIntentExpectedOutputs(raw.expected_outputs);
279
275
  if (!Array.isArray(expectedOutputs)) return expectedOutputs;
280
- const rawVerification = raw.verification ?? [];
276
+ const verification = normalizeIntentVerification(raw.verification, checks);
277
+ if (!Array.isArray(verification)) return verification;
278
+
279
+ return {
280
+ ok: true,
281
+ intent: {
282
+ version: 2,
283
+ readRoots,
284
+ writeRoots,
285
+ relevantPaths,
286
+ pathProvenance: declaredIntentPathProvenance({ readRoots, writeRoots, relevantPaths }),
287
+ expectedOutputs,
288
+ verification,
289
+ },
290
+ };
291
+ }
292
+
293
+ export function normalizeIntentExpectedOutputs(raw: unknown): string[] | DispatchIntentNormalizationResult {
294
+ return normalizePathList(raw, "intent.expected_outputs", normalizeExpectedOutputEntry);
295
+ }
296
+
297
+ export function normalizeIntentVerification(
298
+ raw: unknown,
299
+ checks: ReadonlyMap<string, DispatchIntentCheckBound>,
300
+ ): DispatchIntentVerification[] | DispatchIntentNormalizationResult {
301
+ const rawVerification = raw ?? [];
281
302
  if (!Array.isArray(rawVerification)) {
282
303
  return fail("verification_malformed", "intent.verification must be an array");
283
304
  }
@@ -321,16 +342,5 @@ export function normalizeDispatchIntent(
321
342
  timeoutMs: Math.min(declared.timeoutMs, Math.max(DISPATCH_INTENT_TIMEOUT_MIN_MS, requested)),
322
343
  });
323
344
  }
324
- return {
325
- ok: true,
326
- intent: {
327
- version: 2,
328
- readRoots,
329
- writeRoots,
330
- relevantPaths,
331
- pathProvenance: declaredIntentPathProvenance({ readRoots, writeRoots, relevantPaths }),
332
- expectedOutputs,
333
- verification,
334
- },
335
- };
345
+ return verification;
336
346
  }
@@ -49,6 +49,7 @@ function receiptsDir(): string {
49
49
  */
50
50
  function envelopeFromReceipt(receipt: RunReceipt, status: RunStatus, receiptPath: string): RunEnvelope {
51
51
  const envelope: RunEnvelope = {
52
+ version: 1,
52
53
  id: receipt.runId,
53
54
  agentId: receipt.agentId,
54
55
  executionRole: receipt.executionRole,
@@ -97,6 +98,7 @@ function envelopeFromReceipt(receipt: RunReceipt, status: RunStatus, receiptPath
97
98
  if (receipt.gate !== undefined) envelope.gate = receipt.gate;
98
99
  if (receipt.plan !== undefined) envelope.plan = receipt.plan;
99
100
  if (receipt.personaOverride !== undefined) envelope.personaOverride = receipt.personaOverride;
101
+ if (receipt.decisionRefs !== undefined) envelope.decisionRefs = receipt.decisionRefs;
100
102
  return envelope;
101
103
  }
102
104
 
@@ -99,6 +99,9 @@ export const RECEIPT_INTEGRITY_FIELD_COVERAGE = {
99
99
  plan: true,
100
100
  fleetGate: true,
101
101
  personaOverride: true,
102
+ // Absent unless the parent board held decisions, so older receipts and
103
+ // decision-free runs digest exactly as before.
104
+ decisionRefs: true,
102
105
  projectContext: true,
103
106
  // Always set on receipts written after #104 landed; absent only on older
104
107
  // receipts, which omit these fields from canonical serialization.
@@ -193,6 +196,7 @@ function ledgerDigestFields(envelope: RunEnvelope): Record<string, unknown> {
193
196
  executionRole: envelope.executionRole,
194
197
  task: envelope.task,
195
198
  ...(envelope.budget !== undefined ? { budget: envelope.budget } : {}),
199
+ ...(envelope.decisionRefs !== undefined ? { decisionRefs: envelope.decisionRefs } : {}),
196
200
  targetId: envelope.targetId,
197
201
  wireModelId: envelope.wireModelId,
198
202
  runtimeId: envelope.runtimeId,
@@ -1,5 +1,5 @@
1
1
  import { randomBytes } from "node:crypto";
2
- import { withStateFileLockSync } from "../../core/state-file-lock.js";
2
+ import { withStateFileLock, withStateFileLockSync } from "../../core/state-file-lock.js";
3
3
  import { endpointLabel } from "../providers/endpoint-capacity.js";
4
4
  import {
5
5
  acquireCapacityLease,
@@ -361,8 +361,10 @@ export function rebindDispatchReservationMember(input: {
361
361
  costUpperBoundUsd: number;
362
362
  capacity: ReservationCapacitySnapshot;
363
363
  nowMs?: number;
364
- }): DispatchReservationRecord {
365
- return withStateFileLockSync(capacityStateLockPath(), () => {
364
+ }): Promise<DispatchReservationRecord> {
365
+ // Retry admission runs inside the async dispatch attempt, so it can yield
366
+ // while waiting for the cross-process lock instead of blocking the event loop.
367
+ return withStateFileLock(capacityStateLockPath(), () => {
366
368
  const nowMs = input.nowMs ?? Date.now();
367
369
  const records = expireRecords(readStore(), nowMs, false);
368
370
  const record = records.find((entry) => entry.ownerId === input.ownerId);
@@ -52,6 +52,7 @@ export interface LedgerOptions {
52
52
 
53
53
  export type CreateRunInput = Omit<
54
54
  RunEnvelope,
55
+ | "version"
55
56
  | "id"
56
57
  | "startedAt"
57
58
  | "endedAt"
@@ -143,6 +144,10 @@ export function listFleetRuns(limit = MAX_FLEET_RUN_SCAN): FleetRunRecord[] {
143
144
  return records.slice(0, Math.max(0, limit));
144
145
  }
145
146
 
147
+ function isRecord(value: unknown): value is Record<string, unknown> {
148
+ return typeof value === "object" && value !== null && !Array.isArray(value);
149
+ }
150
+
146
151
  // `RunEnvelope.heartbeatAt` deliberately persists only the absolute wall-clock
147
152
  // anchor used for operator display and crash recovery. The monotonic heartbeat
148
153
  // stamp belongs to the live transport in this process; serializing it would
@@ -152,9 +157,16 @@ function readRuns(): RunEnvelope[] {
152
157
  if (!existsSync(path)) return [];
153
158
  const raw = readFileSync(path, "utf8").trim();
154
159
  if (raw.length === 0) return [];
155
- const parsed = JSON.parse(raw) as RunEnvelope[];
160
+ const parsed = JSON.parse(raw) as unknown;
156
161
  if (!Array.isArray(parsed)) return [];
157
- return parsed;
162
+ const envelopes: RunEnvelope[] = [];
163
+ for (const entry of parsed) {
164
+ if (!isRecord(entry)) continue;
165
+ const version = entry.version === undefined ? 1 : entry.version;
166
+ if (version !== 1) continue;
167
+ envelopes.push({ ...(entry as unknown as RunEnvelope), version: 1 });
168
+ }
169
+ return envelopes;
158
170
  }
159
171
 
160
172
  function resolveMaxRuns(opt?: number | undefined): number {
@@ -241,6 +253,7 @@ export function openLedger(opts?: LedgerOptions): Ledger {
241
253
  return {
242
254
  create(input: CreateRunInput): RunEnvelope {
243
255
  const envelope: RunEnvelope = {
256
+ version: 1,
244
257
  id: input.id ?? newRunId(),
245
258
  agentId: input.agentId,
246
259
  executionRole: input.executionRole,
@@ -10,6 +10,7 @@ import type { GatewayRoutingObservation } from "../../core/gateway-routing.js";
10
10
  import type { ResponseModelIdObservation } from "../../core/response-model-id.js";
11
11
  import type { SkillActivation } from "../../core/skill-activation.js";
12
12
  import type { ToolProfileName } from "../../tools/profiles.js";
13
+ import type { DeclaredCheckReport } from "../../tools/verify/scripts.js";
13
14
  import type { AgentAudience } from "../agents/spec.js";
14
15
  import type { EvidenceTag } from "../evidence/index.js";
15
16
  import type { CostProvenance, RuntimeTargetSnapshot } from "../providers/index.js";
@@ -442,6 +443,7 @@ export interface RunReceiptFindingsSummary {
442
443
  }
443
444
 
444
445
  export interface RunEnvelope {
446
+ version: 1;
445
447
  id: string;
446
448
  agentId: string;
447
449
  /** Semantic role this attempt's route statistics belong to. */
@@ -483,6 +485,8 @@ export interface RunEnvelope {
483
485
  plan?: RunPlanProvenance;
484
486
  /** Ad-hoc specialist provenance; present only when a persona override composed the stable prompt. */
485
487
  personaOverride?: RunPersonaOverride;
488
+ /** Decision refs active on the parent decision board at dispatch; absent when the board was empty. */
489
+ decisionRefs?: ReadonlyArray<string>;
486
490
  exitCode: number | null;
487
491
  pid: number | null;
488
492
  heartbeatAt: string | null;
@@ -710,11 +714,14 @@ export interface RunHostVerificationCheck {
710
714
  check: string;
711
715
  argv: string[];
712
716
  cwd: string;
717
+ /** For a numeric-compare or perf-budget check, 1 when the judgement failed even though the command exited 0. */
713
718
  exitCode: number;
714
719
  durationMs: number;
715
720
  memo: boolean;
716
721
  outputTail: string;
717
722
  artifactPath?: string;
723
+ /** Structured verdict of a numeric-compare or perf-budget check; absent for a command check. Covered by the receipt digest through `hostVerification`. */
724
+ report?: DeclaredCheckReport;
718
725
  /** Run that owns the evidence when it did not come from this run: a memo hit, or the batch member the shared check ran under. */
719
726
  evidenceRunId?: string;
720
727
  }
@@ -811,8 +818,14 @@ export interface RunReceipt {
811
818
  node?: RunNodeIdentity;
812
819
  /**
813
820
  * Route and node identity attested by the worker process that executed the
814
- * run. Absent only when no worker announced, which is the stubbed-spawn path
815
- * contract tests use; every native and remote transport attests.
821
+ * run. Every native and remote worker transport attests, so a receipt from
822
+ * the worker path carries this field. It is legitimately absent on receipts
823
+ * that no worker process produced: the print-mode main-agent receipt
824
+ * (src/cli/modes/print.ts), which runs in the orchestrator process, and ACP
825
+ * delegation receipts, whose external agent never sends an announce frame.
826
+ * The eval process invariants (src/domains/eval/metrics/invariants.ts) rely
827
+ * on that absence to skip the main-agent path when counting orphaned
828
+ * workers. Contract tests that stub the spawn also leave it absent.
816
829
  */
817
830
  attestation?: RunReceiptAttestation;
818
831
  /** Dead-node failover hops, oldest first; absent when the run was never rerouted. */
@@ -828,6 +841,8 @@ export interface RunReceipt {
828
841
  fleetGate?: { path: string; pathHash: string };
829
842
  /** Ad-hoc specialist provenance; present only when a persona override composed the stable prompt. */
830
843
  personaOverride?: RunPersonaOverride;
844
+ /** Decision refs active on the parent decision board at dispatch; absent when the board was empty. */
845
+ decisionRefs?: ReadonlyArray<string>;
831
846
  /** Effective project-context tier for this run; absent on receipts written before this field landed. */
832
847
  projectContext?: RunProjectContextProvenance;
833
848
  /**
@@ -1,6 +1,5 @@
1
1
  import { isAbsolute, relative, resolve, sep } from "node:path";
2
2
  import { normalizeClioCoderEventRecord } from "../../../core/naming-events.js";
3
- import { shellQuote } from "../../../core/shell-quote.js";
4
3
  import { clioStateDir } from "../../../core/xdg.js";
5
4
  import {
6
5
  evidenceMetricsFromReceipt,
@@ -22,19 +21,23 @@ export async function runClioRunRunner(
22
21
  readObservation?: { allowedPaths: string[]; decoyPaths: string[] },
23
22
  ): Promise<EvalRunnerOutput> {
24
23
  const prompt = runner.prompt ?? "";
25
- const args = [
26
- shellQuote(clioEntry),
24
+ // Spawned as an argv, not a shell line: the task timeout has to reach the
25
+ // Clio process itself so the receipt it seals and the exit code this
26
+ // runner reports describe the same ending. See runShellCommand.
27
+ const args: [string, ...string[]] = [
28
+ process.execPath,
29
+ clioEntry,
27
30
  "run",
28
31
  "--json",
29
- ...(runner.agent === undefined ? [] : ["--agent", shellQuote(runner.agent)]),
32
+ ...(runner.agent === undefined ? [] : ["--agent", runner.agent]),
30
33
  "--target",
31
- shellQuote(target.id),
32
- ...(target.model === undefined ? [] : ["--model", shellQuote(target.model)]),
33
- ...(target.thinking === undefined ? [] : ["--thinking", shellQuote(target.thinking)]),
34
+ target.id,
35
+ ...(target.model === undefined ? [] : ["--model", target.model]),
36
+ ...(target.thinking === undefined ? [] : ["--thinking", target.thinking]),
34
37
  ...(runner.autonomy === undefined ? [] : ["--autonomy", runner.autonomy]),
35
- shellQuote(prompt),
38
+ prompt,
36
39
  ];
37
- const result = await runShellCommand(`${process.execPath} ${args.join(" ")}`, cwd, runner.timeoutMs ?? timeoutMs, env);
40
+ const result = await runShellCommand(args, cwd, runner.timeoutMs ?? timeoutMs, env);
38
41
  // Usage is folded from the live stream, not from the bounded stdout
39
42
  // artifact: a verbose run's `message_end` events do not survive truncation.
40
43
  const tokens = result.usage;
@@ -1,4 +1,3 @@
1
- import { shellQuote } from "../../../core/shell-quote.js";
2
1
  import type { EvalSuiteTargetV2 } from "../schema/suite.js";
3
2
  import { type EvalRunnerOutput, runShellCommand } from "./external-command.js";
4
3
 
@@ -9,12 +8,8 @@ export async function runContextIndexRunner(
9
8
  target: EvalSuiteTargetV2,
10
9
  env?: NodeJS.ProcessEnv,
11
10
  ): Promise<EvalRunnerOutput> {
12
- const result = await runShellCommand(
13
- `${shellQuote(process.execPath)} ${shellQuote(clioEntry)} context index --json`,
14
- cwd,
15
- timeoutMs,
16
- env,
17
- );
11
+ const args: [string, ...string[]] = [process.execPath, clioEntry, "context", "index", "--json"];
12
+ const result = await runShellCommand(args, cwd, timeoutMs, env);
18
13
  const parsed = parseContextIndexOutput(result.stdout);
19
14
  return {
20
15
  assignmentId: null,
@@ -1,6 +1,5 @@
1
1
  import { existsSync, statSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { shellQuote } from "../../../core/shell-quote.js";
4
3
  import type { EvalRunnerV2, EvalSuiteTargetV2 } from "../schema/suite.js";
5
4
  import { type EvalRunnerOutput, runShellCommand } from "./external-command.js";
6
5
 
@@ -13,7 +12,7 @@ export async function runContextInitRunner(
13
12
  env?: NodeJS.ProcessEnv,
14
13
  ): Promise<EvalRunnerOutput> {
15
14
  const extraArgs = runner.args ?? [];
16
- const command = [
15
+ const args: [string, ...string[]] = [
17
16
  process.execPath,
18
17
  clioEntry,
19
18
  "context",
@@ -25,10 +24,8 @@ export async function runContextInitRunner(
25
24
  ...(target.model === undefined ? [] : ["--model", target.model]),
26
25
  ...(target.thinking === undefined ? [] : ["--thinking", target.thinking]),
27
26
  ...extraArgs,
28
- ]
29
- .map(shellQuote)
30
- .join(" ");
31
- const result = await runShellCommand(command, cwd, runner.timeoutMs ?? timeoutMs, env);
27
+ ];
28
+ const result = await runShellCommand(args, cwd, runner.timeoutMs ?? timeoutMs, env);
32
29
  const payload = parseInitPayload(result.stdout);
33
30
  const candidateGeneration = recordField(payload, "generation");
34
31
  const generation = isValidGenerationPayload(payload, candidateGeneration) ? candidateGeneration : null;
@@ -65,6 +65,11 @@ const OUTPUT_HEAD_LIMIT = 20_000;
65
65
  const OUTPUT_TRUNCATION_MARKER = "\n[output middle truncated; tail preserved]\n";
66
66
  const METRIC_JSONL_LIMIT = 256_000;
67
67
  const METRIC_JSONL_LINE_LIMIT = 64_000;
68
+ // After the deadline's SIGTERM, how long a run gets to end on its own terms
69
+ // before SIGKILL. Clio seals the interrupted run's receipt inside its
70
+ // coordinated shutdown, which budgets 500ms per hook across several hooks; a
71
+ // one-second grace killed it mid-seal and the timed-out run left no receipt.
72
+ const TIMEOUT_KILL_GRACE_MS = 5_000;
68
73
 
69
74
  export async function runExternalCommandRunner(
70
75
  runner: EvalRunnerV2,
@@ -133,8 +138,23 @@ export async function runExternalCommandRunner(
133
138
  };
134
139
  }
135
140
 
141
+ /**
142
+ * Run one eval subprocess to completion or to the timeout.
143
+ *
144
+ * A string runs through the shell, which is what an operator's
145
+ * `external-command` line and the verifier commands are. An argv array spawns
146
+ * the program directly, with no shell between the runner and the process
147
+ * whose exit code it reports. That distinction matters on the timeout path:
148
+ * `/bin/sh -c` on dash keeps the shell as the parent, so SIGTERM to the shell
149
+ * stops the shell alone while the program runs on to completion. A
150
+ * `clio-coder run` spawned that way finished its turn after the deadline,
151
+ * sealed a succeeded receipt with exit 0, and exited 0, and this runner
152
+ * reported the shell's signal death as exit 124 (issue #275). With an argv the
153
+ * signal reaches Clio itself, which seals the run as canceled with the exit
154
+ * status it then reports, so the receipt and the exit code agree either way.
155
+ */
136
156
  export function runShellCommand(
137
- command: string,
157
+ command: string | readonly [string, ...string[]],
138
158
  cwd: string,
139
159
  timeoutMs: number,
140
160
  env?: NodeJS.ProcessEnv,
@@ -153,14 +173,12 @@ export function runShellCommand(
153
173
  const callLedgerFold = createEvalCallLedgerFold();
154
174
  let timedOut = false;
155
175
  let settled = false;
156
- const child = spawn(command, {
157
- cwd,
158
- shell: true,
159
- stdio: ["ignore", "pipe", "pipe"],
160
- // The overlay is additive: an eval item pins where Clio writes its
161
- // journal, and inherits everything else the operator's shell provides.
162
- env: env === undefined ? process.env : { ...process.env, ...env },
163
- });
176
+ // The overlay is additive: an eval item pins where Clio writes its
177
+ // journal, and inherits everything else the operator's shell provides.
178
+ const spawnEnv = env === undefined ? process.env : { ...process.env, ...env };
179
+ const [file, args, shell] = typeof command === "string" ? [command, [], true] : [command[0], command.slice(1), false];
180
+ const child = spawn(file, args, { cwd, shell, stdio: ["ignore", "pipe", "pipe"], env: spawnEnv });
181
+ const commandLine = typeof command === "string" ? command : command.join(" ");
164
182
  child.stdout.setEncoding("utf8");
165
183
  child.stderr.setEncoding("utf8");
166
184
  child.stdout.on("data", (chunk: string) => {
@@ -177,14 +195,14 @@ export function runShellCommand(
177
195
  const timer = setTimeout(() => {
178
196
  timedOut = true;
179
197
  child.kill("SIGTERM");
180
- setTimeout(() => child.kill("SIGKILL"), 1000);
198
+ setTimeout(() => child.kill("SIGKILL"), TIMEOUT_KILL_GRACE_MS);
181
199
  }, timeoutMs);
182
200
  const finish = (exitCode: number): void => {
183
201
  if (settled) return;
184
202
  settled = true;
185
203
  clearTimeout(timer);
186
204
  resolve({
187
- command,
205
+ command: commandLine,
188
206
  exitCode,
189
207
  stdout,
190
208
  metricJsonl: metricCapture.finish(),