@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
@@ -1,14 +1,18 @@
1
+ import { resolve } from "node:path";
1
2
  import { ToolNames } from "../../core/tool-names.js";
2
3
  import { isProjectVerifierCheckId, isVerificationScriptName } from "../../core/verification-scripts.js";
4
+ import { type DeclaredCheckSourceRef, PROJECT_VERIFIER_CATALOG_RELATIVE_PATH } from "../../tools/verify/catalog.js";
5
+ import type { UserTaskAcceptance } from "../user-tasks/acceptance.js";
3
6
  import {
4
7
  detectValidationCommand,
5
8
  extractCommandDeleteTargets,
6
9
  extractCommandWriteTargets,
7
10
  toolMutationPaths,
8
11
  } from "./protected-artifacts.js";
12
+ import type { Rigor } from "./rigor.js";
9
13
 
10
14
  export const FINISH_CONTRACT_ADVISORY_MESSAGE =
11
- "[Clio Coder] finish-contract advisory: you changed files this turn without recording validation evidence or an explicit limitation. Run a verification command or state what could not be verified.";
15
+ "[Clio Coder] finish-contract advisory: you changed files this turn without recording validation evidence or a limitation receipt. Run a verification command or call limitation with the scope and reason.";
12
16
 
13
17
  /**
14
18
  * The recent-window cap (entries since the last user message). Exported so a
@@ -17,11 +21,24 @@ export const FINISH_CONTRACT_ADVISORY_MESSAGE =
17
21
  */
18
22
  export const DEFAULT_RECENT_ENTRY_LIMIT = 80;
19
23
 
20
- export type FinishContractEvidenceKind = "validation_command" | "protected_artifact" | "dispatch_receipt";
24
+ export type FinishContractEvidenceKind =
25
+ | "validation_command"
26
+ | "protected_artifact"
27
+ | "dispatch_receipt"
28
+ | "limitation";
29
+
30
+ interface ValidationExecutionEvidence {
31
+ source?: DeclaredCheckSourceRef;
32
+ cwd?: string;
33
+ argv?: ReadonlyArray<string>;
34
+ durationMs?: number;
35
+ }
21
36
 
22
- export interface FinishContractEvidence {
37
+ export interface FinishContractEvidence extends ValidationExecutionEvidence {
23
38
  kind: FinishContractEvidenceKind;
24
39
  summary: string;
40
+ check?: string;
41
+ paths?: ReadonlyArray<string>;
25
42
  turnId?: string;
26
43
  }
27
44
 
@@ -48,16 +65,22 @@ export type FinishContractAssessment =
48
65
  };
49
66
 
50
67
  export interface FinishContractInput {
51
- assistantText: string;
52
68
  sessionEntries?: ReadonlyArray<unknown>;
53
69
  assistantTurnId?: string | null;
54
70
  recentEntryLimit?: number;
71
+ rigor?: Rigor;
72
+ activeAcceptance?: UserTaskAcceptance;
73
+ /** Owning session workspace, supplied by the finish registration. */
74
+ workspaceRoot?: string;
55
75
  }
56
76
 
57
77
  interface ToolCallEvidenceCandidate {
58
78
  turnId?: string;
59
79
  toolCallId: string;
60
80
  command: string;
81
+ check?: string;
82
+ paths?: string[];
83
+ execution?: ValidationExecutionEvidence;
61
84
  }
62
85
 
