@intentius/chant 0.91.0 → 0.94.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (361) hide show
  1. package/dist/cli/commands/build.d.ts.map +1 -1
  2. package/dist/cli/commands/lint.d.ts.map +1 -1
  3. package/dist/cli/handlers/graph.d.ts.map +1 -1
  4. package/dist/cli/handlers/operator.d.ts +6 -0
  5. package/dist/cli/handlers/operator.d.ts.map +1 -1
  6. package/dist/cli/handlers/run.d.ts.map +1 -1
  7. package/dist/cli/main.d.ts.map +1 -1
  8. package/dist/cli/mcp/workspace-plugins.d.ts +6 -6
  9. package/dist/cli/mcp/workspace-tools.d.ts +2 -1
  10. package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
  11. package/dist/cli/plugins.d.ts.map +1 -1
  12. package/dist/cli/registry.d.ts +12 -2
  13. package/dist/cli/registry.d.ts.map +1 -1
  14. package/dist/codegen/pinned-upgrade.d.ts.map +1 -1
  15. package/dist/config.d.ts +22 -0
  16. package/dist/config.d.ts.map +1 -1
  17. package/dist/lexicon-module.d.ts +22 -0
  18. package/dist/lexicon-module.d.ts.map +1 -1
  19. package/dist/lifecycle/converge-ledger.d.ts +19 -0
  20. package/dist/lifecycle/converge-ledger.d.ts.map +1 -1
  21. package/dist/lifecycle/run-ledger.d.ts +2 -0
  22. package/dist/lifecycle/run-ledger.d.ts.map +1 -1
  23. package/dist/lifecycle/symptoms.d.ts +51 -0
  24. package/dist/lifecycle/symptoms.d.ts.map +1 -1
  25. package/dist/lint/rules/index.d.ts +4 -2
  26. package/dist/lint/rules/index.d.ts.map +1 -1
  27. package/dist/lint/rules/op/index.d.ts +14 -3
  28. package/dist/lint/rules/op/index.d.ts.map +1 -1
  29. package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts.map +1 -1
  30. package/dist/lint/rules/op/sys010-key-over-plain-http.d.ts +29 -0
  31. package/dist/lint/rules/op/sys010-key-over-plain-http.d.ts.map +1 -0
  32. package/dist/lint/rules/sys001-no-literal-key.d.ts +3 -0
  33. package/dist/lint/rules/sys001-no-literal-key.d.ts.map +1 -0
  34. package/dist/op/__fixtures__/decide-stub-backend.d.ts +53 -0
  35. package/dist/op/__fixtures__/decide-stub-backend.d.ts.map +1 -0
  36. package/dist/op/activities/activity-contracts.d.ts +201 -0
  37. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  38. package/dist/op/activities/apply.d.ts.map +1 -1
  39. package/dist/op/activities/change-coverage.d.ts +44 -0
  40. package/dist/op/activities/change-coverage.d.ts.map +1 -0
  41. package/dist/op/activities/converge.d.ts +24 -1
  42. package/dist/op/activities/converge.d.ts.map +1 -1
  43. package/dist/op/activities/decide.d.ts +107 -0
  44. package/dist/op/activities/decide.d.ts.map +1 -0
  45. package/dist/op/activities/index.d.ts +10 -0
  46. package/dist/op/activities/index.d.ts.map +1 -1
  47. package/dist/op/activities/shell.d.ts +32 -0
  48. package/dist/op/activities/shell.d.ts.map +1 -1
  49. package/dist/op/activities/source-release.d.ts +86 -0
  50. package/dist/op/activities/source-release.d.ts.map +1 -0
  51. package/dist/op/activities/source-rollback.d.ts +100 -0
  52. package/dist/op/activities/source-rollback.d.ts.map +1 -0
  53. package/dist/op/activities/work-evidence.d.ts +42 -0
  54. package/dist/op/activities/work-evidence.d.ts.map +1 -0
  55. package/dist/op/activity-contract.d.ts +1 -0
  56. package/dist/op/activity-contract.d.ts.map +1 -1
  57. package/dist/op/activity-profiles.d.ts +16 -0
  58. package/dist/op/activity-profiles.d.ts.map +1 -1
  59. package/dist/op/builders.d.ts +84 -2
  60. package/dist/op/builders.d.ts.map +1 -1
  61. package/dist/op/composites/converge-op.d.ts +31 -4
  62. package/dist/op/composites/converge-op.d.ts.map +1 -1
  63. package/dist/op/decide-backend.d.ts +116 -0
  64. package/dist/op/decide-backend.d.ts.map +1 -0
  65. package/dist/op/decide-config.d.ts +120 -0
  66. package/dist/op/decide-config.d.ts.map +1 -0
  67. package/dist/op/decide-read-inputs.d.ts +40 -0
  68. package/dist/op/decide-read-inputs.d.ts.map +1 -0
  69. package/dist/op/gate-wait.d.ts +43 -0
  70. package/dist/op/gate-wait.d.ts.map +1 -0
  71. package/dist/op/gate.d.ts +5 -2
  72. package/dist/op/gate.d.ts.map +1 -1
  73. package/dist/op/index.d.ts +11 -4
  74. package/dist/op/index.d.ts.map +1 -1
  75. package/dist/op/local-executor.d.ts +29 -4
  76. package/dist/op/local-executor.d.ts.map +1 -1
  77. package/dist/op/local-output.d.ts +2 -0
  78. package/dist/op/local-output.d.ts.map +1 -1
  79. package/dist/op/op-ir.d.ts +2 -0
  80. package/dist/op/op-ir.d.ts.map +1 -1
  81. package/dist/op/op-verb-class.d.ts.map +1 -1
  82. package/dist/op/operator.d.ts +97 -1
  83. package/dist/op/operator.d.ts.map +1 -1
  84. package/dist/op/runtime.d.ts +37 -5
  85. package/dist/op/runtime.d.ts.map +1 -1
  86. package/dist/op/runtimes/local.d.ts.map +1 -1
  87. package/dist/op/source-archive.d.ts +63 -0
  88. package/dist/op/source-archive.d.ts.map +1 -0
  89. package/dist/op/steward-points.d.ts +136 -0
  90. package/dist/op/steward-points.d.ts.map +1 -0
  91. package/dist/op/steward-turn.d.ts +53 -0
  92. package/dist/op/steward-turn.d.ts.map +1 -0
  93. package/dist/op/steward.d.ts +22 -1
  94. package/dist/op/steward.d.ts.map +1 -1
  95. package/dist/op/types.d.ts +7 -0
  96. package/dist/op/types.d.ts.map +1 -1
  97. package/dist/op/work-lease-run.d.ts +1 -1
  98. package/dist/op/work-lease-run.d.ts.map +1 -1
  99. package/dist/workspace/__fixtures__/decide-workspace.d.ts +100 -0
  100. package/dist/workspace/__fixtures__/decide-workspace.d.ts.map +1 -0
  101. package/dist/workspace/answers-ledger.d.ts +76 -0
  102. package/dist/workspace/answers-ledger.d.ts.map +1 -0
  103. package/dist/workspace/box-intent.d.ts +85 -0
  104. package/dist/workspace/box-intent.d.ts.map +1 -0
  105. package/dist/workspace/changes-cli.d.ts +16 -0
  106. package/dist/workspace/changes-cli.d.ts.map +1 -0
  107. package/dist/workspace/changes.d.ts +200 -0
  108. package/dist/workspace/changes.d.ts.map +1 -0
  109. package/dist/workspace/chant-migrations/chud-lexicon-exit-fly-site.d.ts +59 -0
  110. package/dist/workspace/chant-migrations/chud-lexicon-exit-fly-site.d.ts.map +1 -0
  111. package/dist/workspace/chant-migrations/chud-lexicon-exit-live-names.d.ts +51 -0
  112. package/dist/workspace/chant-migrations/chud-lexicon-exit-live-names.d.ts.map +1 -0
  113. package/dist/workspace/chant-migrations/chud-lexicon-exit-rollback.d.ts +26 -0
  114. package/dist/workspace/chant-migrations/chud-lexicon-exit-rollback.d.ts.map +1 -0
  115. package/dist/workspace/chant-migrations/chud-lexicon-exit-ship-inputs.d.ts +59 -0
  116. package/dist/workspace/chant-migrations/chud-lexicon-exit-ship-inputs.d.ts.map +1 -0
  117. package/dist/workspace/chant-migrations/chud-lexicon-exit-ship-policy.d.ts +22 -0
  118. package/dist/workspace/chant-migrations/chud-lexicon-exit-ship-policy.d.ts.map +1 -0
  119. package/dist/workspace/chant-migrations/chud-lexicon-exit.d.ts +71 -0
  120. package/dist/workspace/chant-migrations/chud-lexicon-exit.d.ts.map +1 -0
  121. package/dist/workspace/chant-migrations.d.ts +104 -0
  122. package/dist/workspace/chant-migrations.d.ts.map +1 -0
  123. package/dist/workspace/checks/boxes.d.ts +47 -1
  124. package/dist/workspace/checks/boxes.d.ts.map +1 -1
  125. package/dist/workspace/checks/diagrams.d.ts +31 -0
  126. package/dist/workspace/checks/diagrams.d.ts.map +1 -0
  127. package/dist/workspace/checks/records.d.ts +7 -0
  128. package/dist/workspace/checks/records.d.ts.map +1 -1
  129. package/dist/workspace/checks.d.ts +7 -0
  130. package/dist/workspace/checks.d.ts.map +1 -1
  131. package/dist/workspace/compose-graph.d.ts +2 -0
  132. package/dist/workspace/compose-graph.d.ts.map +1 -1
  133. package/dist/workspace/conformance/index.d.ts.map +1 -1
  134. package/dist/workspace/decide.d.ts +49 -4
  135. package/dist/workspace/decide.d.ts.map +1 -1
  136. package/dist/workspace/decision-points.schema.json +3 -3
  137. package/dist/workspace/declaration.d.ts +58 -0
  138. package/dist/workspace/declaration.d.ts.map +1 -1
  139. package/dist/workspace/declaration.schema.json +119 -1
  140. package/dist/workspace/declared-kinds.d.ts +20 -0
  141. package/dist/workspace/declared-kinds.d.ts.map +1 -1
  142. package/dist/workspace/generated-files.d.ts +7 -0
  143. package/dist/workspace/generated-files.d.ts.map +1 -1
  144. package/dist/workspace/intent-joins.d.ts +5 -5
  145. package/dist/workspace/intent.d.ts.map +1 -1
  146. package/dist/workspace/lineage-check.d.ts +3 -0
  147. package/dist/workspace/lineage-check.d.ts.map +1 -1
  148. package/dist/workspace/lineage-upgrade.d.ts +6 -1
  149. package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
  150. package/dist/workspace/ls.d.ts +39 -1
  151. package/dist/workspace/ls.d.ts.map +1 -1
  152. package/dist/workspace/point-answer.schema.json +95 -0
  153. package/dist/workspace/points-cli.d.ts +2 -0
  154. package/dist/workspace/points-cli.d.ts.map +1 -1
  155. package/dist/workspace/points.d.ts +4 -6
  156. package/dist/workspace/points.d.ts.map +1 -1
  157. package/dist/workspace/reason-codes.d.ts +16 -2
  158. package/dist/workspace/reason-codes.d.ts.map +1 -1
  159. package/dist/workspace/records-cli.d.ts +11 -1
  160. package/dist/workspace/records-cli.d.ts.map +1 -1
  161. package/dist/workspace/records-close.d.ts +1 -1
  162. package/dist/workspace/records-close.d.ts.map +1 -1
  163. package/dist/workspace/records-write.d.ts +11 -5
  164. package/dist/workspace/records-write.d.ts.map +1 -1
  165. package/dist/workspace/records.d.ts +34 -4
  166. package/dist/workspace/records.d.ts.map +1 -1
  167. package/dist/workspace/status-gates.d.ts +7 -2
  168. package/dist/workspace/status-gates.d.ts.map +1 -1
  169. package/dist/workspace/status-stewards.d.ts +65 -1
  170. package/dist/workspace/status-stewards.d.ts.map +1 -1
  171. package/dist/workspace/status.d.ts +24 -0
  172. package/dist/workspace/status.d.ts.map +1 -1
  173. package/dist/workspace/work-cli.d.ts +113 -2
  174. package/dist/workspace/work-cli.d.ts.map +1 -1
  175. package/dist/workspace/work-evidence.d.ts +74 -0
  176. package/dist/workspace/work-evidence.d.ts.map +1 -0
  177. package/dist/workspace/work.d.ts +43 -1
  178. package/dist/workspace/work.d.ts.map +1 -1
  179. package/package.json +1 -1
  180. package/src/cli/commands/audit.test.ts +9 -1
  181. package/src/cli/commands/build.ts +3 -2
  182. package/src/cli/commands/lint.ts +7 -1
  183. package/src/cli/handlers/graph.test.ts +20 -0
  184. package/src/cli/handlers/graph.ts +16 -2
  185. package/src/cli/handlers/operator.ts +34 -2
  186. package/src/cli/handlers/run.test.ts +154 -2
  187. package/src/cli/handlers/run.ts +121 -2
  188. package/src/cli/main.test.ts +10 -0
  189. package/src/cli/main.ts +23 -3
  190. package/src/cli/mcp/workspace-plugins.ts +6 -6
  191. package/src/cli/mcp/workspace-tools.test.ts +35 -19
  192. package/src/cli/mcp/workspace-tools.ts +62 -4
  193. package/src/cli/plugins.ts +11 -3
  194. package/src/cli/registry.ts +12 -2
  195. package/src/cli/serve-mcp-workspace.test.ts +6 -6
  196. package/src/codegen/pinned-upgrade.ts +2 -1
  197. package/src/codegen/release-wiring.test.ts +49 -2
  198. package/src/components/capability-plugin-loader.ts +3 -3
  199. package/src/components/cli-support.ts +2 -2
  200. package/src/config.ts +11 -0
  201. package/src/lexicon-module.ts +56 -1
  202. package/src/lexicon-package.test.ts +96 -0
  203. package/src/lifecycle/converge-ledger.ts +20 -0
  204. package/src/lifecycle/run-ledger.ts +6 -0
  205. package/src/lifecycle/symptoms.ts +95 -0
  206. package/src/lint/rules/index.ts +6 -2
  207. package/src/lint/rules/op/index.ts +17 -4
  208. package/src/lint/rules/op/ops014-converge-rule-refusals.test.ts +23 -1
  209. package/src/lint/rules/op/ops014-converge-rule-refusals.ts +10 -6
  210. package/src/lint/rules/op/sys010-key-over-plain-http.test.ts +52 -0
  211. package/src/lint/rules/op/sys010-key-over-plain-http.ts +100 -0
  212. package/src/lint/rules/sys001-no-literal-key.test.ts +31 -0
  213. package/src/lint/rules/sys001-no-literal-key.ts +67 -0
  214. package/src/op/__fixtures__/decide-stub-backend.ts +143 -0
  215. package/src/op/activities/activity-contracts.ts +190 -1
  216. package/src/op/activities/apply.ts +7 -6
  217. package/src/op/activities/change-coverage.ts +60 -0
  218. package/src/op/activities/converge.test.ts +55 -1
  219. package/src/op/activities/converge.ts +208 -29
  220. package/src/op/activities/decide.live.test.ts +78 -0
  221. package/src/op/activities/decide.test.ts +308 -0
  222. package/src/op/activities/decide.ts +267 -0
  223. package/src/op/activities/index.ts +21 -0
  224. package/src/op/activities/shell.test.ts +91 -1
  225. package/src/op/activities/shell.ts +91 -2
  226. package/src/op/activities/source-release.test.ts +118 -0
  227. package/src/op/activities/source-release.ts +168 -0
  228. package/src/op/activities/source-rollback.test.ts +131 -0
  229. package/src/op/activities/source-rollback.ts +239 -0
  230. package/src/op/activities/work-evidence.ts +62 -0
  231. package/src/op/activity-contract-registry.test.ts +2 -0
  232. package/src/op/activity-contract-registry.ts +2 -2
  233. package/src/op/activity-contract.test.ts +12 -0
  234. package/src/op/activity-contract.ts +6 -1
  235. package/src/op/activity-profiles.ts +29 -0
  236. package/src/op/activity-registry.test.ts +1 -1
  237. package/src/op/activity-registry.ts +2 -2
  238. package/src/op/builders.ts +119 -5
  239. package/src/op/command-gate.test.ts +192 -0
  240. package/src/op/composites/converge-op.test.ts +27 -1
  241. package/src/op/composites/converge-op.ts +69 -15
  242. package/src/op/decide-backend.test.ts +44 -0
  243. package/src/op/decide-backend.ts +212 -0
  244. package/src/op/decide-config.test.ts +52 -0
  245. package/src/op/decide-config.ts +80 -0
  246. package/src/op/decide-read-inputs.ts +117 -0
  247. package/src/op/gate-approval.ts +2 -2
  248. package/src/op/gate-wait.ts +57 -0
  249. package/src/op/gate.ts +8 -3
  250. package/src/op/generate-pipeline.ts +2 -2
  251. package/src/op/index.ts +18 -3
  252. package/src/op/local-executor.ts +151 -15
  253. package/src/op/local-output.test.ts +33 -0
  254. package/src/op/local-output.ts +20 -2
  255. package/src/op/op-ir.test.ts +10 -0
  256. package/src/op/op-ir.ts +4 -0
  257. package/src/op/op-verb-class.ts +1 -0
  258. package/src/op/operator.ts +290 -11
  259. package/src/op/runtime.ts +31 -8
  260. package/src/op/runtimes/local.ts +3 -1
  261. package/src/op/source-archive.ts +161 -0
  262. package/src/op/steward-points.test.ts +364 -0
  263. package/src/op/steward-points.ts +194 -0
  264. package/src/op/steward-turn.ts +79 -0
  265. package/src/op/steward.test.ts +87 -3
  266. package/src/op/steward.ts +25 -1
  267. package/src/op/types.ts +7 -0
  268. package/src/op/work-lease-run.test.ts +20 -1
  269. package/src/op/work-lease-run.ts +2 -2
  270. package/src/workspace/__fixtures__/decide-workspace.ts +129 -0
  271. package/src/workspace/__fixtures__/studio-template-84c8b21.json +67 -0
  272. package/src/workspace/answers-ledger.ts +118 -0
  273. package/src/workspace/box-intent.test.ts +205 -0
  274. package/src/workspace/box-intent.ts +159 -0
  275. package/src/workspace/changes-cli.ts +56 -0
  276. package/src/workspace/changes.schema.json +472 -0
  277. package/src/workspace/changes.test.ts +315 -0
  278. package/src/workspace/changes.ts +512 -0
  279. package/src/workspace/chant-migrations/chud-lexicon-exit-fly-site.ts +234 -0
  280. package/src/workspace/chant-migrations/chud-lexicon-exit-live-names.ts +164 -0
  281. package/src/workspace/chant-migrations/chud-lexicon-exit-rollback.ts +223 -0
  282. package/src/workspace/chant-migrations/chud-lexicon-exit-ship-inputs.ts +195 -0
  283. package/src/workspace/chant-migrations/chud-lexicon-exit-ship-policy.ts +108 -0
  284. package/src/workspace/chant-migrations/chud-lexicon-exit.ts +882 -0
  285. package/src/workspace/chant-migrations.e2e.test.ts +1089 -0
  286. package/src/workspace/chant-migrations.ts +169 -0
  287. package/src/workspace/check-contract.test.ts +4 -2
  288. package/src/workspace/check.schema.json +19 -5
  289. package/src/workspace/checks/boxes.test.ts +82 -2
  290. package/src/workspace/checks/boxes.ts +202 -1
  291. package/src/workspace/checks/diagrams.test.ts +134 -0
  292. package/src/workspace/checks/diagrams.ts +108 -0
  293. package/src/workspace/checks/records.ts +47 -0
  294. package/src/workspace/checks.ts +20 -2
  295. package/src/workspace/compose-graph.ts +2 -0
  296. package/src/workspace/conformance/__fixture__/decisions/decision.kind.mjs +10 -0
  297. package/src/workspace/conformance/__fixture__/decisions/decision.schema.json +26 -0
  298. package/src/workspace/conformance/index.ts +5 -0
  299. package/src/workspace/decide.test.ts +1 -1
  300. package/src/workspace/decide.ts +156 -23
  301. package/src/workspace/decision-points.schema.json +3 -3
  302. package/src/workspace/declaration.schema.json +119 -1
  303. package/src/workspace/declaration.test.ts +58 -0
  304. package/src/workspace/declaration.ts +112 -0
  305. package/src/workspace/declared-kinds.test.ts +31 -5
  306. package/src/workspace/declared-kinds.ts +46 -1
  307. package/src/workspace/generated-files.ts +14 -0
  308. package/src/workspace/graph-cli.ts +1 -1
  309. package/src/workspace/graph.schema.json +9 -1
  310. package/src/workspace/intent-joins.test.ts +5 -5
  311. package/src/workspace/intent-joins.ts +5 -5
  312. package/src/workspace/intent.schema.json +8 -2
  313. package/src/workspace/intent.ts +3 -10
  314. package/src/workspace/lineage-check.ts +16 -1
  315. package/src/workspace/lineage-upgrade-cli.ts +1 -1
  316. package/src/workspace/lineage-upgrade.test.ts +70 -0
  317. package/src/workspace/lineage-upgrade.ts +81 -10
  318. package/src/workspace/ls-contract.test.ts +57 -0
  319. package/src/workspace/ls.schema.json +46 -1
  320. package/src/workspace/ls.ts +62 -3
  321. package/src/workspace/points-cli.ts +17 -3
  322. package/src/workspace/points-write.schema.json +11 -1
  323. package/src/workspace/points.schema.json +51 -0
  324. package/src/workspace/points.test.ts +16 -15
  325. package/src/workspace/points.ts +5 -17
  326. package/src/workspace/read-contract.test.ts +9 -1
  327. package/src/workspace/reason-codes.test.ts +18 -8
  328. package/src/workspace/reason-codes.ts +21 -2
  329. package/src/workspace/records-amend.schema.json +2 -0
  330. package/src/workspace/records-cli.ts +27 -4
  331. package/src/workspace/records-close.schema.json +2 -0
  332. package/src/workspace/records-formats.test.ts +15 -15
  333. package/src/workspace/records-new.schema.json +2 -0
  334. package/src/workspace/records-review.schema.json +2 -0
  335. package/src/workspace/records-sessions-write.e2e.test.ts +77 -0
  336. package/src/workspace/records-sessions-write.test.ts +2 -34
  337. package/src/workspace/records-since.test.ts +8 -7
  338. package/src/workspace/records-write.e2e.test.ts +108 -0
  339. package/src/workspace/records-write.test.ts +32 -41
  340. package/src/workspace/records-write.ts +33 -15
  341. package/src/workspace/records.schema.json +36 -2
  342. package/src/workspace/records.test.ts +49 -4
  343. package/src/workspace/records.ts +109 -3
  344. package/src/workspace/status-contract.test.ts +14 -8
  345. package/src/workspace/status-gates.ts +9 -4
  346. package/src/workspace/status-stewards.ts +155 -5
  347. package/src/workspace/status.schema.json +129 -8
  348. package/src/workspace/status.ts +46 -1
  349. package/src/workspace/work-acceptance.test.ts +274 -0
  350. package/src/workspace/{work-cli.test.ts → work-cli.e2e.test.ts} +1 -1
  351. package/src/workspace/work-cli.ts +193 -10
  352. package/src/workspace/work-evidence.schema.json +212 -0
  353. package/src/workspace/work-evidence.ts +169 -0
  354. package/src/workspace/work-history.schema.json +349 -0
  355. package/src/workspace/work-history.test.ts +156 -0
  356. package/src/workspace/{work-readiness-chud.test.ts → work-readiness.test.ts} +30 -32
  357. package/src/workspace/work.test.ts +20 -2
  358. package/src/workspace/work.ts +100 -1
  359. /package/src/cli/{discovery-skip.test.ts → discovery-skip.e2e.test.ts} +0 -0
  360. /package/src/workspace/conformance/{conformance.test.ts → conformance.e2e.test.ts} +0 -0
  361. /package/src/workspace/{which-chant.test.ts → which-chant.e2e.test.ts} +0 -0
