@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
@@ -34,9 +34,9 @@
34
34
  */
35
35
 
36
36
  import { execFileSync, spawn } from "node:child_process";
37
- import { chmodSync, existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
37
+ import { chmodSync, existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
38
38
  import { tmpdir } from "node:os";
39
- import { basename, join, relative, resolve, sep } from "node:path";
39
+ import { basename, dirname, join, relative, resolve, sep } from "node:path";
40
40
  import { computePlanDigest } from "../lifecycle/plan-digest";
41
41
  import type { ResolvedGateApproval } from "../op/gate-approval";
42
42
  import { checkLineage, findingKey, type CheckFinding } from "./lineage-check";
@@ -56,10 +56,13 @@ import {
56
56
  type ManualStep,
57
57
  } from "./lineage-lock";
58
58
  import { mergeFile } from "./lineage-merge";
59
+ import { reportPlan, runChantMigrations, type ChantMigrationReport } from "./chant-migrations";
60
+ import { readDeclaration, readerVersion, WorkspaceReadError } from "./declaration";
59
61
  import { assertCodeAllowed, planMigrations, runMigration, splitMigrations, type LoadedMigration } from "./lineage-migrations";
60
62
  import { applyUpstream, type UpdateResult } from "./lineage-update";
61
63
  import { carryParameters, readManifest, substituteParameters } from "./template-manifest";
62
64
  import { repinSubstituted, type RepinnedRecord } from "./template-pins";
65
+ import { workingTree } from "./tree";
63
66
 
64
67
  /** The kind the patch digest is taken under, so it never collides with another kind of plan. */
65
68
  export const UPGRADE_PLAN_KIND = "workspace-upgrade";
@@ -74,10 +77,12 @@ export class UpgradeError extends LockError {
74
77
  // ── Checks ───────────────────────────────────────────────────────────────────
75
78
 
76
79
  export interface UpgradeCheck {
77
- name: "build" | "lint" | "workspace check";
80
+ name: "build" | "lint" | "workspace check" | "migration";
78
81
  status: "passed" | "failed" | "skipped";
79
82
  /** Why a check was skipped, or the tail of a failed command's output. */
80
83
  detail?: string;
84
+ /** The chant member `build` or `lint` ran in ("." for the scope root), when it is not the only one. */
85
+ member?: string;
81
86
  }
82
87
 
83
88
  /** Runs `chant build` or `chant lint` in a directory. */
@@ -141,6 +146,8 @@ export interface StagedUpgrade {
141
146
  to: string | null;
142
147
  commit?: { from?: string; to: string };
143
148
  migrations: string[];
149
+ /** The migrations chant ships (./chant-migrations.ts) that found something to move, applied or not. */
150
+ chantMigrations: ChantMigrationReport[];
144
151
  written: string[];
145
152
  merged: string[];
146
153
  removed: string[];
@@ -460,6 +467,25 @@ function tail(text: string, lines = 30): string {
460
467
  return text.trimEnd().split("\n").slice(-lines).join("\n");
461
468
  }
462
469
 
470
+ /**
471
+ * The `chant` members `chant.workspace.json` declares in `dir` (#2804): a
472
+ * template built as a workspace, such as the studio kit's, keeps its chant
473
+ * project in a member (`delivery`), not at the scope's root, and the root
474
+ * itself never has a `chant.config`. Empty when `dir` declares no workspace,
475
+ * or the declaration cannot be read: the caller then falls back to `dir`
476
+ * itself.
477
+ */
478
+ function chantMemberDirs(dir: string): string[] {
479
+ try {
480
+ return readDeclaration(workingTree(dir))
481
+ .members.filter((m) => m.kind === "chant")
482
+ .map((m) => m.dir);
483
+ } catch (err) {
484
+ if (err instanceof WorkspaceReadError) return [];
485
+ throw err;
486
+ }
487
+ }
488
+
463
489
  /**
464
490
  * Stage an upgrade: steps 1 to 5 and the digest of step 6. Nothing in the
465
491
  * project's tree changes. The caller decides the gate, then applies the patch
@@ -568,6 +594,12 @@ export async function stageUpgrade(options: UpgradeOptions): Promise<StagedUpgra
568
594
  rmSync(scratch, { recursive: true, force: true });
569
595
  }
570
596
 
597
+ // chant's own migrations (#2737): planned from what the scope holds, and
598
+ // applied only when the plan has no conflict. A conflict fails the checks.
599
+ // Each is planned from the tree the ones before it left.
600
+ const chantRuns = lineage.kind === "template" ? runChantMigrations({ dir: scopeDir, lineage: staged, chantVersion: readerVersion() }) : [];
601
+ const chantMigrations: ChantMigrationReport[] = chantRuns.map((r) => reportPlan(r.plan, r.applied));
602
+
571
603
  // 4. Merge per file.
572
604
  const result = applyUpstream(scopeDir, staged, upstream.files, { base, merge: mergeFile });
573
605
  for (const path of result.written) if (upstream.executable.has(path)) chmodSync(join(scopeDir, path), 0o755);
@@ -607,12 +639,40 @@ export async function stageUpgrade(options: UpgradeOptions): Promise<StagedUpgra
607
639
 
608
640
  // 5. Checks.
609
641
  const checks: UpgradeCheck[] = [];
642
+ for (const m of chantMigrations.filter((m) => !m.applied)) {
643
+ checks.push({
644
+ name: "migration",
645
+ status: "failed",
646
+ detail: `${m.id} was not applied: ${m.conflicts.map((c) => `${c.path}: ${c.reason}`).join("\n")}`,
647
+ });
648
+ }
610
649
  if (changed) {
611
- if (isChantProject(worktreeProject)) {
650
+ // Every `chant` member chant.workspace.json declares, so a template
651
+ // whose chant project sits in a member (the studio kit's `delivery/`)
652
+ // is built and linted there, not skipped for having no chant.config at
653
+ // the scope's root (#2804). A scope with no workspace declaration falls
654
+ // back to the root, as before.
655
+ const members = chantMemberDirs(worktreeProject);
656
+ const targets = members.length > 0 ? members : isChantProject(worktreeProject) ? ["."] : [];
657
+ if (targets.length > 0) {
612
658
  const run = options.runChant ?? spawnChant;
613
- for (const command of ["build", "lint"] as const) {
614
- const r = await run(command, worktreeProject);
615
- checks.push(r.exitCode === 0 ? { name: command, status: "passed" } : { name: command, status: "failed", detail: tail(r.output) });
659
+ const namedMember = targets.length > 1 || targets[0] !== ".";
660
+ // A member that installs its packages in its own directory resolves them from its node_modules, which is not
661
+ // an ancestor of the staging worktree: link it in from the project (#2847). The patch and its digest are
662
+ // computed above, so the links never reach them, and dispose() removes them with the worktree.
663
+ for (const member of targets) {
664
+ if (member === ".") continue;
665
+ const from = join(root, member, "node_modules");
666
+ const to = join(worktreeProject, member, "node_modules");
667
+ if (existsSync(from) && !existsSync(to) && existsSync(dirname(to))) symlinkSync(from, to, "dir");
668
+ }
669
+ for (const member of targets) {
670
+ const dir = member === "." ? worktreeProject : join(worktreeProject, member);
671
+ const at = namedMember ? { member } : {};
672
+ for (const command of ["build", "lint"] as const) {
673
+ const r = await run(command, dir);
674
+ checks.push(r.exitCode === 0 ? { name: command, status: "passed", ...at } : { name: command, status: "failed", detail: tail(r.output), ...at });
675
+ }
616
676
  }
617
677
  } else {
618
678
  for (const name of ["build", "lint"] as const) checks.push({ name, status: "skipped", detail: "not a chant project (no chant.config)" });
@@ -627,7 +687,8 @@ export async function stageUpgrade(options: UpgradeOptions): Promise<StagedUpgra
627
687
  from: fromDir ? sourcePin(lineage.source) : lineage.ref ?? null,
628
688
  to: fromDir ? sourcePin(staged.source) : staged.ref ?? null,
629
689
  ...(upstream.commit ? { commit: { ...(lineage.address?.commit ? { from: lineage.address.commit } : {}), to: upstream.commit } } : {}),
630
- migrations: plan.chain.map((m) => m.migration.id),
690
+ migrations: [...plan.chain.map((m) => m.migration.id), ...chantMigrations.filter((m) => m.applied).map((m) => m.id)],
691
+ chantMigrations,
631
692
  written: result.written,
632
693
  merged: result.merged,
633
694
  removed: result.removed,
@@ -721,7 +782,14 @@ export function describeStaged(staged: StagedUpgrade): string[] {
721
782
  const lines: string[] = [];
722
783
  const at = (p: string) => (staged.scope === "." ? p : `${staged.scope}/${p}`);
723
784
  lines.push(`${staged.scope} ${staged.template} ${staged.from ?? "(no ref)"} -> ${staged.to ?? "(no ref)"}`);
724
- for (const id of staged.migrations) lines.push(` migration: ${id}`);
785
+ const chantIds = new Set(staged.chantMigrations.map((m) => m.id));
786
+ for (const id of staged.migrations) if (!chantIds.has(id)) lines.push(` migration: ${id}`);
787
+ for (const m of staged.chantMigrations) {
788
+ lines.push(` chant migration: ${m.id} (${m.applied ? "applied" : "not applied: it has conflicts"}), to ${m.description}`);
789
+ for (const c of m.changes) lines.push(` ${c.action === "delete" ? "delete" : "write"}: ${at(c.path)} (${c.why}${c.edited ? "; it was edited since the template, and the edit stays in git history" : ""})`);
790
+ for (const n of m.notMoved) lines.push(` not moved: ${n.what} -> ${n.where}`);
791
+ for (const c of m.conflicts) lines.push(` conflict: ${at(c.path)}: ${c.reason}`);
792
+ }
725
793
  for (const p of staged.written) lines.push(` updated: ${at(p)}`);
726
794
  for (const p of staged.merged) lines.push(` merged: ${at(p)}`);
727
795
  for (const p of staged.removed) lines.push(` removed: ${at(p)}`);
@@ -735,7 +803,10 @@ export function describeStaged(staged: StagedUpgrade): string[] {
735
803
  ` governance: ${staged.governance.paths.join(", ")}; needs ${q.count} human approval(s)${q.roles ? ` with a role in ${q.roles.join(", ")}` : ""}, from the rules at ${staged.governance.rules.slice(0, 12)}`,
736
804
  );
737
805
  }
738
- for (const c of staged.checks) lines.push(` ${c.name}: ${c.status}${c.status === "skipped" && c.detail ? ` (${c.detail})` : ""}`);
806
+ for (const c of staged.checks) {
807
+ const label = c.member !== undefined ? `${c.name} (${c.member})` : c.name;
808
+ lines.push(` ${label}: ${c.status}${c.status === "skipped" && c.detail ? ` (${c.detail})` : ""}`);
809
+ }
739
810
  lines.push(` patch: ${staged.changedPaths.length} file(s)${staged.lock.tracked ? "" : ` and the untracked ${LOCK_FILE}`}, ${staged.digest}`);
740
811
  return lines;
741
812
  }
@@ -135,6 +135,63 @@ describe("chant workspace ls on built workspaces", () => {
135
135
  expect(doc.summary).toEqual({ members: 5, unreadable: 3, groups: 1, matches: 0 });
136
136
  });
137
137
 
138
+ test("lists declared diagrams flattened across the workspace and its members (#2764)", () => {
139
+ const root = repo({
140
+ "chant.workspace.json": JSON.stringify({
141
+ name: "acme",
142
+ schema: 1,
143
+ members: [
144
+ {
145
+ name: "docs",
146
+ dir: "docs",
147
+ kind: "other",
148
+ because: "the docs site",
149
+ diagrams: [
150
+ {
151
+ name: "architecture",
152
+ title: "Studio architecture",
153
+ source: "docs/diagrams/architecture.d2",
154
+ render: "docs/diagrams/architecture.svg",
155
+ renderer: { tool: "d2", version: "0.9.0", args: ["--layout=elk", "--theme=0", "--pad=40", "--omit-version"] },
156
+ sourceHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
157
+ },
158
+ ],
159
+ },
160
+ ],
161
+ diagrams: [
162
+ {
163
+ name: "boundary",
164
+ title: "chant and hud",
165
+ source: null,
166
+ render: "docs/diagrams/boundary.svg",
167
+ renderer: { tool: "graphviz", version: "9.0.0", args: [] },
168
+ },
169
+ ],
170
+ }),
171
+ "docs/README.md": "",
172
+ });
173
+ const doc = result(listWorkspace({ cwd: root }));
174
+ expectValid(doc);
175
+ expect(doc.diagrams).toEqual([
176
+ {
177
+ name: "boundary",
178
+ title: "chant and hud",
179
+ source: null,
180
+ render: "docs/diagrams/boundary.svg",
181
+ renderer: { tool: "graphviz", version: "9.0.0", args: [] },
182
+ member: null,
183
+ },
184
+ {
185
+ name: "architecture",
186
+ title: "Studio architecture",
187
+ source: "docs/diagrams/architecture.d2",
188
+ render: "docs/diagrams/architecture.svg",
189
+ renderer: { tool: "d2", version: "0.9.0", args: ["--layout=elk", "--theme=0", "--pad=40", "--omit-version"] },
190
+ member: "docs",
191
+ },
192
+ ]);
193
+ });
194
+
138
195
  test("a nested workspace member is readable by its own declaration, and ls inside it lists the inner one", () => {
139
196
  const root = repo({
140
197
  "chant.workspace.json": declaration([{ name: "kit", dir: "vendor/kit", kind: "workspace" }]),
@@ -8,7 +8,7 @@
8
8
  "result": {
9
9
  "description": "The declaration was read. Exit code 0, even when some members can't be read.",
10
10
  "type": "object",
11
- "required": ["$schema", "contract", "chant", "at", "workspace", "members", "groups", "summary"],
11
+ "required": ["$schema", "contract", "chant", "at", "workspace", "members", "groups", "diagrams", "summary"],
12
12
  "properties": {
13
13
  "$schema": { "const": "https://intentius.io/chant/schemas/workspace/ls/v1/ls.schema.json" },
14
14
  "contract": { "const": 1 },
@@ -61,6 +61,11 @@
61
61
  "type": "array",
62
62
  "items": { "$ref": "#/$defs/group" }
63
63
  },
64
+ "diagrams": {
65
+ "description": "Added in contract 1 by #2764. Every declared diagram artifact, flattened across the workspace and its members: the workspace's own first, then each member's, in file order.",
66
+ "type": "array",
67
+ "items": { "$ref": "#/$defs/diagram" }
68
+ },
64
69
  "summary": {
65
70
  "type": "object",
66
71
  "required": ["members", "unreadable", "groups", "matches"],
@@ -138,9 +143,49 @@
138
143
  }
139
144
  }
140
145
  ]
146
+ },
147
+ "acceptance": {
148
+ "description": "Added in contract 1 by #2772. For a work kind whose work block names acceptance criteria: each current work item that lists some, in path order, with how many of its criteria passing evidence meets, read in the tree read. Null for any other kind, and when the kind or its records can't be read.",
149
+ "oneOf": [
150
+ { "type": "null" },
151
+ {
152
+ "type": "array",
153
+ "items": {
154
+ "type": "object",
155
+ "required": ["item", "state", "met", "total"],
156
+ "properties": {
157
+ "item": { "type": "string", "description": "The work item's id." },
158
+ "state": { "type": ["string", "null"], "description": "The item's state." },
159
+ "met": { "type": "integer", "minimum": 0, "description": "Criteria met by evidence naming them, with result pass and the criterion's verification; a manual verdict counts only from someone other than the implementer." },
160
+ "total": { "type": "integer", "minimum": 0, "description": "Criteria the item lists." }
161
+ }
162
+ }
163
+ }
164
+ ]
141
165
  }
142
166
  }
143
167
  },
168
+ "diagram": {
169
+ "description": "A diagram artifact the declaration names (#2764), pinned to the renderer its render was made with. chant runs no renderer; everything here comes straight from the declaration.",
170
+ "type": "object",
171
+ "required": ["name", "title", "source", "render", "renderer", "member"],
172
+ "properties": {
173
+ "name": { "type": "string" },
174
+ "title": { "type": "string" },
175
+ "source": { "type": ["string", "null"], "description": "From the workspace root, with / separators. Null for an SVG with no source." },
176
+ "render": { "type": "string", "description": "From the workspace root, with / separators." },
177
+ "renderer": {
178
+ "type": "object",
179
+ "required": ["tool", "version", "args"],
180
+ "properties": {
181
+ "tool": { "enum": ["d2", "mermaid", "graphviz"] },
182
+ "version": { "type": "string", "description": "The exact release the render was made with, such as 0.9.0." },
183
+ "args": { "type": "array", "items": { "type": "string" }, "description": "Passed before the input and output paths, in order." }
184
+ }
185
+ },
186
+ "member": { "type": ["string", "null"], "description": "The member that declares it, or null for the workspace's own." }
187
+ }
188
+ },
144
189
  "group": {
145
190
  "type": "object",
146
191
  "required": ["name", "kind", "glob", "matches", "skipped", "reason"],
@@ -13,6 +13,11 @@
13
13
  * and each member's, with each kind file's own name when it loads (#2680).
14
14
  * Loading one imports it, so {@link listWorkspace} lists them unloaded and
15
15
  * {@link listWorkspaceWithKinds}, which the command runs, loads them.
16
+ *
17
+ * The declared diagram artifacts are listed too, flattened across the
18
+ * workspace and its members into one `diagrams` array (#2764). Unlike record
19
+ * kinds, nothing about a diagram is loaded: everything printed comes
20
+ * straight from the declaration, and chant runs no renderer to list it.
16
21
  */
17
22
 
18
23
  import { existsSync } from "node:fs";
@@ -20,11 +25,13 @@ import { resolve } from "node:path";
20
25
  import { formatError } from "../cli/format";
21
26
  import type { CommandContext } from "../cli/registry";
22
27
  import {
28
+ declaredDiagrams,
23
29
  readDeclaration,
24
30
  readerVersion,
25
31
  resolveGroups,
26
32
  WorkspaceReadError,
27
33
  type Declaration,
34
+ type DiagramDeclaration,
28
35
  type ErrorLocation,
29
36
  type Member,
30
37
  type WorkspaceErrorCode,
@@ -74,6 +81,20 @@ export interface LsRecordKind {
74
81
  kind: string | null;
75
82
  /** Why the kind file can't be loaded, or null. */
76
83
  reason: { code: DeclaredKindReasonCode; message: string } | null;
84
+ /**
85
+ * For a work kind with acceptance criteria (#2772): each current work item
86
+ * that lists criteria, with how many passing evidence meets. Null for any
87
+ * other kind, and when the kind or its records can't be read.
88
+ */
89
+ acceptance: LsAcceptance[] | null;
90
+ }
91
+
92
+ /** A work item's acceptance criteria, counted (#2772). */
93
+ export interface LsAcceptance {
94
+ item: string;
95
+ state: string | null;
96
+ met: number;
97
+ total: number;
77
98
  }
78
99
 
79
100
  export interface LsMember {
@@ -89,6 +110,25 @@ export interface LsMember {
89
110
  records: LsRecordKind[];
90
111
  }
91
112
 
113
+ /**
114
+ * A diagram artifact the declaration names (#2764), flattened across the
115
+ * workspace and its members: hud reads one list, the way it already reads
116
+ * `records --json`'s flat `assets[]`. `sourceHash`, the declaration's own
117
+ * bookkeeping for `chant workspace check`'s drift finding, is not printed
118
+ * here; a reader that draws diagrams has no use for it.
119
+ */
120
+ export interface LsDiagram {
121
+ name: string;
122
+ title: string;
123
+ /** From the workspace root, with / separators. Null for an SVG with no source. */
124
+ source: string | null;
125
+ /** From the workspace root, with / separators. */
126
+ render: string;
127
+ renderer: DiagramDeclaration["renderer"];
128
+ /** The member that declares it, or null for the workspace's own. */
129
+ member: string | null;
130
+ }
131
+
92
132
  export interface LsGroup {
93
133
  name: string;
94
134
  kind: "examples";
@@ -117,6 +157,8 @@ export type LsDocument =
117
157
  };
118
158
  members: LsMember[];
119
159
  groups: LsGroup[];
160
+ /** Every declared diagram, the workspace's own first, then each member's, in file order (#2764). */
161
+ diagrams: LsDiagram[];
120
162
  summary: { members: number; unreadable: number; groups: number; matches: number };
121
163
  }
122
164
  | {
@@ -162,11 +204,12 @@ export async function listWorkspaceWithKinds(query: LsQuery): Promise<LsDocument
162
204
  const { doc, declaration, tree, rootOnDisk } = readListing(query);
163
205
  if (!declaration || "error" in doc) return doc;
164
206
  const { loadDeclaredKinds } = await import("./declared-kinds");
165
- const loaded = await loadDeclaredKinds(declaration, tree!, rootOnDisk!);
207
+ const loaded = await loadDeclaredKinds(declaration, tree!, rootOnDisk!, { acceptance: true, ...(doc.at !== null ? { at: doc.at } : {}) });
166
208
  const byPath = new Map(loaded.map((k) => [k.declared.path, k]));
167
209
  const fill = (r: LsRecordKind): LsRecordKind => {
168
210
  const k = byPath.get(r.path)!;
169
- return { ...r, name: r.name ?? k.kind, kind: k.kind, reason: k.reason };
211
+ const acceptance = k.acceptance ? k.acceptance.map((a) => ({ item: a.item, state: a.state, met: a.met, total: a.total })) : null;
212
+ return { ...r, name: r.name ?? k.kind, kind: k.kind, reason: k.reason, acceptance };
170
213
  };
171
214
  return {
172
215
  ...doc,
@@ -175,7 +218,7 @@ export async function listWorkspaceWithKinds(query: LsQuery): Promise<LsDocument
175
218
  };
176
219
  }
177
220
 
178
- const unloaded = (records: Declaration["records"]): LsRecordKind[] => records.map((r) => ({ name: r.name, path: r.path, kind: null, reason: null }));
221
+ const unloaded = (records: Declaration["records"]): LsRecordKind[] => records.map((r) => ({ name: r.name, path: r.path, kind: null, reason: null, acceptance: null }));
179
222
 
180
223
  function readListing(query: LsQuery): { doc: LsDocument; declaration?: Declaration; tree?: WorkspaceTree; rootOnDisk?: string } {
181
224
  const chant = readerVersion();
@@ -213,6 +256,14 @@ function readListing(query: LsQuery): { doc: LsDocument; declaration?: Declarati
213
256
  ? { code: "no-matches", message: `no directory ${g.group.globs.join(" or ")} matches${tree.label} holds a chant project` }
214
257
  : null,
215
258
  }));
259
+ const diagrams: LsDiagram[] = declaredDiagrams(declaration).map((d) => ({
260
+ name: d.name,
261
+ title: d.title,
262
+ source: d.source,
263
+ render: d.render,
264
+ renderer: d.renderer,
265
+ member: d.member,
266
+ }));
216
267
  const doc: LsDocument = {
217
268
  ...head,
218
269
  at,
@@ -227,6 +278,7 @@ function readListing(query: LsQuery): { doc: LsDocument; declaration?: Declarati
227
278
  },
228
279
  members,
229
280
  groups: lsGroups,
281
+ diagrams,
230
282
  summary: {
231
283
  members: members.length,
232
284
  unreadable: members.filter((m) => !m.readable).length,
@@ -296,8 +348,15 @@ function formatLs(doc: Extract<LsDocument, { members: unknown }>): string {
296
348
  kinds.forEach(({ r }, i) => {
297
349
  lines.push(rendered[i + 1]);
298
350
  if (r.reason) lines.push(` ${r.reason.code}: ${r.reason.message}`);
351
+ for (const a of r.acceptance ?? []) lines.push(` ${a.item} ${a.state ?? "-"}: ${a.met}/${a.total} criteria met`);
299
352
  });
300
353
  }
354
+ if (doc.diagrams.length > 0) {
355
+ lines.push("");
356
+ const rows = [["DIAGRAM", "MEMBER", "SOURCE", "RENDER", "RENDERER"]];
357
+ for (const d of doc.diagrams) rows.push([d.name, d.member ?? "(workspace)", d.source ?? "-", d.render, `${d.renderer.tool} ${d.renderer.version}`]);
358
+ lines.push(...table(rows));
359
+ }
301
360
  if (doc.groups.length > 0) {
302
361
  lines.push("");
303
362
  const rows = [["GROUP", "KIND", "GLOB", "PROJECTS"]];
@@ -15,7 +15,7 @@
15
15
  */
16
16
 
17
17
  import { readFileSync } from "node:fs";
18
- import { resolve } from "node:path";
18
+ import { relative, resolve, sep } from "node:path";
19
19
  import { formatError } from "../cli/format";
20
20
  import type { CommandContext } from "../cli/registry";
21
21
  import { declaredRecordKinds, readDeclaration, readerVersion, WorkspaceReadError, type ErrorLocation, type WorkspaceErrorCode } from "./declaration";
@@ -24,6 +24,7 @@ import { answerPoint, askPoint, questionView, POINTS_WRITE_CONTRACT_VERSION, POI
24
24
  import { candidates, inputOutput, pointsFileOf, pointVersion, quorumOf, readPointsThrough, type Decider, type ModelAsk, type WireAnswer } from "./points";
25
25
  import type { ReasonCode } from "./reason-codes";
26
26
  import { gitRevisionSource, workingTreeSource } from "./record-source";
27
+ import { readLedgerAnswers, withLedgerAnswers, type LedgerAnswers } from "./answers-ledger";
27
28
  import { loadRecordKind, RecordReadError, type ReadErrorCode } from "./records";
28
29
  import { readRecordsFor } from "./records-cli";
29
30
  import { locateWorkspace } from "./which-chant";
@@ -70,6 +71,8 @@ export interface PointView {
70
71
  questionType: string;
71
72
  instructions: string;
72
73
  candidates: (string | boolean)[];
74
+ /** What each candidate means, as the points file declares it: an object of strings for noul and choice, an array for score. */
75
+ criteria: Record<string, string> | string[];
73
76
  /** Each input: its name, the read-contract output it names, and its description. */
74
77
  inputs: { name: string; output: string; description: string }[];
75
78
  deciders: Decider[];
@@ -136,7 +139,17 @@ export async function workspacePoints(query: PointsQuery): Promise<PointsDocumen
136
139
  if (k.given) sources.push({ kind: k.shown, points: null, reason: { code: "kind-invalid", message: `the ${loaded.kind.name} kind has no answers block, so it holds no answers to decision points` } });
137
140
  continue;
138
141
  }
139
- const read = await readRecordsFor({ kind: k.file, cwd: query.cwd, at: query.at });
142
+ // Without --at, the questions a steward keeps on the lifecycle ledger are read too (#2786).
143
+ let ledger = undefined as LedgerAnswers | undefined;
144
+ const read = await readRecordsFor({
145
+ kind: k.file,
146
+ cwd: query.cwd,
147
+ at: query.at,
148
+ overlay: async (base, { loaded: l, root }) => {
149
+ ledger = await readLedgerAnswers({ file: l.file, name: l.kind.name, dirRel: relative(root, l.dir).split(sep).join("/") || "." });
150
+ return withLedgerAnswers(base, ledger);
151
+ },
152
+ });
140
153
  const source = read.at !== null && read.top ? gitRevisionSource(read.top, read.at) : workingTreeSource(read.root);
141
154
  const file = pointsFileOf(read.loaded, read.root);
142
155
  const parsed = readPointsThrough(source, file);
@@ -152,13 +165,14 @@ export async function workspacePoints(query: PointsQuery): Promise<PointsDocumen
152
165
  questionType: p.question.type,
153
166
  instructions: p.question.instructions,
154
167
  candidates: candidates(p.question),
168
+ criteria: p.question.criteria,
155
169
  inputs: Object.entries(p.inputs).map(([n, description]) => ({ name: n, output: inputOutput(n), description })),
156
170
  deciders: p.deciders,
157
171
  quorum: quorumOf(p),
158
172
  });
159
173
  }
160
174
  for (const e of read.result.records) {
161
- const view = questionView(e, typeof e.data?.point === "string" ? declared[e.data.point] : undefined);
175
+ const view = questionView(e, typeof e.data?.point === "string" ? declared[e.data.point] : undefined, ledger?.byPath.get(e.path)?.ledger ?? null);
162
176
  if (view && (!query.open || view.open)) questions.push(view);
163
177
  }
164
178
  } catch (err) {
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://intentius.io/chant/schemas/workspace/points-write/v1/points-write.schema.json",
4
4
  "title": "chant workspace points ask and answer output",
5
- "description": "What `chant workspace points ask` and `points answer` print (ws-058, #2739): the answer record asked for or written, as `points --json` lists a question. ask returns the record that is there when the same point, declaration and inputs were answered or proposed before (reused), and otherwise writes one: answered from a table, proposed from a model at or above its threshold, escalated to people below it. answer records people's answer to an open question once the point's quorum is met. Nothing is committed, and nothing is written on a refusal or with --dry-run. The error codes are a closed list, each in the one closed list of `reason-codes.ts`. Written at contract version 1 of the write documents.",
5
+ "description": "What `chant workspace points ask` and `points answer` print (ws-058, #2739): the answer record asked for or written, as `points --json` lists a question. ask returns the record that is there when the same point, declaration and inputs were answered or proposed before (reused), and otherwise writes one: answered from a table, proposed from a model at or above its threshold, escalated to people below it. answer records people's answer to an open question once the point's quorum is met. Nothing is committed to the working branch, and nothing is written on a refusal or with --dry-run. In a steward's turn a new record goes on the lifecycle ledger instead of the working tree (#2786), as a commit on chant/lifecycle, and a record already held there is rewritten there. The error codes are a closed list, each in the one closed list of `reason-codes.ts`. Written at contract version 1 of the write documents.",
6
6
  "oneOf": [
7
7
  {
8
8
  "$ref": "#/$defs/result"
@@ -372,6 +372,13 @@
372
372
  "type": "string"
373
373
  }
374
374
  },
375
+ "ledger": {
376
+ "type": [
377
+ "string",
378
+ "null"
379
+ ],
380
+ "description": "Where the record is held when it is on the lifecycle ledger rather than in the working tree (#2786): chant/lifecycle:<path>, which git show reads. A question asked in a steward's turn is held there, since a steward never writes the checkout; people's answer to it is written there too. null for a record in the working tree. path is where the record reads as being either way."
381
+ },
375
382
  "askedOn": {
376
383
  "type": [
377
384
  "string",
@@ -465,6 +472,7 @@
465
472
  "point-decider-failed",
466
473
  "answer-not-candidate",
467
474
  "quorum-not-met",
475
+ "answer-in-steward-turn",
468
476
  "record-not-found",
469
477
  "record-closed",
470
478
  "record-id-taken",
@@ -473,6 +481,8 @@
473
481
  "record-id-duplicate",
474
482
  "record-supersedes-unknown",
475
483
  "record-supersedes-conflict",
484
+ "record-remediates-unknown",
485
+ "record-remediates-not-closed",
476
486
  "session-seal-mismatch",
477
487
  "session-verdict-unknown-record"
478
488
  ]
@@ -191,6 +191,7 @@
191
191
  "questionType",
192
192
  "instructions",
193
193
  "candidates",
194
+ "criteria",
194
195
  "inputs",
195
196
  "deciders",
196
197
  "quorum"
@@ -236,6 +237,23 @@
236
237
  ]
237
238
  }
238
239
  },
240
+ "criteria": {
241
+ "description": "What each candidate means, as the points file declares it: noul and choice give an object of strings keyed by candidate, score an array of ordered level descriptions.",
242
+ "oneOf": [
243
+ {
244
+ "type": "object",
245
+ "additionalProperties": {
246
+ "type": "string"
247
+ }
248
+ },
249
+ {
250
+ "type": "array",
251
+ "items": {
252
+ "type": "string"
253
+ }
254
+ }
255
+ ]
256
+ },
239
257
  "inputs": {
240
258
  "type": "array",
241
259
  "items": {
@@ -583,6 +601,39 @@
583
601
  "type": "string"
584
602
  }
585
603
  },
604
+ "askedBy": {
605
+ "description": "The steward whose turn asked the question (#2749), and the run it was in, or null. The steward waits on the question and never answers it: a person does.",
606
+ "oneOf": [
607
+ {
608
+ "type": "null"
609
+ },
610
+ {
611
+ "type": "object",
612
+ "required": [
613
+ "steward",
614
+ "run"
615
+ ],
616
+ "properties": {
617
+ "steward": {
618
+ "type": "string"
619
+ },
620
+ "run": {
621
+ "type": [
622
+ "string",
623
+ "null"
624
+ ]
625
+ }
626
+ }
627
+ }
628
+ ]
629
+ },
630
+ "ledger": {
631
+ "type": [
632
+ "string",
633
+ "null"
634
+ ],
635
+ "description": "Where the record is held when it is on the lifecycle ledger rather than in the working tree (#2786): chant/lifecycle:<path>, which git show reads. A question asked in a steward's turn is held there, since a steward never writes the checkout; people's answer to it is written there too. null for a record in the working tree. path is where the record reads as being either way."
636
+ },
586
637
  "askedOn": {
587
638
  "type": [
588
639
  "string",