63
86
  interface MutationCandidate {
@@ -65,32 +88,26 @@ interface MutationCandidate {
65
88
  paths: string[];
66
89
  }
67
90
 
68
- const LIMITATION_PATTERNS: ReadonlyArray<RegExp> = [
69
- /\b(?:blocked by|blocker|blockers|unable to|not able to|could not|couldn't|cannot|can't)\b/i,
70
- /\b(?:did not|didn't|have not|haven't|has not|hasn't|was not able|wasn't able)\b/i,
71
- /\b(?:not complete|incomplete)\b/i,
72
- /\b(?:not|un)(?:\s|-)?(?:validated|verified|tested)\b/i,
73
- /^\s*Tests\s*:\s*(?:not run|not executed|not available|failed|blocked|skipped)\b/im,
74
- /^\s*Known gaps?\s*:\s*(?!\s*(?:none|no\b|n\/a|not applicable)\b).+/im,
75
- /\bremaining(?:\s+\w+){0,3}\s+(?:work|issue|issues|gap|gaps|blocker|blockers)\b/i,
76
- ];
77
-
78
91
  /**
79
92
  * Action-scoped completion contract. The engine keys off what the turn actually
80
93
  * DID, not how the prompt was phrased: it engages only when the recent window
81
94
  * mutated workspace state and then settled (turn_end) without recording
82
- * validation evidence or an explicit limitation. The turn settling is itself
95
+ * validation evidence or a limitation receipt. The turn settling is itself
83
96
  * the completion signal, so the model never has to type "done" to be gated, and
84
97
  * a work request phrased as a question cannot bypass the gate.
85
98
  *
86
99
  * Decision order (pure function of ledger receipts):
87
100
  * 1. no mutating receipt in the window -> ok/no_mutation
88
101
  * 2. validation evidence present -> ok/validation_evidence
89
- * 3. explicit limitation stated in the text -> ok/explicit_limitation
102
+ * 3. successful `limitation` receipt present -> ok/explicit_limitation
90
103
  * 4. otherwise -> engage/unvalidated_mutation
104
+ *
105
+ * The assistant's prose never enters the decision. A limitation counts only
106
+ * as a `limitation` tool_call paired with a non-error tool_result inside the
107
+ * same window the mutation scan uses, so a run cannot satisfy the contract by
108
+ * wording alone and a real limitation is never missed for its phrasing.
91
109
  */
92
110
  export function assessFinishContract(input: FinishContractInput): FinishContractAssessment {
93
- const assistantText = input.assistantText.trim();
94
111
  const sessionEntries = input.sessionEntries ?? [];
95
112
  const assistantTurnId = input.assistantTurnId ?? null;
96
113
  const window = recentEntries(sessionEntries, assistantTurnId, input.recentEntryLimit ?? DEFAULT_RECENT_ENTRY_LIMIT);
@@ -100,13 +117,39 @@ export function assessFinishContract(input: FinishContractInput): FinishContract
100
117
  return { kind: "ok", reason: "no_mutation", evidence: [], mutatedPaths };
101
118
  }
102
119
 
103
- const evidence = collectValidationEvidence(window);
120
+ const acceptanceChecks = new Set(input.activeAcceptance?.verification.map((item) => item.check) ?? []);
121
+ const evidence = collectValidationEvidence(window, acceptanceChecks, input.workspaceRoot);
122
+ const limitations = collectLimitationEvidence(window);
123
+ const required = input.rigor === "high" ? (input.activeAcceptance?.verification ?? []) : [];
124
+ if (required.length > 0) {
125
+ const passes = new Set(
126
+ required.filter((check) => evidence.some((item) => acceptanceCheckPassed(item, check, input.workspaceRoot))),
127
+ );
128
+ const limited = new Set(limitations.flatMap((item) => item.paths ?? []));
129
+ const missing = [
130
+ ...new Set(required.filter((check) => !passes.has(check) && !limited.has(check.check)).map((item) => item.check)),
131
+ ];
132
+ if (missing.length > 0)
133
+ return {
134
+ kind: "engage",
135
+ reason: "unvalidated_mutation",
136
+ message: `[Clio Coder] high-rigor finish gate: operator acceptance still requires passing validation for: ${missing.join(", ")}. Run each named check or include its exact id in limitation.paths.`,
137
+ evidence: [...evidence, ...limitations],
138
+ mutatedPaths,
139
+ };
140
+ return {
141
+ kind: "ok",
142
+ reason: passes.size === required.length ? "validation_evidence" : "explicit_limitation",
143
+ evidence: [...evidence, ...limitations],
144
+ mutatedPaths,
145
+ };
146
+ }
104
147
  if (evidence.length > 0) {
105
148
  return { kind: "ok", reason: "validation_evidence", evidence, mutatedPaths };
106
149
  }
107
150
 
108
- if (hasExplicitLimitation(assistantText)) {
109
- return { kind: "ok", reason: "explicit_limitation", evidence: [], mutatedPaths };
151
+ if (limitations.length > 0) {
152
+ return { kind: "ok", reason: "explicit_limitation", evidence: limitations, mutatedPaths };
110
153
  }
111
154
 
112
155
  return {
@@ -118,16 +161,80 @@ export function assessFinishContract(input: FinishContractInput): FinishContract
118
161
  };
119
162
  }
120
163
 
164
+ function acceptanceCheckPassed(
165
+ evidence: FinishContractEvidence,
166
+ required: UserTaskAcceptance["verification"][number],
167
+ workspaceRoot: string | undefined,
168
+ ): boolean {
169
+ if (evidence.kind !== "validation_command" || evidence.check !== required.check || !workspaceRoot || !evidence.source)
170
+ return false;
171
+ if (evidence.durationMs !== undefined && evidence.durationMs > required.timeoutMs) return false;
172
+ const root = resolve(workspaceRoot);
173
+ if (evidence.source.kind === "project-catalog")
174
+ return evidence.source.path === resolve(root, PROJECT_VERIFIER_CATALOG_RELATIVE_PATH);
175
+ return (
176
+ evidence.source.path === resolve(root, "package.json") &&
177
+ evidence.cwd === root &&
178
+ validationCheckId(evidence.argv?.join(" ") ?? "") === required.check
179
+ );
180
+ }
181
+
121
182
  /**
122
- * The single retained text escape valve: the assistant explicitly states what
123
- * it could not verify. Everything else about the contract is receipt-grounded;
124
- * this is the one place prose still drives the decision. Candidate for a future
125
- * structured "limitation" signal so the whole gate becomes text-independent.
183
+ * Successful `limitation` receipts over the recent window. A receipt is a
184
+ * `limitation` tool_call whose tool_result in the same window is not an
185
+ * error, judged by the same `successfulToolResultId` rule the mutation and
186
+ * validation scans use. A call the tool rejected (bad reason, empty scope)
187
+ * leaves no receipt, so it cannot settle the contract.
126
188
  */
127
- function hasExplicitLimitation(text: string): boolean {
128
- const normalized = text.trim();
129
- if (normalized.length === 0) return false;
130
- return LIMITATION_PATTERNS.some((pattern) => pattern.test(normalized));
189
+ function collectLimitationEvidence(recent: ReadonlyArray<unknown>): FinishContractEvidence[] {
190
+ const evidence: FinishContractEvidence[] = [];
191
+ const calls = new Map<string, ToolCallEvidenceCandidate>();
192
+ const seen = new Set<string>();
193
+
194
+ for (const entry of recent) {
195
+ const call = limitationToolCall(entry);
196
+ if (call !== null) {
197
+ calls.set(call.toolCallId, call);
198
+ continue;
199
+ }
200
+ const resultId = successfulToolResultId(entry);
201
+ if (resultId === null) continue;
202
+ const candidate = calls.get(resultId);
203
+ if (candidate === undefined) continue;
204
+ const item: FinishContractEvidence = {
205
+ kind: "limitation",
206
+ summary: candidate.command,
207
+ ...(candidate.paths ? { paths: candidate.paths } : {}),
208
+ };
209
+ if (candidate.turnId !== undefined) item.turnId = candidate.turnId;
210
+ pushEvidence(evidence, seen, item);
211
+ }
212
+
213
+ return evidence;
214
+ }
215
+
216
+ function limitationToolCall(entry: unknown): ToolCallEvidenceCandidate | null {
217
+ const record = asRecord(entry);
218
+ if (record?.kind !== "message" || record.role !== "tool_call") return null;
219
+ const payload = asRecord(record.payload);
220
+ if (payload === null) return null;
221
+ const toolName = stringFromFirst(payload, ["name", "toolName", "tool"]);
222
+ if (toolName !== ToolNames.Limitation) return null;
223
+ const toolCallId = stringFromFirst(payload, ["toolCallId", "tool_call_id", "id"]) ?? turnIdOf(entry);
224
+ if (toolCallId === null) return null;
225
+ const args = asRecord(payload.args ?? payload.arguments ?? payload.input);
226
+ const scope = typeof args?.scope === "string" ? args.scope.trim() : "";
227
+ const reason = typeof args?.reason === "string" ? args.reason.trim() : "";
228
+ const candidate: ToolCallEvidenceCandidate = {
229
+ toolCallId,
230
+ paths: Array.isArray(args?.paths)
231
+ ? args.paths.filter((item): item is string => typeof item === "string").map((item) => item.trim())
232
+ : [],
233
+ command: `limitation recorded: ${scope.length > 0 ? scope : "unspecified"} (reason=${reason.length > 0 ? reason : "unspecified"})`,
234
+ };
235
+ const turnId = turnIdOf(entry);
236
+ if (turnId !== null) candidate.turnId = turnId;
237
+ return candidate;
131
238
  }
132
239
 
133
240
  /**
@@ -226,7 +333,11 @@ function pushPath(paths: string[], seen: Set<string>, path: string): void {
226
333
  * Inspection was removed because inspecting the repo is never a mutation, so it
227
334
  * can no longer be on the path to engaging the contract.
228
335
  */
229
- function collectValidationEvidence(recent: ReadonlyArray<unknown>): FinishContractEvidence[] {
336
+ function collectValidationEvidence(
337
+ recent: ReadonlyArray<unknown>,
338
+ acceptanceChecks: ReadonlySet<string>,
339
+ workspaceRoot: string | undefined,
340
+ ): FinishContractEvidence[] {
230
341
  const evidence: FinishContractEvidence[] = [];
231
342
  const toolCalls = new Map<string, ToolCallEvidenceCandidate>();
232
343
  const dispatchCalls = new Map<string, ToolCallEvidenceCandidate>();
@@ -239,7 +350,7 @@ function collectValidationEvidence(recent: ReadonlyArray<unknown>): FinishContra
239
350
  continue;
240
351
  }
241
352
 
242
- const call = bashValidationCall(entry);
353
+ const call = bashValidationCall(entry, acceptanceChecks, workspaceRoot);
243
354
  if (call !== null) {
244
355
  toolCalls.set(call.toolCallId, call);
245
356
  continue;
@@ -267,19 +378,19 @@ function collectValidationEvidence(recent: ReadonlyArray<unknown>): FinishContra
267
378
  }
268
379
  const candidate = toolCalls.get(resultId);
269
380
  if (candidate !== undefined) {
270
- pushEvidence(evidence, seen, validationEvidence(candidate));
381
+ pushEvidence(evidence, seen, validationEvidence(candidate, entry));
271
382
  }
272
383
  continue;
273
384
  }
274
385
 
275
- const bashExecution = bashExecutionEvidence(entry);
386
+ const bashExecution = bashExecutionEvidence(entry, acceptanceChecks, workspaceRoot);
276
387
  if (bashExecution !== null) pushEvidence(evidence, seen, bashExecution);
277
388
  }
278
389
 
279
390
  return evidence;
280
391
  }