@@ -0,0 +1,134 @@
1
+ /**
2
+ * The diagram checks, WSP131 to WSP133 (#2764).
3
+ */
4
+
5
+ import { execFileSync } from "node:child_process";
6
+ import { mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
7
+ import { tmpdir } from "node:os";
8
+ import { dirname, join } from "node:path";
9
+ import { afterAll, describe, expect, test } from "vitest";
10
+ import { runDeclarationChecks } from "../checks";
11
+ import { sha256Hex } from "../../content-digest";
12
+
13
+ const scratch: string[] = [];
14
+ afterAll(() => {
15
+ for (const d of scratch) rmSync(d, { recursive: true, force: true });
16
+ });
17
+ function repo(files: Record<string, string>): string {
18
+ const root = realpathSync(mkdtempSync(join(tmpdir(), "chant-diagrams-")));
19
+ scratch.push(root);
20
+ execFileSync("git", ["init", "-q"], { cwd: root });
21
+ for (const [path, text] of Object.entries(files)) {
22
+ mkdirSync(dirname(join(root, path)), { recursive: true });
23
+ writeFileSync(join(root, path), text);
24
+ }
25
+ return root;
26
+ }
27
+
28
+ const renderer = { tool: "d2", version: "0.9.0", args: ["--layout=elk", "--theme=0", "--pad=40", "--omit-version"] };
29
+ const diagram = (extra: Record<string, unknown> = {}) => ({
30
+ name: "architecture",
31
+ title: "Studio architecture",
32
+ source: "docs/diagrams/architecture.d2",
33
+ render: "docs/diagrams/architecture.svg",
34
+ renderer,
35
+ ...extra,
36
+ });
37
+
38
+ const declaration = (diagrams: unknown[], extra: Record<string, unknown> = {}) =>
39
+ JSON.stringify({ name: "acme", schema: 1, members: [{ name: "docs", dir: "docs", kind: "other", because: "the docs site", diagrams }], ...extra }, null, 2);
40
+
41
+ /** The findings, less WSP009, which every member of kind other gets. */
42
+ const found = async (root: string) => (await runDeclarationChecks(root, (f) => f, { gather: false })).diagnostics.filter((d) => d.ruleId !== "WSP009");
43
+
44
+ describe("diagram-source-missing (WSP131) and diagram-render-missing (WSP132)", () => {
45
+ test("both files present passes", async () => {
46
+ const root = repo({
47
+ "chant.workspace.json": declaration([diagram()]),
48
+ "docs/diagrams/architecture.d2": "x -> y",
49
+ "docs/diagrams/architecture.svg": "<svg></svg>",
50
+ });
51
+ expect(await found(root)).toEqual([]);
52
+ });
53
+
54
+ test("a missing source fails WSP131, and a missing render fails WSP132, each once", async () => {
55
+ const root = repo({
56
+ "chant.workspace.json": declaration([diagram()]),
57
+ "docs/diagrams/architecture.svg": "<svg></svg>",
58
+ });
59
+ const d = await found(root);
60
+ expect(d.map((x) => [x.ruleId, x.code, x.entity])).toEqual([["WSP131", "diagram-source-missing", "docs"]]);
61
+ expect(d[0].message).toContain("member docs's diagram architecture names the source docs/diagrams/architecture.d2, which does not exist");
62
+ });
63
+
64
+ test("no source (an SVG with none) is never checked for a source", async () => {
65
+ const root = repo({
66
+ "chant.workspace.json": declaration([diagram({ source: null })]),
67
+ "docs/diagrams/architecture.svg": "<svg></svg>",
68
+ });
69
+ expect(await found(root)).toEqual([]);
70
+ });
71
+
72
+ test("a missing render fails WSP132", async () => {
73
+ const root = repo({
74
+ "chant.workspace.json": declaration([diagram()]),
75
+ "docs/diagrams/architecture.d2": "x -> y",
76
+ });
77
+ const d = await found(root);
78
+ expect(d.map((x) => [x.ruleId, x.code])).toEqual([["WSP132", "diagram-render-missing"]]);
79
+ });
80
+
81
+ test("the top-level (workspace's own) diagrams are checked too, reported with no entity", async () => {
82
+ const root = repo({ "chant.workspace.json": declaration([], { diagrams: [diagram({ name: "boundary" })] }), "docs/README.md": "" });
83
+ const d = await found(root);
84
+ expect(d.map((x) => [x.ruleId, x.entity])).toEqual([
85
+ ["WSP131", undefined],
86
+ ["WSP132", undefined],
87
+ ]);
88
+ expect(d[0].message).toContain("the workspace's own diagram boundary names the source");
89
+ });
90
+ });
91
+
92
+ describe("diagram-render-drift (WSP133)", () => {
93
+ const SOURCE = "x -> y\n";
94
+ const HASH = sha256Hex(Buffer.from(SOURCE, "utf-8"));
95
+
96
+ test("no recorded sourceHash: never checked for drift, even when the source changed", async () => {
97
+ const root = repo({
98
+ "chant.workspace.json": declaration([diagram()]),
99
+ "docs/diagrams/architecture.d2": "something else entirely",
100
+ "docs/diagrams/architecture.svg": "<svg></svg>",
101
+ });
102
+ expect(await found(root)).toEqual([]);
103
+ });
104
+
105
+ test("a recorded sourceHash that matches the source passes", async () => {
106
+ const root = repo({
107
+ "chant.workspace.json": declaration([diagram({ sourceHash: HASH })]),
108
+ "docs/diagrams/architecture.d2": SOURCE,
109
+ "docs/diagrams/architecture.svg": "<svg></svg>",
110
+ });
111
+ expect(await found(root)).toEqual([]);
112
+ });
113
+
114
+ test("a source that changed since the recorded hash fails WSP133, without running any renderer", async () => {
115
+ const root = repo({
116
+ "chant.workspace.json": declaration([diagram({ sourceHash: HASH })]),
117
+ "docs/diagrams/architecture.d2": "x -> y -> z\n",
118
+ "docs/diagrams/architecture.svg": "<svg></svg>",
119
+ });
120
+ const d = await found(root);
121
+ expect(d.map((x) => [x.ruleId, x.code])).toEqual([["WSP133", "diagram-render-drift"]]);
122
+ expect(d[0].message).toContain("source docs/diagrams/architecture.d2 changed since docs/diagrams/architecture.svg was rendered from it");
123
+ expect(d[0].message).toContain(`recorded sourceHash ${HASH.slice(0, 12)}`);
124
+ });
125
+
126
+ test("a missing source is left to WSP131; WSP133 finds nothing to compare", async () => {
127
+ const root = repo({
128
+ "chant.workspace.json": declaration([diagram({ sourceHash: HASH })]),
129
+ "docs/diagrams/architecture.svg": "<svg></svg>",
130
+ });
131
+ const d = await found(root);
132
+ expect(d.map((x) => x.ruleId)).toEqual(["WSP131"]);
133
+ });
134
+ });
@@ -0,0 +1,108 @@
1
+ /**
2
+ * The diagram checks, WSP131 to WSP133 (#2764).
3
+ *
4
+ * A declared diagram names a source and a render, pinned to the renderer
5
+ * its render was made with. chant never runs a renderer (ws-052), on a read
6
+ * path or here: these checks read the declaration and the tree checked
7
+ * only, and a render's drift is caught by comparing a recorded content hash,
8
+ * never by re-rendering the source.
9
+ *
10
+ * | Id | Code | Fails when |
11
+ * |---|---|---|
12
+ * | WSP131 | `diagram-source-missing` | a diagram names a source, and it does not exist in the tree read |
13
+ * | WSP132 | `diagram-render-missing` | a diagram's render does not exist in the tree read |
14
+ * | WSP133 | `diagram-render-drift` | a diagram records a `sourceHash`, and the source's bytes now hash to something else |
15
+ *
16
+ * WSP133 is opt-in per diagram: without a recorded `sourceHash` there is
17
+ * nothing to compare, so the entry is silently not checked for drift. A
18
+ * declaration records one by hashing the source when it commits a fresh
19
+ * render, the way a decision's evidence pins a file by hash
20
+ * (`../record-assets.ts`). Comparing that one recorded hash against the
21
+ * source in the tree read is cheap and deterministic, unlike shelling out to
22
+ * the pinned renderer and diffing its output, which chant does nowhere.
23
+ */
24
+
25
+ import { sha256Hex } from "../../content-digest";
26
+ import type { WorkspaceCheck, WorkspaceCheckContext, WorkspaceDiagnostic } from "../checks";
27
+ import { declaredDiagrams, type DiagramDeclaration } from "../declaration";
28
+ import type { ReasonCode } from "../reason-codes";
29
+
30
+ /** The read contract's codes for the diagram findings, carried as `code` on each. */
31
+ export const DIAGRAM_FINDING_CODES = ["diagram-source-missing", "diagram-render-missing", "diagram-render-drift"] as const satisfies readonly ReasonCode[];
32
+
33
+ export const WSP_DIAGRAM_SOURCE_MISSING = "WSP131";
34
+ export const WSP_DIAGRAM_RENDER_MISSING = "WSP132";
35
+ export const WSP_DIAGRAM_RENDER_DRIFT = "WSP133";
36
+
37
+ const where = (d: DiagramDeclaration) => (d.member === null ? "the workspace's own" : `member ${d.member}'s`);
38
+
39
+ function diagramFinding(check: WorkspaceCheck, d: DiagramDeclaration, field: "source" | "render" | "sourceHash", code: (typeof DIAGRAM_FINDING_CODES)[number], message: string): WorkspaceDiagnostic {
40
+ return {
41
+ checkId: check.id,
42
+ severity: check.severity,
43
+ code,
44
+ message: `${code}: ${message}`,
45
+ entity: d.member ?? undefined,
46
+ pointer: `${d.pointer}/${field}`,
47
+ };
48
+ }
49
+
50
+ export const DIAGRAM_CHECKS: readonly WorkspaceCheck[] = [
51
+ {
52
+ id: WSP_DIAGRAM_SOURCE_MISSING,
53
+ name: "diagram-source-missing",
54
+ description: "A diagram's source file, when it names one, exists in the tree read.",
55
+ severity: "error",
56
+ configurable: true,
57
+ check(ctx: WorkspaceCheckContext) {
58
+ const out: WorkspaceDiagnostic[] = [];
59
+ for (const d of declaredDiagrams(ctx.declaration)) {
60
+ if (d.source === null || ctx.tree.stat(d.source) === "file") continue;
61
+ out.push(diagramFinding(this, d, "source", "diagram-source-missing", `${where(d)} diagram ${d.name} names the source ${d.source}, which does not exist${ctx.tree.label}`));
62
+ }
63
+ return out;
64
+ },
65
+ },
66
+ {
67
+ id: WSP_DIAGRAM_RENDER_MISSING,
68
+ name: "diagram-render-missing",
69
+ description: "A diagram's render exists in the tree read.",
70
+ severity: "error",
71
+ configurable: true,
72
+ check(ctx: WorkspaceCheckContext) {
73
+ const out: WorkspaceDiagnostic[] = [];
74
+ for (const d of declaredDiagrams(ctx.declaration)) {
75
+ if (ctx.tree.stat(d.render) === "file") continue;
76
+ out.push(diagramFinding(this, d, "render", "diagram-render-missing", `${where(d)} diagram ${d.name} names the render ${d.render}, which does not exist${ctx.tree.label}`));
77
+ }
78
+ return out;
79
+ },
80
+ },
81
+ {
82
+ id: WSP_DIAGRAM_RENDER_DRIFT,
83
+ name: "diagram-render-drift",
84
+ description: "A diagram that records a sourceHash is unchanged since its render was made: the source's bytes still hash to it. Runs no renderer.",
85
+ severity: "error",
86
+ configurable: true,
87
+ check(ctx: WorkspaceCheckContext) {
88
+ const out: WorkspaceDiagnostic[] = [];
89
+ for (const d of declaredDiagrams(ctx.declaration)) {
90
+ if (d.sourceHash === null || d.source === null) continue;
91
+ if (ctx.tree.stat(d.source) !== "file") continue; // diagram-source-missing already reports it
92
+ const bytes = ctx.tree.bytes ? ctx.tree.bytes(d.source) : Buffer.from(ctx.tree.read(d.source), "utf-8");
93
+ const actual = sha256Hex(bytes);
94
+ if (actual === d.sourceHash) continue;
95
+ out.push(
96
+ diagramFinding(
97
+ this,
98
+ d,
99
+ "sourceHash",
100
+ "diagram-render-drift",
101
+ `${where(d)} diagram ${d.name}'s source ${d.source} changed since ${d.render} was rendered from it: recorded sourceHash ${d.sourceHash.slice(0, 12)} does not match the source's current hash ${actual.slice(0, 12)}${ctx.tree.label}; rerun the renderer and commit the result, or update sourceHash`,
102
+ ),
103
+ );
104
+ }
105
+ return out;
106
+ },
107
+ },
108
+ ];
@@ -15,11 +15,20 @@
15
15
  * | WSP114 | the records of `--kind` can't be read (fixed) |
16
16
  * | WSP115 | a record kind the declaration names is missing or does not load as one (fixed, #2680) |
17
17
  * | WSP116 | an answer kind's decision points file is missing or not valid (fixed, ws-058, #2738) |
18
+ * | WSP117 | a done work item has an acceptance criterion no passing evidence meets (#2772) |
19
+ *
20
+ * WSP117 reads the records of every declared work kind with acceptance
21
+ * criteria, in the working tree, and the records of `--kind`. It carries the
22
+ * reason code `work-acceptance-unmet`.
18
23
  */
19
24
 
20
25
  import type { WorkspaceCheck, WorkspaceDiagnostic } from "../checks";
26
+ import type { ReasonCode } from "../reason-codes";
21
27
  import type { ReadErrorCode, RecordEntry } from "../records";
22
28
 
29
+ /** The read contract's codes the record checks carry as `code`: WSP117's (#2772). */
30
+ export const RECORD_FINDING_CODES = ["work-acceptance-unmet"] as const satisfies readonly ReasonCode[];
31
+
23
32
  /** The records `check --kind` read, or why they could not be read. */
24
33
  export type RecordFacts =
25
34
  | { kind: string; records: readonly (RecordEntry & { file: string })[] }
@@ -124,4 +133,42 @@ export const RECORD_CHECKS: readonly WorkspaceCheck[] = [
124
133
  return out;
125
134
  },
126
135
  },
136
+ {
137
+ id: "WSP117",
138
+ name: "work-acceptance-unmet",
139
+ description:
140
+ "Every done work item meets its acceptance criteria: each has evidence that names it, passed, and has the verification the criterion expects, and a manual verdict is from someone other than the implementer (#2772).",
141
+ severity: "error",
142
+ configurable: true,
143
+ check(ctx) {
144
+ const out: WorkspaceDiagnostic[] = [];
145
+ const seen = new Set<string>();
146
+ const facts = ctx.facts?.records;
147
+ if (facts && !("error" in facts)) {
148
+ for (const r of facts.records) {
149
+ if (r.supersededBy !== null) continue;
150
+ const w = r.warnings.find((x) => x.code === "work-acceptance-unmet");
151
+ if (!w || seen.has(r.file)) continue;
152
+ seen.add(r.file);
153
+ out.push({ checkId: this.id, severity: this.severity, message: w.message, pointer: "", file: r.file, code: "work-acceptance-unmet" });
154
+ }
155
+ }
156
+ for (const k of ctx.facts?.declaredKinds ?? []) {
157
+ for (const a of k.acceptance ?? []) {
158
+ if (a.unmet === null || seen.has(a.file)) continue;
159
+ seen.add(a.file);
160
+ out.push({
161
+ checkId: this.id,
162
+ severity: this.severity,
163
+ message: a.unmet,
164
+ ...(k.declared.member !== null ? { entity: k.declared.member } : {}),
165
+ pointer: "",
166
+ file: a.file,
167
+ code: "work-acceptance-unmet",
168
+ });
169
+ }
170
+ }
171
+ return out;
172
+ },
173
+ },
127
174
  ];
@@ -29,8 +29,12 @@
29
29
  * | WSP111 to WSP114 | records read with `--kind` (#2549) |
30
30
  * | WSP115 | the record kinds the declaration names (#2680) |
31
31
  * | WSP116 | the decision points file of each declared answer kind (ws-058) |
32
+ * | WSP117 | a done work item's acceptance criteria all met (#2772) |
32
33
  * | WSP121, WSP122 | boxes: no literal credential, every capability brokered (#2726) |
33
34
  * | WSP123, WSP124 | box isolation: no shared port, state path or cookie, no literal machine path (#2727) |
35
+ * | WSP125 | boxes: a fountain Box declares the callback token fountain gives its sandbox (#2780) |
36
+ * | WSP126, WSP127 | box intent: the decision record a box names exists, and constrains a member or path of this workspace (#2850, #2857) |
37
+ * | WSP131 to WSP133 | diagram artifacts: source and render exist, a recorded source hash still matches (#2764) |
34
38
  */
35
39
 
36
40
  import { realpathSync } from "node:fs";
@@ -49,7 +53,9 @@ import { gatherLedgerFacts, LEDGER_CHECKS, type MemberLedgerFacts } from "./chec
49
53
  import { gatherPipelineFacts, PIPELINE_CHECKS, type MemberPipelineFacts } from "./checks/pipelines";
50
54
  import { RECORD_CHECKS, type RecordFacts } from "./checks/records";
51
55
  import { BOX_CHECKS } from "./checks/boxes";
56
+ import { DIAGRAM_CHECKS } from "./checks/diagrams";
52
57
  import { loadDeclaredKinds, type DeclaredKind } from "./declared-kinds";
58
+ import { resolveBoxIntents, type ResolvedBoxIntent } from "./box-intent";
53
59
 
54
60
  /**
55
61
  * What the checks beyond the declaration read, gathered from the checkout
@@ -67,6 +73,8 @@ export interface WorkspaceFacts {
67
73
  records?: RecordFacts;
68
74
  /** The record kinds the declaration names, each loaded, or only looked for under `--at` (#2680). */
69
75
  declaredKinds?: readonly DeclaredKind[];
76
+ /** The decision record each box's intent names, read from the working tree (#2850). */
77
+ boxIntents?: readonly ResolvedBoxIntent[];
70
78
  }
71
79
 
72
80
  /** What every declaration check reads. */
@@ -319,6 +327,8 @@ export const WORKSPACE_CHECKS: readonly WorkspaceCheck[] = [
319
327
  ...RECORD_CHECKS,
320
328
  // Boxes and their brokered capabilities (#2726).
321
329
  ...BOX_CHECKS,
330
+ // Diagram artifacts (#2764).
331
+ ...DIAGRAM_CHECKS,
322
332
  ];
323
333
 
324
334
  const BY_ID = new Map(WORKSPACE_CHECKS.map((c) => [c.id, c]));
@@ -461,8 +471,16 @@ export async function runDeclarationChecks(
461
471
  const { registry, problems } = loadKindRegistry(declaration.pins, root);
462
472
  const gathered = options.gather === false || options.tree ? {} : await gatherWorkspaceFacts(root, declaration, options);
463
473
  // The declared record kinds load from the working tree; under --at only whether each exists at the revision is checked (#2680).
464
- const declaredKinds = options.gather === false ? undefined : await loadDeclaredKinds(declaration, tree, root, { load: !options.tree });
465
- const facts: WorkspaceFacts = { ...gathered, ...(options.records ? { records: options.records } : {}), ...(declaredKinds ? { declaredKinds } : {}) };
474
+ // A declared work kind's acceptance criteria are counted in the working tree too (#2772).
475
+ const declaredKinds = options.gather === false ? undefined : await loadDeclaredKinds(declaration, tree, root, { load: !options.tree, acceptance: !options.tree });
476
+ // A box's intent is read from the working tree's records, so not under --at (#2850).
477
+ const boxIntents = declaredKinds && !options.tree ? await resolveBoxIntents(declaration, root, declaredKinds) : undefined;
478
+ const facts: WorkspaceFacts = {
479
+ ...gathered,
480
+ ...(options.records ? { records: options.records } : {}),
481
+ ...(declaredKinds ? { declaredKinds } : {}),
482
+ ...(boxIntents ? { boxIntents } : {}),
483
+ };
466
484
  const ctx: WorkspaceCheckContext = { declaration, tree, groups, kinds: registry, kindProblems: problems, facts };
467
485
  const findings = runWorkspaceChecks(ctx);
468
486
  const { active, suppressed } = applyCheckSettings(declaration, findings);
@@ -113,6 +113,8 @@ export interface GraphRecord {
113
113
  state: string | null;
114
114
  valid: boolean;
115
115
  supersededBy: string | null;
116
+ /** The ids of records whose remediates link names this one (#2774). */
117
+ remediatedBy: string[];
116
118
  }
117
119
 
118
120
  /** Read one member's `chant graph --format ir` output, upgrading an unversioned (v1) IR in place. */
@@ -17,6 +17,9 @@ export const recordKind = {
17
17
  // Sealed once reached (#2555).
18
18
  closedStates: ["ratified", "superseded"],
19
19
  supersedes: { field: "supersedes", key: "decision" },
20
+ // A remediates link names a closed decision this one fixes the consequence
21
+ // of, without replacing it: the target's state never changes (#2774).
22
+ remediates: { field: "remediates", key: "decision" },
20
23
  // A supersedes link takes effect under an equal or stricter approval rule
21
24
  // (#2524 D4): from a record ranked above 0 and at least as high as the one it
22
25
  // names. A decided record supersedes a decided or proposed one, a ratified
@@ -26,9 +29,16 @@ export const recordKind = {
26
29
  pins: { field: "evidence" },
27
30
  // member:<name> and path:<path> entries are the record's links in workspace graph (#2549).
28
31
  constrains: { field: "constrains" },
32
+ // Workspace paths a change carrying the decision out must not touch
33
+ // (#2773): chant workspace check --changes reports change-out-of-scope.
34
+ outOfScope: { field: "out_of_scope" },
29
35
  // Verdicts, and the field naming the decider, for each record's digest and
30
36
  // quorum (#2671, #2672).
31
37
  reviews: { field: "reviews", decider: "decided_by" },
38
+ // records new --by and the MCP records-new tool's by name a proposal's
39
+ // proposer here, apart from decided_by, which stays null until the
40
+ // decision is decided (#2756).
41
+ proposedBy: { field: "proposed_by" },
32
42
  // source is where a decision came from, and says where its proposal came
33
43
  // from too: via, client, harness, model, session, turns and a transcript
34
44
  // pinned by hash (#2708).
@@ -116,6 +116,19 @@
116
116
  ]
117
117
  }
118
118
  },
119
+ "remediates": {
120
+ "description": "Closed decisions this one fixes the consequence of, without replacing them: each names the decision by id and may say why (#2774). The named decision's status never changes because of a remediates link; a decision still open is amended instead, never remediated. Optional: most decisions remediate nothing.",
121
+ "type": "array",
122
+ "items": {
123
+ "type": "object",
124
+ "additionalProperties": false,
125
+ "required": ["decision"],
126
+ "properties": {
127
+ "decision": { "$ref": "#/definitions/decisionId" },
128
+ "reason": { "type": ["string", "null"] }
129
+ }
130
+ }
131
+ },
119
132
  "evidence": {
120
133
  "description": "The research, audits, design sections and workspace files behind the decision. A public link carries a url; a file in the workspace carries a path and the hash of its bytes (#2549). May be empty when the decision cites nothing; chant workspace records then warns decision-no-evidence (#2654).",
121
134
  "type": "array",
@@ -127,6 +140,10 @@
127
140
  "description": "Who chose. A forge login. Null in state proposed or withdrawn.",
128
141
  "type": ["string", "null"]
129
142
  },
143
+ "proposed_by": {
144
+ "description": "Who proposed this decision: the name chant workspace records new --by or the MCP records-new tool's by wrote, for a decision that opened proposed (#2756). Null or absent when nobody named a proposer, or the decision opened straight into a later state. Never cleared by a later decide, ratify or supersede.",
145
+ "type": ["string", "null"]
146
+ },
130
147
  "decided_on": {
131
148
  "description": "When the choice was made, as YYYY-MM-DD. Quoted, so YAML reads it as a string.",
132
149
  "type": ["string", "null"],
@@ -218,6 +235,15 @@
218
235
  "type": "string",
219
236
  "pattern": "^([A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+#[0-9]+|[a-z][a-z0-9]{0,15}-[0-9]{3,}|member:[a-z0-9][a-z0-9-]{0,39}|path:(?!/)(?!(?:[^/]*/)*\\.{1,2}(?:/|$))(?!.*//)[^\\\\\\u0000-\\u001f]*[^/\\\\\\u0000-\\u001f])$"
220
237
  }
238
+ },
239
+ "out_of_scope": {
240
+ "description": "Workspace paths this decision puts out of scope (#2773): files or directories, from the workspace root, that a change carrying out the decision must not touch. chant workspace check --changes reports a change to one as change-out-of-scope while the decision is in hand, and the decision does not cover it even where its constrains would. Optional.",
241
+ "type": "array",
242
+ "items": {
243
+ "type": "string",
244
+ "pattern": "^(?!/)(?!(?:[^/]*/)*\\.{1,2}(?:/|$))(?!.*//)[^\\\\\\u0000-\\u001f]*[^/\\\\\\u0000-\\u001f]$"
245
+ },
246
+ "uniqueItems": true
221
247
  }
222
248
  },
223
249
  "allOf": [
@@ -346,6 +346,9 @@ export function mcpToolCall(argv: readonly string[]): { name: string; arguments:
346
346
  else if (a === "--since") args.since = value();
347
347
  else if (a === "--compare-to") args.compareTo = value();
348
348
  else if (a === "--intent") args.intent = value();
349
+ else if (a === "--changes") args.range = value();
350
+ else if (a === "--work") args.work = value();
351
+ else if (a === "--severity") args.severity = value();
349
352
  else if (a === "--current") args.current = true;
350
353
  else if (a === "--composites") args.composites = true;
351
354
  else if (a.startsWith("-")) return undefined;
@@ -358,6 +361,8 @@ export function mcpToolCall(argv: readonly string[]): { name: string; arguments:
358
361
  return positional.length === 1 && kinds.length === 0 ? { name: "workspace-status", arguments: { ...args, env: positional[0] } } : undefined;
359
362
  case "records":
360
363
  return positional.length === 0 && kinds.length <= 1 ? { name: "workspace-records", arguments: { ...args, ...(kinds.length ? { kind: kinds[0] } : {}) } } : undefined;
364
+ case "check":
365
+ return positional.length === 0 && args.range !== undefined ? { name: "workspace-changes", arguments: { ...args, ...(kinds.length ? { kind: kinds } : {}) } } : undefined;
361
366
  case "graph":
362
367
  return positional.length === 0 ? { name: "workspace-graph", arguments: { ...args, ...(kinds.length ? { kind: kinds } : {}) } } : undefined;
363
368
  default:
@@ -134,7 +134,7 @@ describe("points ask (#2739)", () => {
134
134
  expect(q).toMatchObject({ state: "escalated", open: true, current: true, model: { answer: "medium", confidence: 0.175, threshold: 0.8, model: "bosun-v3.1-1.7b", observed: false } });
135
135
  expect(q.escalations.map((e) => e.reason)).toEqual(["no row matches these inputs", "not observed: confidence 0.175 is below the threshold 0.8"]);
136
136
  expect(listed.questions.every((x) => x.open)).toBe(true);
137
- expect(listed.points.map((p) => p.name)).toEqual(["slice-tier", "ship-skip", "needs-decision"]);
137
+ expect(listed.points.map((p) => p.name)).toEqual(["slice-tier", "ship-skip", "finding-triage", "needs-a-decision", "needs-decision"]);
138
138
  expect(listed.points[0].inputs[0]).toEqual({ name: "work-item.criteria", output: "work-item", description: "acceptance criteria in the work item" });
139
139
 
140
140
  // A later ask still escalating keeps the standing record; one a model answers rewrites it, as proposed.