281
392
 
282
- function recentEntries(
393
+ export function recentEntries(
283
394
  entries: ReadonlyArray<unknown>,
284
395
  assistantTurnId: string | null,
285
396
  recentEntryLimit: number,
@@ -299,7 +410,11 @@ function recentEntries(
299
410
  return entries.slice(startInclusive, endExclusive);
300
411
  }
301
412
 
302
- function bashValidationCall(entry: unknown): ToolCallEvidenceCandidate | null {
413
+ function bashValidationCall(
414
+ entry: unknown,
415
+ acceptanceChecks: ReadonlySet<string>,
416
+ workspaceRoot: string | undefined,
417
+ ): ToolCallEvidenceCandidate | null {
303
418
  const record = asRecord(entry);
304
419
  if (record?.kind !== "message" || record.role !== "tool_call") return null;
305
420
  const payload = asRecord(record.payload);
@@ -309,13 +424,15 @@ function bashValidationCall(entry: unknown): ToolCallEvidenceCandidate | null {
309
424
  const args = asRecord(payload.args ?? payload.arguments ?? payload.input);
310
425
  const command = typeof args?.command === "string" ? args.command : null;
311
426
  if (command === null) return null;
312
- const detected = detectValidationCommand(command);
427
+ const detected = acceptanceValidationCommand(command, acceptanceChecks);
313
428
  if (detected.kind !== "validation") return null;
314
429
  const toolCallId = stringFromFirst(payload, ["toolCallId", "tool_call_id", "id"]) ?? turnIdOf(entry);
315
430
  if (toolCallId === null) return null;
316
431
  const candidate: ToolCallEvidenceCandidate = {
317
432
  toolCallId,
318
433
  command: detected.matched,
434
+ ...validationCheckFields(detected.matched),
435
+ execution: packageCommandExecution(command, args?.cwd, workspaceRoot),
319
436
  };
320
437
  const turnId = turnIdOf(entry);
321
438
  if (turnId !== null) candidate.turnId = turnId;
@@ -380,6 +497,7 @@ function validationToolCall(entry: unknown): ToolCallEvidenceCandidate | null {
380
497
  const candidate: ToolCallEvidenceCandidate = {
381
498
  toolCallId,
382
499
  command: summary,
500
+ ...validationCheckFields(summary),
383
501
  };
384
502
  const turnId = turnIdOf(entry);
385
503
  if (turnId !== null) candidate.turnId = turnId;
@@ -414,7 +532,8 @@ function successfulToolResultId(entry: unknown): string | null {
414
532
  if (payload.isError === true || payload.error === true) return null;
415
533
  const result = asRecord(payload.result);
416
534
  const details = asRecord(result?.details);
417
- if (details?.kind === "error") return null;
535
+ if (result?.kind === "error" || details?.kind === "error") return null;
536
+ if (typeof details?.exitCode === "number" && details.exitCode !== 0) return null;
418
537
  return stringFromFirst(payload, ["toolCallId", "tool_call_id", "id"]);
419
538
  }
420
539
 
@@ -482,18 +601,24 @@ function dispatchReceiptAgentId(
482
601
  return candidate?.command.match(/^agent=([^\s]+)/)?.[1] ?? "unknown";
483
602
  }
484
603
 
485
- function bashExecutionEvidence(entry: unknown): FinishContractEvidence | null {
604
+ function bashExecutionEvidence(
605
+ entry: unknown,
606
+ acceptanceChecks: ReadonlySet<string>,
607
+ workspaceRoot: string | undefined,
608
+ ): FinishContractEvidence | null {
486
609
  const record = asRecord(entry);
487
610
  if (record?.kind !== "bashExecution") return null;
488
611
  if (typeof record.command !== "string") return null;
489
612
  if (record.cancelled === true) return null;
490
613
  if (record.exitCode !== 0) return null;
491
- const detected = detectValidationCommand(record.command);
614
+ const detected = acceptanceValidationCommand(record.command, acceptanceChecks);
492
615
  if (detected.kind !== "validation") return null;
493
616
  const contextMarker = record.excludeFromContext === true ? " [not sent to model]" : "";
494
617
  const evidence: FinishContractEvidence = {
495
618
  kind: "validation_command",
496
619
  summary: `validation command passed: ${detected.matched}${contextMarker}`,
620
+ ...validationCheckFields(detected.matched),
621
+ ...packageCommandExecution(record.command, record.cwd, workspaceRoot),
497
622
  };
498
623
  const turnId = turnIdOf(entry);
499
624
  if (turnId !== null) evidence.turnId = turnId;
@@ -515,17 +640,80 @@ function protectedArtifactEvidence(entry: unknown): FinishContractEvidence | nul
515
640
  return evidence;
516
641
  }
517
642
 
518
- function validationEvidence(candidate: ToolCallEvidenceCandidate): FinishContractEvidence {
643
+ function validationEvidence(candidate: ToolCallEvidenceCandidate, entry: unknown): FinishContractEvidence {
519
644
  const evidence: FinishContractEvidence = {
520
645
  kind: "validation_command",
521
646
  summary: `validation command passed: ${candidate.command}`,
647
+ ...(candidate.check ? { check: candidate.check } : {}),
648
+ ...(candidate.execution ?? declaredCheckExecution(entry, candidate.check)),
522
649
  };
523
650
  if (candidate.turnId !== undefined) evidence.turnId = candidate.turnId;
524
651
  return evidence;
525
652
  }
526
653
 
654
+ /** Preserve the verifier's execution facts only when they identify the declared check. */
655
+ function declaredCheckExecution(entry: unknown, check: string | undefined): ValidationExecutionEvidence {
656
+ const payload = asRecord(asRecord(entry)?.payload);
657
+ const details = asRecord(asRecord(payload?.result)?.details);
658
+ const source = asRecord(details?.source);
659
+ if (
660
+ !details ||
661
+ details.check !== check ||
662
+ details.exitCode !== 0 ||
663
+ details.aborted === true ||
664
+ details.timedOut === true ||
665
+ details.outputCapped === true ||
666
+ (source?.kind !== "package.json" && source?.kind !== "project-catalog") ||
667
+ typeof source.path !== "string" ||
668
+ typeof details.cwd !== "string" ||
669
+ !Array.isArray(details.argv) ||
670
+ !details.argv.every((arg): arg is string => typeof arg === "string")
671
+ )
672
+ return {};
673
+ if (source.kind === "project-catalog") {
674
+ const argv = details.argv;
675
+ if (
676
+ !Array.isArray(details.declaredCommand) ||
677
+ details.declaredCommand.length !== details.argv.length ||
678
+ !details.declaredCommand.every((arg, index) => arg === argv[index]) ||
679
+ typeof details.declaredCwd !== "string" ||
680
+ typeof details.declaredTimeoutMs !== "number" ||
681
+ typeof details.durationMs !== "number" ||
682
+ details.durationMs > details.declaredTimeoutMs ||
683
+ resolve(source.path, "../..", details.declaredCwd) !== details.cwd
684
+ )
685
+ return {};
686
+ }
687
+ return {
688
+ source: { kind: source.kind, path: source.path },
689
+ cwd: details.cwd,
690
+ argv: details.argv,
691
+ ...(typeof details.durationMs === "number" ? { durationMs: details.durationMs } : {}),
692
+ };
693
+ }
694
+
695
+ /** Exact package commands retain their execution cwd; a matched substring proves no project identity. */
696
+ function packageCommandExecution(
697
+ command: string,
698
+ cwd: unknown,
699
+ workspaceRoot: string | undefined,
700
+ ): ValidationExecutionEvidence {
701
+ if (
702
+ !workspaceRoot ||
703
+ !/^(?:npm|pnpm|yarn|bun) /u.test(command.trim()) ||
704
+ validationCheckId(command.trim()) === undefined
705
+ )
706
+ return {};
707
+ const executionCwd = resolve(workspaceRoot, typeof cwd === "string" ? cwd : ".");
708
+ return {
709
+ source: { kind: "package.json", path: resolve(executionCwd, "package.json") },
710
+ cwd: executionCwd,
711
+ argv: command.trim().split(/\s+/u),
712
+ };
713
+ }
714
+
527
715
  function pushEvidence(evidence: FinishContractEvidence[], seen: Set<string>, item: FinishContractEvidence): void {
528
- const key = `${item.kind}\0${item.summary}\0${item.turnId ?? ""}`;
716
+ const key = JSON.stringify(item);
529
717
  if (seen.has(key)) return;
530
718
  seen.add(key);
531
719
  evidence.push(item);
@@ -554,3 +742,27 @@ function stringFromFirst(record: Record<string, unknown>, keys: ReadonlyArray<st
554
742
  function asRecord(value: unknown): Record<string, unknown> | null {
555
743
  return value !== null && typeof value === "object" ? (value as Record<string, unknown>) : null;
556
744
  }
745
+
746
+ /** Exact check ids, never substring matches against rendered evidence summaries. */
747
+ function validationCheckId(command: string): string | undefined {
748
+ return (
749
+ /^(?:npm|pnpm|yarn|bun) (?:run )?([a-z0-9][a-z0-9._:-]*)$/.exec(command)?.[1] ??
750
+ /^verify ([a-z0-9][a-z0-9._:-]*)$/.exec(command)?.[1]
751
+ );
752
+ }
753
+
754
+ function validationCheckFields(command: string): { check?: string } {
755
+ const check = validationCheckId(command);
756
+ return check ? { check } : {};
757
+ }
758
+
759
+ /** Additional package scripts are admitted only by the operator's typed acceptance. */
760
+ function acceptanceValidationCommand(
761
+ command: string,
762
+ checks: ReadonlySet<string>,
763
+ ): ReturnType<typeof detectValidationCommand> {
764
+ const exact = command.trim();
765
+ const check = /^npm run ([a-z0-9][a-z0-9._:-]*)$/.exec(exact)?.[1];
766
+ if (check && checks.has(check)) return { kind: "validation", matched: exact };
767
+ return detectValidationCommand(command);
768
+ }
@@ -42,4 +42,23 @@ export {
42
42
  type PathPolicyKind,
43
43
  type PathPolicyOperation,
44
44
  } from "./path-policy.js";
45
- export { parseRigorOverride, type Rigor, resolveRigor } from "./rigor.js";
45
+ export {
46
+ parseRigorOverride,
47
+ type Rigor,
48
+ type RigorResolution,
49
+ type RigorSource,
50
+ resolveRigor,
51
+ rigorResolution,
52
+ } from "./rigor.js";
53
+ export {
54
+ describeValidationContract,
55
+ loadValidationContract,
56
+ parseValidationContractText,
57
+ VALIDATION_CONTRACT_CAPS,
58
+ VALIDATION_CONTRACT_MARKDOWN_PATH,
59
+ VALIDATION_CONTRACT_YAML_PATHS,
60
+ type ValidationContract,
61
+ type ValidationContractArtifact,
62
+ type ValidationContractLoadResult,
63
+ type ValidationContractRuntime,
64
+ } from "./validation-contract.js";
@@ -204,12 +204,15 @@ export function createSafetyPolicyEngine(options: SafetyPolicyEngineOptions = {}
204
204
  const pathPolicyInput = projectPolicy.disableDefaultPathPolicy
205
205
  ? projectPolicy.pathPolicy
206
206
  : mergePathPolicyInputs(DEFAULT_DAMAGE_CONTROL_PATH_POLICY, projectPolicy.pathPolicy);
207
- // Clio's own secret store, by absolute path. The static default list carries
208
- // the `credentials.yaml` literal; the expansion has to happen here because
207
+ // Clio's own secret store and user skills, by absolute path.
208
+ // Config directory expansion has to happen here because
209
209
  // the list cannot call config helpers at module scope.
210
210
  const expandedDefaults = projectPolicy.disableDefaultPathPolicy
211
211
  ? pathPolicyInput
212
- : mergePathPolicyInputs(pathPolicyInput, { zeroAccessPaths: clioCredentialStorePaths() });
212
+ : mergePathPolicyInputs(pathPolicyInput, {
213
+ zeroAccessPaths: clioCredentialStorePaths(),
214
+ readOnlyPaths: clioSkillsRootPaths(),
215
+ });
213
216
  const pathPolicy = compilePathPolicy(expandedDefaults, projectPolicyRoot);
214
217
  // Bash-read scanning tests argument tokens against zero-access entries only:
215
218
  // read-only paths stay readable from bash by design, secrets do not.
@@ -478,6 +481,15 @@ function clioCredentialStorePaths(): string[] {
478
481
  }
479
482
  }
480
483
 
484
+ /** Absolute path of Clio's user skills, when resolvable. */
485
+ function clioSkillsRootPaths(): string[] {
486
+ try {
487
+ return [path.join(clioConfigDir(), "skills")];
488
+ } catch {
489
+ return [];
490
+ }
491
+ }
492
+
481
493
  /**
482
494
  * The exact safe presence form: `grep -q` or `grep -sq` (either flag order)
483
495
  * with a `^NAME=`-shaped pattern and a single file argument. Exit code only;
@@ -966,10 +978,16 @@ function cwdArg(args: Record<string, unknown> | undefined, fallback: string): st
966
978
  }
967
979
 
968
980
  /**
969
- * Tools whose arguments are a file's contents rather than a command line.
970
- * Their damage-control scan is the destination path alone.
981
+ * Tools whose arguments carry file contents or task prose, not command lines.
982
+ * Their damage-control scan is the destination path alone, when present.
971
983
  */
972
- const CONTENT_BEARING_TOOLS: ReadonlySet<string> = new Set([ToolNames.Write, ToolNames.Edit, ToolNames.Artifact]);
984
+ const CONTENT_BEARING_TOOLS: ReadonlySet<string> = new Set([
985
+ ToolNames.Write,
986
+ ToolNames.Edit,
987
+ ToolNames.Artifact,
988
+ ToolNames.Dispatch,
989
+ ToolNames.Tasks,
990
+ ]);
973
991
 
974
992
  /**
975
993
  * The text damage-control rules are matched against.
@@ -991,6 +1009,8 @@ const CONTENT_BEARING_TOOLS: ReadonlySet<string> = new Set([ToolNames.Write, Too
991
1009
  * full. Only the destination path is scanned for a mutation tool, which keeps
992
1010
  * any path-shaped rule working; where the file may land is the write tool's own
993
1011
  * gate in `writePathClass`.
1012
+ * Dispatch and task-board prose likewise does not execute; worker commands
1013
+ * are scanned when the worker calls an execute-class tool.
994
1014
  */
995
1015
  function damageControlScan(call: ClassifierCall): string {
996
1016
  if (!CONTENT_BEARING_TOOLS.has(call.tool)) return serializeArgs(call.args);
@@ -5,44 +5,71 @@
5
5
  *
6
6
  * - `normal`: the finish-contract advisory stays a soft `warn` reminder.
7
7
  * - `high`: an unvalidated completion claim re-prompts the model to run
8
- * validation or state a limitation before the turn settles.
8
+ * validation or record a limitation before the turn settles.
9
9
  *
10
10
  * Rigor resolves from a per-session / per-dispatch override (the `CLIO_CODER_RIGOR`
11
11
  * env var today) layered over a repo-derived default. The repo-derived default
12
- * is `high` when the workspace declares a scientific-validation contract by the
13
- * documented path convention below, and `normal` otherwise. This keeps the
14
- * evidence bar derived from what the repo actually is rather than from a global
15
- * toggle.
12
+ * is `high` only when the workspace's scientific-validation contract parses
13
+ * under the version-1 schema in `validation-contract.ts`. A contract that
14
+ * fails to parse is diagnosed and leaves rigor at `normal`, and a Markdown
15
+ * `VALIDATION.md` is advisory prose that never raises rigor on its own. This
16
+ * keeps the evidence bar derived from what the repo actually declares rather
17
+ * than from a filename or a global toggle.
16
18
  */
17
19
 
18
- import { existsSync } from "node:fs";
19
- import path from "node:path";
20
+ import { describeValidationContract, loadValidationContract } from "./validation-contract.js";
20
21
 
21
22
  export type Rigor = "normal" | "high";
22
23
 
23
- /**
24
- * Workspace-root files that declare a scientific-validation contract. Presence
25
- * of any one of these raises the repo-derived rigor default to `high`. The
26
- * convention is intentionally small and explicit: a project opts into a higher
27
- * evidence bar by committing a validation contract at its root.
28
- */
29
- const VALIDATION_CONTRACT_FILES: ReadonlyArray<string> = [
30
- path.join(".clio-coder", "validation.yaml"),
31
- path.join(".clio-coder", "validation.yml"),
32
- "validation.yaml",
33
- "validation.yml",
34
- "VALIDATION.md",
35
- ];
24
+ /** Why rigor settled where it did, so a caller can print the reason. */
25
+ export type RigorSource = "override" | "validation-contract" | "invalid-contract" | "markdown-advisory" | "none";
26
+
27
+ export interface RigorResolution {
28
+ rigor: Rigor;
29
+ source: RigorSource;
30
+ /** Present when a contract was found but could not raise rigor: the parse fault or the Markdown advisory note. */
31
+ diagnostic?: string;
32
+ /** The repository-relative contract path, when one was found. */
33
+ contractPath?: string;
34
+ }
35
+
36
+ export interface RigorOptions {
37
+ cwd?: string;
38
+ override?: Rigor | null;
39
+ }
36
40
 
37
41
  /**
38
- * Resolve the effective rigor. An explicit override (`"high"` | `"normal"`)
39
- * always wins; otherwise the repo-derived default keys off the presence of a
40
- * validation contract at the workspace root.
42
+ * Resolve the effective rigor and the reason for it. An explicit override
43
+ * (`"high"` | `"normal"`) always wins; otherwise the repo-derived default keys
44
+ * off the parsed validation contract at the workspace root.
41
45
  */
42
- export function resolveRigor(options: { cwd?: string; override?: Rigor | null }): Rigor {
43
- if (options.override === "high" || options.override === "normal") return options.override;
46
+ export function rigorResolution(options: RigorOptions): RigorResolution {
47
+ if (options.override === "high" || options.override === "normal") {
48
+ return { rigor: options.override, source: "override" };
49
+ }
44
50
  const cwd = options.cwd ?? process.cwd();
45
- return hasValidationContract(cwd) ? "high" : "normal";
51
+ const loaded = loadValidationContract(cwd);
52
+ if (!loaded.ok) {
53
+ const resolution: RigorResolution = { rigor: "normal", source: "invalid-contract", contractPath: loaded.path };
54
+ const diagnostic = describeValidationContract(loaded);
55
+ if (diagnostic !== null) resolution.diagnostic = diagnostic;
56
+ return resolution;
57
+ }
58
+ if (loaded.contract !== null) {
59
+ return { rigor: "high", source: "validation-contract", contractPath: loaded.path };
60
+ }
61
+ if ("advisory" in loaded) {
62
+ const resolution: RigorResolution = { rigor: "normal", source: "markdown-advisory", contractPath: loaded.path };
63
+ const diagnostic = describeValidationContract(loaded);
64
+ if (diagnostic !== null) resolution.diagnostic = diagnostic;
65
+ return resolution;
66
+ }
67
+ return { rigor: "normal", source: "none" };
68
+ }
69
+
70
+ /** The effective rigor alone; see {@link rigorResolution} for the reason. */
71
+ export function resolveRigor(options: RigorOptions): Rigor {
72
+ return rigorResolution(options).rigor;
46
73
  }
47
74
 
48
75
  /**
@@ -57,16 +84,3 @@ export function parseRigorOverride(value: string | null | undefined): Rigor | nu
57
84
  if (normalized === "normal") return "normal";
58
85
  return null;
59
86
  }
60
-
61
- /**
62
- * True when the workspace declares a scientific-validation contract by the
63
- * documented path convention. Pure and resilient: any filesystem error is
64
- * swallowed and treated as "no contract".
65
- */
66
- function hasValidationContract(cwd: string): boolean {
67
- try {
68
- return VALIDATION_CONTRACT_FILES.some((relative) => existsSync(path.join(cwd, relative)));
69
- } catch {
70
- return false;
71
- }
72
- }