@intentius/chant 0.124.46 → 0.124.49

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 (726) hide show
  1. package/dist/__golden__/wave-loop/op-by-apply-after-fails.json +10 -0
  2. package/dist/__golden__/wave-loop/op-by-apply-hold.json +10 -0
  3. package/dist/__golden__/wave-loop/op-by-apply-ignored.json +10 -0
  4. package/dist/__golden__/wave-loop/op-by-apply-pass.json +10 -0
  5. package/dist/__golden__/wave-loop/op-by-apply-skip.json +10 -0
  6. package/dist/__golden__/wave-loop/op-cost-member-clash.json +6 -0
  7. package/dist/__golden__/wave-loop/op-cost-member.json +13 -0
  8. package/dist/__golden__/wave-loop/op-denied-source.json +16 -0
  9. package/dist/__golden__/wave-loop/op-denied.json +16 -0
  10. package/dist/__golden__/wave-loop/op-discover-rest-short.json +6 -0
  11. package/dist/__golden__/wave-loop/op-discover-rest-stops.json +13 -0
  12. package/dist/__golden__/wave-loop/op-discover-rest.json +26 -0
  13. package/dist/__golden__/wave-loop/op-discover-wide.json +26 -0
  14. package/dist/__golden__/wave-loop/op-held-on-again.json +16 -0
  15. package/dist/__golden__/wave-loop/op-held-on-before-apply.json +16 -0
  16. package/dist/__golden__/wave-loop/op-held-on.json +16 -0
  17. package/dist/__golden__/wave-loop/op-hooks-apply-fails.json +13 -0
  18. package/dist/__golden__/wave-loop/op-hooks-fail-after-apply.json +13 -0
  19. package/dist/__golden__/wave-loop/op-hooks-fail-before-apply.json +13 -0
  20. package/dist/__golden__/wave-loop/op-hooks-fail-before-plan.json +13 -0
  21. package/dist/__golden__/wave-loop/op-hooks-held.json +13 -0
  22. package/dist/__golden__/wave-loop/op-hooks-hold-before-apply.json +13 -0
  23. package/dist/__golden__/wave-loop/op-hooks-pass.json +13 -0
  24. package/dist/__golden__/wave-loop/op-hooks-refused-at-base.json +13 -0
  25. package/dist/__golden__/wave-loop/op-hooks-share.json +33 -0
  26. package/dist/__golden__/wave-loop/op-outcome-held.json +10 -0
  27. package/dist/__golden__/wave-loop/op-outcome-replan-approved.json +10 -0
  28. package/dist/__golden__/wave-loop/op-outcome-replan-forever.json +10 -0
  29. package/dist/__golden__/wave-loop/op-outcome-replan.json +10 -0
  30. package/dist/__golden__/wave-loop/op-outcome-stood-down.json +10 -0
  31. package/dist/__golden__/wave-loop/op-outcome-unreadable.json +10 -0
  32. package/dist/__golden__/wave-loop/op-overridden.json +16 -0
  33. package/dist/__golden__/wave-loop/op-per-run.json +20 -0
  34. package/dist/__golden__/wave-loop/op-point.json +13 -0
  35. package/dist/__golden__/wave-loop/op-pr-review.json +10 -0
  36. package/dist/__golden__/wave-loop/op-sealed.json +10 -0
  37. package/dist/__golden__/wave-loop/op-spend-change-only.json +10 -0
  38. package/dist/__golden__/wave-loop/op-spend.json +10 -0
  39. package/dist/__golden__/wave-loop/op-wave-commands.json +55 -0
  40. package/dist/__golden__/wave-loop/op-wide-refused.json +26 -0
  41. package/dist/__golden__/wave-loop/op-wide-waiting.json +29 -0
  42. package/dist/__golden__/wave-loop/op-wide.json +39 -0
  43. package/dist/action-pins.d.ts +61 -0
  44. package/dist/action-pins.d.ts.map +1 -0
  45. package/dist/action-pins.js +77 -0
  46. package/dist/cli/commands/carve.d.ts +3 -0
  47. package/dist/cli/commands/carve.d.ts.map +1 -1
  48. package/dist/cli/commands/carve.js +36 -7
  49. package/dist/cli/commands/import-report.d.ts +26 -0
  50. package/dist/cli/commands/import-report.d.ts.map +1 -0
  51. package/dist/cli/commands/import-report.js +65 -0
  52. package/dist/cli/commands/import.d.ts +11 -1
  53. package/dist/cli/commands/import.d.ts.map +1 -1
  54. package/dist/cli/commands/import.js +22 -3
  55. package/dist/cli/commands/migrate.d.ts +13 -1
  56. package/dist/cli/commands/migrate.d.ts.map +1 -1
  57. package/dist/cli/commands/migrate.js +134 -10
  58. package/dist/cli/handlers/approve.d.ts.map +1 -1
  59. package/dist/cli/handlers/approve.js +41 -14
  60. package/dist/cli/handlers/build.d.ts.map +1 -1
  61. package/dist/cli/handlers/build.js +6 -1
  62. package/dist/cli/handlers/graph-reader.d.ts +26 -0
  63. package/dist/cli/handlers/graph-reader.d.ts.map +1 -0
  64. package/dist/cli/handlers/graph-reader.js +41 -0
  65. package/dist/cli/handlers/graph.d.ts.map +1 -1
  66. package/dist/cli/handlers/graph.js +14 -0
  67. package/dist/cli/handlers/ledger.d.ts +3 -0
  68. package/dist/cli/handlers/ledger.d.ts.map +1 -0
  69. package/dist/cli/handlers/ledger.js +55 -0
  70. package/dist/cli/handlers/migrate.d.ts.map +1 -1
  71. package/dist/cli/handlers/migrate.js +18 -11
  72. package/dist/cli/handlers/misc.d.ts.map +1 -1
  73. package/dist/cli/handlers/misc.js +12 -1
  74. package/dist/cli/handlers/pr-env.js +2 -2
  75. package/dist/cli/handlers/pr.d.ts +15 -1
  76. package/dist/cli/handlers/pr.d.ts.map +1 -1
  77. package/dist/cli/handlers/pr.js +183 -40
  78. package/dist/cli/handlers/query.d.ts +7 -2
  79. package/dist/cli/handlers/query.d.ts.map +1 -1
  80. package/dist/cli/handlers/query.js +16 -4
  81. package/dist/cli/handlers/release.d.ts.map +1 -1
  82. package/dist/cli/handlers/release.js +6 -3
  83. package/dist/cli/handlers/search.d.ts +2 -4
  84. package/dist/cli/handlers/search.d.ts.map +1 -1
  85. package/dist/cli/handlers/search.js +13 -39
  86. package/dist/cli/main.d.ts.map +1 -1
  87. package/dist/cli/main.js +87 -14
  88. package/dist/cli/parse-args.d.ts.map +1 -1
  89. package/dist/cli/parse-args.js +47 -2
  90. package/dist/cli/registry.d.ts +22 -4
  91. package/dist/cli/registry.d.ts.map +1 -1
  92. package/dist/components/builders.d.ts +5 -1
  93. package/dist/components/builders.d.ts.map +1 -1
  94. package/dist/components/builders.js +5 -0
  95. package/dist/components/capability-plugin-loader.d.ts.map +1 -1
  96. package/dist/components/capability-plugin-loader.js +16 -1
  97. package/dist/components/capability-plugin.d.ts +14 -0
  98. package/dist/components/capability-plugin.d.ts.map +1 -1
  99. package/dist/components/pr-hooks.d.ts +21 -1
  100. package/dist/components/pr-hooks.d.ts.map +1 -1
  101. package/dist/components/pr-hooks.js +72 -4
  102. package/dist/components/pr-pipeline.d.ts +45 -10
  103. package/dist/components/pr-pipeline.d.ts.map +1 -1
  104. package/dist/components/pr-pipeline.js +47 -14
  105. package/dist/components/pr-policy.d.ts +36 -0
  106. package/dist/components/pr-policy.d.ts.map +1 -1
  107. package/dist/components/pr-policy.js +36 -0
  108. package/dist/components/pr-run.d.ts +3 -0
  109. package/dist/components/pr-run.d.ts.map +1 -1
  110. package/dist/components/pr-run.js +1 -0
  111. package/dist/components/starter-plugin.d.ts +1 -0
  112. package/dist/components/starter-plugin.d.ts.map +1 -1
  113. package/dist/components/starter-plugin.js +5 -1
  114. package/dist/components/verbs/index.d.ts +2 -0
  115. package/dist/components/verbs/index.d.ts.map +1 -1
  116. package/dist/components/verbs/index.js +2 -0
  117. package/dist/components/verbs/publish-artifact.d.ts +65 -0
  118. package/dist/components/verbs/publish-artifact.d.ts.map +1 -0
  119. package/dist/components/verbs/publish-artifact.js +74 -0
  120. package/dist/components/verbs/publish-image.d.ts +129 -0
  121. package/dist/components/verbs/publish-image.d.ts.map +1 -0
  122. package/dist/components/verbs/publish-image.js +145 -0
  123. package/dist/config-environments.d.ts +20 -0
  124. package/dist/config-environments.d.ts.map +1 -1
  125. package/dist/config-environments.js +12 -0
  126. package/dist/config.d.ts +25 -1
  127. package/dist/config.d.ts.map +1 -1
  128. package/dist/config.js +10 -2
  129. package/dist/gate-decision.d.ts +20 -2
  130. package/dist/gate-decision.d.ts.map +1 -1
  131. package/dist/gate-decision.js +30 -3
  132. package/dist/graph-ir.d.ts +25 -0
  133. package/dist/graph-ir.d.ts.map +1 -1
  134. package/dist/graph-walk.d.ts +66 -0
  135. package/dist/graph-walk.d.ts.map +1 -0
  136. package/dist/graph-walk.js +70 -0
  137. package/dist/import/parser.d.ts +19 -0
  138. package/dist/import/parser.d.ts.map +1 -1
  139. package/dist/lexicon.d.ts +94 -6
  140. package/dist/lexicon.d.ts.map +1 -1
  141. package/dist/lifecycle/gate-ledger.d.ts +11 -0
  142. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  143. package/dist/lifecycle/git.d.ts +2 -5
  144. package/dist/lifecycle/git.d.ts.map +1 -1
  145. package/dist/lifecycle/ledger-read.d.ts +65 -0
  146. package/dist/lifecycle/ledger-read.d.ts.map +1 -0
  147. package/dist/lifecycle/ledger-read.js +160 -0
  148. package/dist/lifecycle/release-workstation.d.ts +7 -0
  149. package/dist/lifecycle/release-workstation.d.ts.map +1 -1
  150. package/dist/lifecycle/release-workstation.js +25 -0
  151. package/dist/lifecycle/run-ledger.d.ts +4 -3
  152. package/dist/lifecycle/run-ledger.d.ts.map +1 -1
  153. package/dist/lifecycle/run-ledger.js +1 -0
  154. package/dist/lint/discover.js +3 -1
  155. package/dist/lint/post-synth.d.ts +127 -1
  156. package/dist/lint/post-synth.d.ts.map +1 -1
  157. package/dist/lint/post-synth.js +84 -2
  158. package/dist/lint/rules/op/index.d.ts +1 -0
  159. package/dist/lint/rules/op/index.d.ts.map +1 -1
  160. package/dist/lint/rules/op/index.js +2 -0
  161. package/dist/lint/rules/op/ops019-workspace-op-target.d.ts +32 -0
  162. package/dist/lint/rules/op/ops019-workspace-op-target.d.ts.map +1 -0
  163. package/dist/lint/rules/op/ops019-workspace-op-target.js +63 -0
  164. package/dist/op/activities/activity-contracts.d.ts +9 -6
  165. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  166. package/dist/op/activities/activity-contracts.js +5 -2
  167. package/dist/op/activities/index.d.ts +2 -2
  168. package/dist/op/activities/index.d.ts.map +1 -1
  169. package/dist/op/activities/index.js +1 -1
  170. package/dist/op/activities/review-agent.d.ts +90 -4
  171. package/dist/op/activities/review-agent.d.ts.map +1 -1
  172. package/dist/op/activities/review-agent.js +217 -19
  173. package/dist/op/activities/work-evidence.d.ts +8 -1
  174. package/dist/op/activities/work-evidence.d.ts.map +1 -1
  175. package/dist/op/activities/work-evidence.js +1 -0
  176. package/dist/op/builders.d.ts +9 -5
  177. package/dist/op/builders.d.ts.map +1 -1
  178. package/dist/op/builders.js +9 -5
  179. package/dist/op/composites/apply-op.d.ts +29 -2
  180. package/dist/op/composites/apply-op.d.ts.map +1 -1
  181. package/dist/op/composites/apply-op.js +20 -1
  182. package/dist/op/gate-approval-base.d.ts +25 -0
  183. package/dist/op/gate-approval-base.d.ts.map +1 -0
  184. package/dist/op/gate-approval-base.js +71 -0
  185. package/dist/op/gate-approval.d.ts +18 -9
  186. package/dist/op/gate-approval.d.ts.map +1 -1
  187. package/dist/op/gate-approval.js +13 -31
  188. package/dist/op/gate-name.d.ts +13 -0
  189. package/dist/op/gate-name.d.ts.map +1 -1
  190. package/dist/op/gate-name.js +14 -1
  191. package/dist/op/gate-quorum.d.ts +100 -3
  192. package/dist/op/gate-quorum.d.ts.map +1 -1
  193. package/dist/op/gate-quorum.js +108 -6
  194. package/dist/op/gate-review.d.ts +9 -0
  195. package/dist/op/gate-review.d.ts.map +1 -1
  196. package/dist/op/gate-review.js +13 -0
  197. package/dist/op/gate.d.ts +47 -5
  198. package/dist/op/gate.d.ts.map +1 -1
  199. package/dist/op/gate.js +25 -4
  200. package/dist/op/index.d.ts +3 -3
  201. package/dist/op/index.d.ts.map +1 -1
  202. package/dist/op/index.js +1 -1
  203. package/dist/op/local-executor.d.ts +45 -3
  204. package/dist/op/local-executor.d.ts.map +1 -1
  205. package/dist/op/local-executor.js +68 -7
  206. package/dist/op/op-waves-run.d.ts +26 -1
  207. package/dist/op/op-waves-run.d.ts.map +1 -1
  208. package/dist/op/op-waves-run.js +259 -13
  209. package/dist/op/op-waves.d.ts +72 -5
  210. package/dist/op/op-waves.d.ts.map +1 -1
  211. package/dist/op/op-waves.js +110 -15
  212. package/dist/op/runtime.d.ts +11 -0
  213. package/dist/op/runtime.d.ts.map +1 -1
  214. package/dist/op/schedule-overdue.d.ts +16 -0
  215. package/dist/op/schedule-overdue.d.ts.map +1 -1
  216. package/dist/op/schedule-overdue.js +36 -0
  217. package/dist/op/stage-hooks.d.ts +9 -2
  218. package/dist/op/stage-hooks.d.ts.map +1 -1
  219. package/dist/op/stage-hooks.js +7 -5
  220. package/dist/op/workspace-run-env.d.ts +22 -0
  221. package/dist/op/workspace-run-env.d.ts.map +1 -0
  222. package/dist/op/workspace-run-env.js +21 -0
  223. package/dist/pr-forge.d.ts +24 -0
  224. package/dist/pr-forge.d.ts.map +1 -1
  225. package/dist/pr-forge.js +47 -0
  226. package/dist/pr-loop.d.ts +10 -0
  227. package/dist/pr-loop.d.ts.map +1 -1
  228. package/dist/pr-loop.js +4 -0
  229. package/dist/pr-migrations.d.ts +62 -0
  230. package/dist/pr-migrations.d.ts.map +1 -0
  231. package/dist/pr-migrations.js +50 -0
  232. package/dist/pr-post.d.ts +42 -0
  233. package/dist/pr-post.d.ts.map +1 -1
  234. package/dist/pr-post.js +68 -2
  235. package/dist/read-filter.d.ts +65 -0
  236. package/dist/read-filter.d.ts.map +1 -0
  237. package/dist/read-filter.js +179 -0
  238. package/dist/run-event.d.ts +1 -1
  239. package/dist/run-event.d.ts.map +1 -1
  240. package/dist/terraform/carve.d.ts +24 -3
  241. package/dist/terraform/carve.d.ts.map +1 -1
  242. package/dist/terraform/carve.js +32 -14
  243. package/dist/terraform/cuts.d.ts +97 -0
  244. package/dist/terraform/cuts.d.ts.map +1 -0
  245. package/dist/terraform/cuts.js +138 -0
  246. package/dist/terraform/folds.d.ts +26 -0
  247. package/dist/terraform/folds.d.ts.map +1 -0
  248. package/dist/terraform/folds.js +38 -0
  249. package/dist/terraform/graph.d.ts.map +1 -1
  250. package/dist/terraform/graph.js +10 -0
  251. package/dist/terraform/reach.d.ts +140 -0
  252. package/dist/terraform/reach.d.ts.map +1 -0
  253. package/dist/terraform/reach.js +170 -0
  254. package/dist/terraform/score.d.ts +14 -0
  255. package/dist/terraform/score.d.ts.map +1 -1
  256. package/dist/terraform/score.js +14 -31
  257. package/dist/terraform/state.d.ts.map +1 -1
  258. package/dist/terraform/state.js +4 -2
  259. package/dist/terraform/types.d.ts +8 -0
  260. package/dist/terraform/types.d.ts.map +1 -1
  261. package/dist/wave-blocked.d.ts +10 -1
  262. package/dist/wave-blocked.d.ts.map +1 -1
  263. package/dist/wave-blocked.js +31 -3
  264. package/dist/wave-runtime.mjs +264 -254
  265. package/dist/workspace/check.schema.json +13 -2
  266. package/dist/workspace/checks/ci-branch.d.ts +44 -0
  267. package/dist/workspace/checks/ci-branch.d.ts.map +1 -0
  268. package/dist/workspace/checks/ci-branch.js +133 -0
  269. package/dist/workspace/checks/gate-approval.d.ts +58 -0
  270. package/dist/workspace/checks/gate-approval.d.ts.map +1 -0
  271. package/dist/workspace/checks/gate-approval.js +103 -0
  272. package/dist/workspace/checks/links.d.ts +3 -0
  273. package/dist/workspace/checks/links.d.ts.map +1 -1
  274. package/dist/workspace/checks/links.js +12 -1
  275. package/dist/workspace/checks/org.d.ts +48 -0
  276. package/dist/workspace/checks/org.d.ts.map +1 -0
  277. package/dist/workspace/checks/org.js +156 -0
  278. package/dist/workspace/checks.d.ts +12 -1
  279. package/dist/workspace/checks.d.ts.map +1 -1
  280. package/dist/workspace/checks.js +63 -7
  281. package/dist/workspace/ci-branches.d.ts +16 -0
  282. package/dist/workspace/ci-branches.d.ts.map +1 -1
  283. package/dist/workspace/ci-branches.js +27 -0
  284. package/dist/workspace/ci-green-cli.js +7 -7
  285. package/dist/workspace/ci-green-workflow.d.ts +1 -1
  286. package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
  287. package/dist/workspace/ci-green-workflow.js +13 -8
  288. package/dist/workspace/ci-green.d.ts +14 -0
  289. package/dist/workspace/ci-green.d.ts.map +1 -1
  290. package/dist/workspace/ci-green.js +44 -11
  291. package/dist/workspace/ci-run.d.ts +9 -1
  292. package/dist/workspace/ci-run.d.ts.map +1 -1
  293. package/dist/workspace/ci-run.js +67 -6
  294. package/dist/workspace/ci-serve.d.ts +9 -0
  295. package/dist/workspace/ci-serve.d.ts.map +1 -1
  296. package/dist/workspace/ci-serve.js +114 -76
  297. package/dist/workspace/compose-graph.d.ts +14 -0
  298. package/dist/workspace/compose-graph.d.ts.map +1 -1
  299. package/dist/workspace/composites.d.ts +4 -1
  300. package/dist/workspace/composites.d.ts.map +1 -1
  301. package/dist/workspace/composites.js +5 -2
  302. package/dist/workspace/composites.schema.json +22 -0
  303. package/dist/workspace/conformance/__writer_fixture__/work/work.schema.json +105 -1
  304. package/dist/workspace/cross-root-lint.d.ts +56 -0
  305. package/dist/workspace/cross-root-lint.d.ts.map +1 -0
  306. package/dist/workspace/cross-root-lint.js +111 -0
  307. package/dist/workspace/dbt-models-reader.d.ts +44 -0
  308. package/dist/workspace/dbt-models-reader.d.ts.map +1 -0
  309. package/dist/workspace/dbt-models-reader.js +356 -0
  310. package/dist/workspace/dbt-project-common.d.ts +40 -0
  311. package/dist/workspace/dbt-project-common.d.ts.map +1 -0
  312. package/dist/workspace/dbt-project-common.js +106 -0
  313. package/dist/workspace/dbt-project-reader.d.ts +4 -3
  314. package/dist/workspace/dbt-project-reader.d.ts.map +1 -1
  315. package/dist/workspace/dbt-project-reader.js +14 -108
  316. package/dist/workspace/decision-rank.d.ts +26 -0
  317. package/dist/workspace/decision-rank.d.ts.map +1 -0
  318. package/dist/workspace/decision-rank.js +16 -0
  319. package/dist/workspace/declaration.d.ts +50 -2
  320. package/dist/workspace/declaration.d.ts.map +1 -1
  321. package/dist/workspace/declaration.js +57 -11
  322. package/dist/workspace/declaration.schema.json +166 -24
  323. package/dist/workspace/environments.d.ts +26 -2
  324. package/dist/workspace/environments.d.ts.map +1 -1
  325. package/dist/workspace/environments.js +26 -2
  326. package/dist/workspace/exit-cli.d.ts +44 -0
  327. package/dist/workspace/exit-cli.d.ts.map +1 -0
  328. package/dist/workspace/exit-cli.js +198 -0
  329. package/dist/workspace/exit.d.ts +159 -0
  330. package/dist/workspace/exit.d.ts.map +1 -0
  331. package/dist/workspace/exit.js +480 -0
  332. package/dist/workspace/exit.schema.json +257 -0
  333. package/dist/workspace/flyway-dir.d.ts +39 -0
  334. package/dist/workspace/flyway-dir.d.ts.map +1 -0
  335. package/dist/workspace/flyway-dir.js +104 -0
  336. package/dist/workspace/flyway-layout.d.ts +96 -0
  337. package/dist/workspace/flyway-layout.d.ts.map +1 -0
  338. package/dist/workspace/flyway-layout.js +308 -0
  339. package/dist/workspace/graph-cache.d.ts +3 -1
  340. package/dist/workspace/graph-cache.d.ts.map +1 -1
  341. package/dist/workspace/graph-cache.js +12 -1
  342. package/dist/workspace/graph-cli.d.ts.map +1 -1
  343. package/dist/workspace/graph-cli.js +26 -8
  344. package/dist/workspace/graph.schema.json +34 -0
  345. package/dist/workspace/import-fetch.d.ts +38 -0
  346. package/dist/workspace/import-fetch.d.ts.map +1 -0
  347. package/dist/workspace/import-fetch.js +99 -0
  348. package/dist/workspace/import-init.d.ts +26 -0
  349. package/dist/workspace/import-init.d.ts.map +1 -0
  350. package/dist/workspace/import-init.js +130 -0
  351. package/dist/workspace/import-source.d.ts +147 -0
  352. package/dist/workspace/import-source.d.ts.map +1 -0
  353. package/dist/workspace/import-source.js +370 -0
  354. package/dist/workspace/init.d.ts +4 -0
  355. package/dist/workspace/init.d.ts.map +1 -1
  356. package/dist/workspace/init.js +70 -3
  357. package/dist/workspace/intent.d.ts +2 -3
  358. package/dist/workspace/intent.d.ts.map +1 -1
  359. package/dist/workspace/intent.js +5 -9
  360. package/dist/workspace/kind-link-readers.d.ts +59 -2
  361. package/dist/workspace/kind-link-readers.d.ts.map +1 -1
  362. package/dist/workspace/kind-link-readers.js +17 -1
  363. package/dist/workspace/kind-readers.d.ts +6 -0
  364. package/dist/workspace/kind-readers.d.ts.map +1 -1
  365. package/dist/workspace/kind-readers.js +18 -0
  366. package/dist/workspace/kinds.d.ts +90 -3
  367. package/dist/workspace/kinds.d.ts.map +1 -1
  368. package/dist/workspace/kinds.js +161 -7
  369. package/dist/workspace/ledger.schema.json +158 -0
  370. package/dist/workspace/lexicon-outputs.d.ts +20 -1
  371. package/dist/workspace/lexicon-outputs.d.ts.map +1 -1
  372. package/dist/workspace/lexicon-outputs.js +102 -10
  373. package/dist/workspace/lineage-check.d.ts +3 -0
  374. package/dist/workspace/lineage-check.d.ts.map +1 -1
  375. package/dist/workspace/lineage-check.js +23 -4
  376. package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
  377. package/dist/workspace/lineage-upgrade.js +13 -4
  378. package/dist/workspace/links.d.ts +8 -1
  379. package/dist/workspace/links.d.ts.map +1 -1
  380. package/dist/workspace/links.js +25 -1
  381. package/dist/workspace/ls.d.ts +8 -0
  382. package/dist/workspace/ls.d.ts.map +1 -1
  383. package/dist/workspace/ls.js +7 -1
  384. package/dist/workspace/ls.schema.json +7 -0
  385. package/dist/workspace/member-commands.d.ts.map +1 -1
  386. package/dist/workspace/member-commands.js +54 -5
  387. package/dist/workspace/migrate-links.d.ts +45 -0
  388. package/dist/workspace/migrate-links.d.ts.map +1 -0
  389. package/dist/workspace/migrate-links.js +100 -0
  390. package/dist/workspace/migration-dirs.d.ts +74 -0
  391. package/dist/workspace/migration-dirs.d.ts.map +1 -0
  392. package/dist/workspace/migration-dirs.js +254 -0
  393. package/dist/workspace/new-migrations.d.ts +47 -0
  394. package/dist/workspace/new-migrations.d.ts.map +1 -0
  395. package/dist/workspace/new-migrations.js +91 -0
  396. package/dist/workspace/pr-report.schema.json +46 -0
  397. package/dist/workspace/promote.d.ts +96 -0
  398. package/dist/workspace/promote.d.ts.map +1 -0
  399. package/dist/workspace/promote.js +263 -0
  400. package/dist/workspace/reason-codes.d.ts +21 -0
  401. package/dist/workspace/reason-codes.d.ts.map +1 -1
  402. package/dist/workspace/reason-codes.js +25 -0
  403. package/dist/workspace/records-cli.d.ts +11 -0
  404. package/dist/workspace/records-cli.d.ts.map +1 -1
  405. package/dist/workspace/records-cli.js +57 -5
  406. package/dist/workspace/records.schema.json +5 -0
  407. package/dist/workspace/release-candidates.d.ts +2 -0
  408. package/dist/workspace/release-candidates.d.ts.map +1 -1
  409. package/dist/workspace/release-candidates.js +5 -2
  410. package/dist/workspace/release-train.d.ts +53 -0
  411. package/dist/workspace/release-train.d.ts.map +1 -0
  412. package/dist/workspace/release-train.js +134 -0
  413. package/dist/workspace/release-workflow.d.ts.map +1 -1
  414. package/dist/workspace/release-workflow.js +4 -3
  415. package/dist/workspace/repo-ref.d.ts +45 -0
  416. package/dist/workspace/repo-ref.d.ts.map +1 -0
  417. package/dist/workspace/repo-ref.js +114 -0
  418. package/dist/workspace/run-event.schema.json +2 -2
  419. package/dist/workspace/runs-cli.d.ts +9 -0
  420. package/dist/workspace/runs-cli.d.ts.map +1 -1
  421. package/dist/workspace/runs-cli.js +48 -3
  422. package/dist/workspace/runs.schema.json +7 -0
  423. package/dist/workspace/search-contract.d.ts +1 -1
  424. package/dist/workspace/search-contract.d.ts.map +1 -1
  425. package/dist/workspace/status-decisions.d.ts +37 -0
  426. package/dist/workspace/status-decisions.d.ts.map +1 -0
  427. package/dist/workspace/status-decisions.js +147 -0
  428. package/dist/workspace/status-gates.d.ts +9 -0
  429. package/dist/workspace/status-gates.d.ts.map +1 -1
  430. package/dist/workspace/status.d.ts +4 -0
  431. package/dist/workspace/status.d.ts.map +1 -1
  432. package/dist/workspace/status.js +14 -2
  433. package/dist/workspace/status.schema.json +6 -0
  434. package/dist/workspace/work-cli.d.ts.map +1 -1
  435. package/dist/workspace/work-cli.js +5 -3
  436. package/dist/workspace/work-evidence-ops.d.ts +83 -0
  437. package/dist/workspace/work-evidence-ops.d.ts.map +1 -0
  438. package/dist/workspace/work-evidence-ops.js +169 -0
  439. package/dist/workspace/work-evidence.d.ts +12 -2
  440. package/dist/workspace/work-evidence.d.ts.map +1 -1
  441. package/dist/workspace/work-evidence.js +21 -3
  442. package/dist/workspace/work-evidence.schema.json +49 -2
  443. package/dist/workspace/workspace-gates.d.ts +110 -0
  444. package/dist/workspace/workspace-gates.d.ts.map +1 -0
  445. package/dist/workspace/workspace-gates.js +140 -0
  446. package/dist/workspace/workspace-kinds.schema.json +108 -10
  447. package/dist/workspace/workspace-ledger.d.ts +76 -0
  448. package/dist/workspace/workspace-ledger.d.ts.map +1 -0
  449. package/dist/workspace/workspace-ledger.js +91 -0
  450. package/dist/workspace/workspace-run.d.ts +112 -0
  451. package/dist/workspace/workspace-run.d.ts.map +1 -0
  452. package/dist/workspace/workspace-run.js +432 -0
  453. package/package.json +1 -1
  454. package/src/__golden__/wave-loop/op-by-apply-after-fails.json +10 -0
  455. package/src/__golden__/wave-loop/op-by-apply-hold.json +10 -0
  456. package/src/__golden__/wave-loop/op-by-apply-ignored.json +10 -0
  457. package/src/__golden__/wave-loop/op-by-apply-pass.json +10 -0
  458. package/src/__golden__/wave-loop/op-by-apply-skip.json +10 -0
  459. package/src/__golden__/wave-loop/op-cost-member-clash.json +6 -0
  460. package/src/__golden__/wave-loop/op-cost-member.json +13 -0
  461. package/src/__golden__/wave-loop/op-denied-source.json +16 -0
  462. package/src/__golden__/wave-loop/op-denied.json +16 -0
  463. package/src/__golden__/wave-loop/op-discover-rest-short.json +6 -0
  464. package/src/__golden__/wave-loop/op-discover-rest-stops.json +13 -0
  465. package/src/__golden__/wave-loop/op-discover-rest.json +26 -0
  466. package/src/__golden__/wave-loop/op-discover-wide.json +26 -0
  467. package/src/__golden__/wave-loop/op-held-on-again.json +16 -0
  468. package/src/__golden__/wave-loop/op-held-on-before-apply.json +16 -0
  469. package/src/__golden__/wave-loop/op-held-on.json +16 -0
  470. package/src/__golden__/wave-loop/op-hooks-apply-fails.json +13 -0
  471. package/src/__golden__/wave-loop/op-hooks-fail-after-apply.json +13 -0
  472. package/src/__golden__/wave-loop/op-hooks-fail-before-apply.json +13 -0
  473. package/src/__golden__/wave-loop/op-hooks-fail-before-plan.json +13 -0
  474. package/src/__golden__/wave-loop/op-hooks-held.json +13 -0
  475. package/src/__golden__/wave-loop/op-hooks-hold-before-apply.json +13 -0
  476. package/src/__golden__/wave-loop/op-hooks-pass.json +13 -0
  477. package/src/__golden__/wave-loop/op-hooks-refused-at-base.json +13 -0
  478. package/src/__golden__/wave-loop/op-hooks-share.json +33 -0
  479. package/src/__golden__/wave-loop/op-outcome-held.json +10 -0
  480. package/src/__golden__/wave-loop/op-outcome-replan-approved.json +10 -0
  481. package/src/__golden__/wave-loop/op-outcome-replan-forever.json +10 -0
  482. package/src/__golden__/wave-loop/op-outcome-replan.json +10 -0
  483. package/src/__golden__/wave-loop/op-outcome-stood-down.json +10 -0
  484. package/src/__golden__/wave-loop/op-outcome-unreadable.json +10 -0
  485. package/src/__golden__/wave-loop/op-overridden.json +16 -0
  486. package/src/__golden__/wave-loop/op-per-run.json +20 -0
  487. package/src/__golden__/wave-loop/op-point.json +13 -0
  488. package/src/__golden__/wave-loop/op-pr-review.json +10 -0
  489. package/src/__golden__/wave-loop/op-sealed.json +10 -0
  490. package/src/__golden__/wave-loop/op-spend-change-only.json +10 -0
  491. package/src/__golden__/wave-loop/op-spend.json +10 -0
  492. package/src/__golden__/wave-loop/op-wave-commands.json +55 -0
  493. package/src/__golden__/wave-loop/op-wide-refused.json +26 -0
  494. package/src/__golden__/wave-loop/op-wide-waiting.json +29 -0
  495. package/src/__golden__/wave-loop/op-wide.json +39 -0
  496. package/src/action-pins.test.ts +78 -0
  497. package/src/action-pins.ts +92 -0
  498. package/src/cli/commands/carve-score-once.test.ts +99 -0
  499. package/src/cli/commands/carve.ts +37 -6
  500. package/src/cli/commands/check-lexicon.test.ts +2 -2
  501. package/src/cli/commands/import-report.ts +71 -0
  502. package/src/cli/commands/import.ts +33 -3
  503. package/src/cli/commands/migrate-dialect.test.ts +145 -0
  504. package/src/cli/commands/migrate.ts +132 -11
  505. package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +4 -4
  506. package/src/cli/handlers/approve.ts +33 -4
  507. package/src/cli/handlers/build.ts +7 -1
  508. package/src/cli/handlers/graph-reader.test.ts +31 -0
  509. package/src/cli/handlers/graph-reader.ts +51 -0
  510. package/src/cli/handlers/graph.ts +14 -0
  511. package/src/cli/handlers/ledger.ts +54 -0
  512. package/src/cli/handlers/migrate.ts +21 -12
  513. package/src/cli/handlers/misc.ts +10 -1
  514. package/src/cli/handlers/pr-env.ts +2 -2
  515. package/src/cli/handlers/pr.ts +176 -37
  516. package/src/cli/handlers/query.ts +16 -4
  517. package/src/cli/handlers/release.ts +6 -3
  518. package/src/cli/handlers/search.ts +13 -34
  519. package/src/cli/main.test.ts +17 -0
  520. package/src/cli/main.ts +87 -14
  521. package/src/cli/parse-args.ts +35 -2
  522. package/src/cli/registry.ts +22 -4
  523. package/src/components/builders.ts +8 -0
  524. package/src/components/capability-plugin-loader.ts +13 -1
  525. package/src/components/capability-plugin.ts +16 -0
  526. package/src/components/pr-env-run-bundle.test.ts +1 -0
  527. package/src/components/pr-hooks.test.ts +73 -5
  528. package/src/components/pr-hooks.ts +95 -5
  529. package/src/components/pr-pipeline.ts +64 -16
  530. package/src/components/pr-policy.test.ts +64 -1
  531. package/src/components/pr-policy.ts +53 -0
  532. package/src/components/pr-run.ts +4 -0
  533. package/src/components/registry.test.ts +1 -1
  534. package/src/components/starter-plugin.ts +6 -0
  535. package/src/components/verbs/index.ts +2 -0
  536. package/src/components/verbs/publish-artifact.test.ts +60 -0
  537. package/src/components/verbs/publish-artifact.ts +122 -0
  538. package/src/components/verbs/publish-image.test.ts +101 -0
  539. package/src/components/verbs/publish-image.ts +265 -0
  540. package/src/config-environments.ts +28 -1
  541. package/src/config.ts +24 -1
  542. package/src/gate-decision-bundle.test.ts +5 -2
  543. package/src/gate-decision-identity.test.ts +20 -0
  544. package/src/gate-decision.ts +46 -3
  545. package/src/graph-ir.ts +27 -0
  546. package/src/graph-walk.test.ts +44 -0
  547. package/src/graph-walk.ts +102 -0
  548. package/src/import/parser.ts +20 -0
  549. package/src/lexicon.ts +98 -6
  550. package/src/lifecycle/gate-ledger.ts +8 -0
  551. package/src/lifecycle/git.ts +2 -2
  552. package/src/lifecycle/ledger-read.test.ts +107 -0
  553. package/src/lifecycle/ledger-read.ts +195 -0
  554. package/src/lifecycle/release-workstation.ts +24 -0
  555. package/src/lifecycle/run-ledger.ts +5 -2
  556. package/src/lint/cross-root.test.ts +122 -0
  557. package/src/lint/discover.ts +3 -1
  558. package/src/lint/post-synth.ts +212 -2
  559. package/src/lint/rules/op/index.ts +2 -0
  560. package/src/lint/rules/op/ops019-workspace-op-target.ts +89 -0
  561. package/src/op/activities/activity-contracts.ts +5 -2
  562. package/src/op/activities/index.ts +2 -2
  563. package/src/op/activities/review-agent.test.ts +221 -4
  564. package/src/op/activities/review-agent.ts +265 -22
  565. package/src/op/activities/work-evidence.ts +9 -1
  566. package/src/op/builders.ts +9 -5
  567. package/src/op/composites/apply-op.test.ts +108 -0
  568. package/src/op/composites/apply-op.ts +37 -2
  569. package/src/op/gate-approval-at-base.test.ts +97 -0
  570. package/src/op/gate-approval-base.ts +72 -0
  571. package/src/op/gate-approval.test.ts +6 -1
  572. package/src/op/gate-approval.ts +22 -27
  573. package/src/op/gate-name.ts +16 -1
  574. package/src/op/gate-quorum-at-base.test.ts +211 -0
  575. package/src/op/gate-quorum.ts +156 -8
  576. package/src/op/gate-review.ts +14 -0
  577. package/src/op/gate.ts +69 -9
  578. package/src/op/index.ts +3 -2
  579. package/src/op/local-executor.ts +91 -11
  580. package/src/op/op-waves-depends-on.test.ts +81 -0
  581. package/src/op/op-waves-hook-targets.test.ts +4 -2
  582. package/src/op/op-waves-quorum-product.test.ts +158 -0
  583. package/src/op/op-waves-run.ts +288 -14
  584. package/src/op/op-waves.ts +143 -14
  585. package/src/op/runtime.ts +8 -0
  586. package/src/op/schedule-overdue.ts +33 -0
  587. package/src/op/stage-hooks.ts +13 -5
  588. package/src/op/workspace-run-env.ts +29 -0
  589. package/src/pr-forge-bundle.test.ts +2 -2
  590. package/src/pr-forge.test.ts +47 -0
  591. package/src/pr-forge.ts +68 -0
  592. package/src/pr-loop-bundle.test.ts +3 -2
  593. package/src/pr-loop.test.ts +52 -0
  594. package/src/pr-loop.ts +12 -0
  595. package/src/pr-migrations.ts +97 -0
  596. package/src/pr-post-open.e2e.test.ts +36 -0
  597. package/src/pr-post.test.ts +102 -3
  598. package/src/pr-post.ts +90 -2
  599. package/src/read-filter.test.ts +54 -0
  600. package/src/read-filter.ts +193 -0
  601. package/src/run-event.ts +1 -1
  602. package/src/stage-hooks-bundle.test.ts +1 -0
  603. package/src/terraform/carve.ts +52 -15
  604. package/src/terraform/cuts.test.ts +173 -0
  605. package/src/terraform/cuts.ts +188 -0
  606. package/src/terraform/folds.ts +44 -0
  607. package/src/terraform/graph.ts +9 -0
  608. package/src/terraform/radius.test.ts +90 -0
  609. package/src/terraform/reach.test.ts +146 -0
  610. package/src/terraform/reach.ts +247 -0
  611. package/src/terraform/score.ts +27 -29
  612. package/src/terraform/state.ts +3 -1
  613. package/src/terraform/types.ts +8 -0
  614. package/src/wave-blocked.ts +28 -3
  615. package/src/wave-loop-golden.test.ts +4 -3
  616. package/src/workspace/__fixtures__/import/spacelift.json +9 -0
  617. package/src/workspace/__fixtures__/import/tfc.json +13 -0
  618. package/src/workspace/behold-kinds.test.ts +2 -1
  619. package/src/workspace/check-contract.test.ts +8 -6
  620. package/src/workspace/check.schema.json +13 -2
  621. package/src/workspace/checks/ci-branch.test.ts +128 -0
  622. package/src/workspace/checks/ci-branch.ts +141 -0
  623. package/src/workspace/checks/gate-approval.test.ts +77 -0
  624. package/src/workspace/checks/gate-approval.ts +121 -0
  625. package/src/workspace/checks/links.ts +12 -1
  626. package/src/workspace/checks/org.ts +169 -0
  627. package/src/workspace/checks.ts +68 -7
  628. package/src/workspace/ci-branches.ts +31 -0
  629. package/src/workspace/ci-green-branches.test.ts +153 -0
  630. package/src/workspace/ci-green-cli.ts +7 -7
  631. package/src/workspace/ci-green-workflow.test.ts +4 -3
  632. package/src/workspace/ci-green-workflow.ts +13 -9
  633. package/src/workspace/ci-green.ts +49 -12
  634. package/src/workspace/ci-run.test.ts +72 -1
  635. package/src/workspace/ci-run.ts +68 -8
  636. package/src/workspace/ci-serve.test.ts +95 -0
  637. package/src/workspace/ci-serve.ts +106 -65
  638. package/src/workspace/component-environments.test.ts +25 -2
  639. package/src/workspace/compose-graph.ts +9 -2
  640. package/src/workspace/composites.schema.json +22 -0
  641. package/src/workspace/composites.test.ts +39 -8
  642. package/src/workspace/composites.ts +6 -2
  643. package/src/workspace/conformance/__writer_fixture__/work/work.schema.json +105 -1
  644. package/src/workspace/cross-root-lint.test.ts +90 -0
  645. package/src/workspace/cross-root-lint.ts +126 -0
  646. package/src/workspace/dbt-kind.test.ts +3 -2
  647. package/src/workspace/dbt-models-reader.ts +362 -0
  648. package/src/workspace/dbt-models.test.ts +248 -0
  649. package/src/workspace/dbt-project-common.ts +113 -0
  650. package/src/workspace/dbt-project-reader.ts +29 -112
  651. package/src/workspace/decision-rank.ts +40 -0
  652. package/src/workspace/declaration.schema.json +166 -24
  653. package/src/workspace/declaration.ts +101 -15
  654. package/src/workspace/environments.ts +40 -4
  655. package/src/workspace/exit-cli.ts +219 -0
  656. package/src/workspace/exit.schema.json +257 -0
  657. package/src/workspace/exit.test.ts +185 -0
  658. package/src/workspace/exit.ts +560 -0
  659. package/src/workspace/flyway-dir.ts +113 -0
  660. package/src/workspace/flyway-layout.ts +337 -0
  661. package/src/workspace/graph-cache.ts +12 -2
  662. package/src/workspace/graph-cli.ts +25 -10
  663. package/src/workspace/graph-contract.test.ts +4 -2
  664. package/src/workspace/graph.schema.json +34 -0
  665. package/src/workspace/identity.test.ts +29 -2
  666. package/src/workspace/import-fetch.ts +151 -0
  667. package/src/workspace/import-init.ts +147 -0
  668. package/src/workspace/import-source.test.ts +183 -0
  669. package/src/workspace/import-source.ts +462 -0
  670. package/src/workspace/init.test.ts +55 -1
  671. package/src/workspace/init.ts +66 -4
  672. package/src/workspace/intent.ts +6 -14
  673. package/src/workspace/kind-link-readers.ts +68 -3
  674. package/src/workspace/kind-readers.test.ts +36 -0
  675. package/src/workspace/kind-readers.ts +18 -1
  676. package/src/workspace/kinds.test.ts +35 -7
  677. package/src/workspace/kinds.ts +212 -11
  678. package/src/workspace/ledger.schema.json +158 -0
  679. package/src/workspace/lexicon-outputs.ts +132 -13
  680. package/src/workspace/lineage-check.ts +30 -7
  681. package/src/workspace/lineage-upgrade.test.ts +20 -0
  682. package/src/workspace/lineage-upgrade.ts +16 -7
  683. package/src/workspace/links.ts +24 -1
  684. package/src/workspace/ls-contract.test.ts +1 -1
  685. package/src/workspace/ls.schema.json +7 -0
  686. package/src/workspace/ls.ts +10 -1
  687. package/src/workspace/member-commands.ts +60 -2
  688. package/src/workspace/member-file-schema.test.ts +124 -0
  689. package/src/workspace/migrate-links.test.ts +99 -0
  690. package/src/workspace/migrate-links.ts +108 -0
  691. package/src/workspace/migration-dirs.test.ts +272 -0
  692. package/src/workspace/migration-dirs.ts +267 -0
  693. package/src/workspace/new-migrations.test.ts +98 -0
  694. package/src/workspace/new-migrations.ts +109 -0
  695. package/src/workspace/org-scope.test.ts +238 -0
  696. package/src/workspace/pr-report.schema.json +46 -0
  697. package/src/workspace/promote.test.ts +114 -0
  698. package/src/workspace/promote.ts +302 -0
  699. package/src/workspace/reason-codes.test.ts +10 -1
  700. package/src/workspace/reason-codes.ts +25 -0
  701. package/src/workspace/records-cli.ts +58 -5
  702. package/src/workspace/records.schema.json +5 -0
  703. package/src/workspace/release-candidates.ts +7 -2
  704. package/src/workspace/release-train.ts +157 -0
  705. package/src/workspace/release-workflow.ts +4 -3
  706. package/src/workspace/repo-ref.ts +113 -0
  707. package/src/workspace/run-event.schema.json +2 -2
  708. package/src/workspace/runs-cli.ts +48 -4
  709. package/src/workspace/runs.schema.json +7 -0
  710. package/src/workspace/status-contract.test.ts +46 -1
  711. package/src/workspace/status-decisions.test.ts +100 -0
  712. package/src/workspace/status-decisions.ts +151 -0
  713. package/src/workspace/status-gates.ts +9 -0
  714. package/src/workspace/status.schema.json +6 -0
  715. package/src/workspace/status.ts +17 -3
  716. package/src/workspace/where-filters.test.ts +39 -0
  717. package/src/workspace/work-cli.ts +5 -3
  718. package/src/workspace/work-evidence-ops.test.ts +176 -0
  719. package/src/workspace/work-evidence-ops.ts +189 -0
  720. package/src/workspace/work-evidence.schema.json +49 -2
  721. package/src/workspace/work-evidence.ts +22 -3
  722. package/src/workspace/workspace-gates.ts +200 -0
  723. package/src/workspace/workspace-kinds.schema.json +108 -10
  724. package/src/workspace/workspace-ledger.ts +130 -0
  725. package/src/workspace/workspace-run.test.ts +322 -0
  726. package/src/workspace/workspace-run.ts +461 -0
@@ -0,0 +1,151 @@
1
+ /**
2
+ * The decisions covering each member, for the gates in `chant workspace
3
+ * status --json` (#3616): a reader showing a gate to the person about to
4
+ * approve it names the decision the gate enforces without a
5
+ * `graph --intent` walk of its own.
6
+ *
7
+ * This is the part of the intent walk (`intent.ts`) that reads only records.
8
+ * A decision covers a member when one of its `constrains` entries is
9
+ * `member:<name>`, or a `path:` that is the member's directory, a directory
10
+ * above it, or a path inside it that the member holds (not a member nested
11
+ * in it). A superseded covering decision stands for the decision that
12
+ * supersedes it, at the end of its chain. Only current decisions are listed,
13
+ * as `<kind>/<id>`, ranked as `why.decisions` ranks them
14
+ * (`decision-rank.ts`). `carried` needs the member's history, so it never
15
+ * applies, and neither do `issue` or `contract`, which need commits. Every
16
+ * declared record kind other than a work kind is read, as the intent walk
17
+ * reads them; a kind that can't be read is left out.
18
+ */
19
+
20
+ import { declaredRecordKinds, type Declaration } from "./declaration";
21
+ import { declaredKindFile } from "./declared-kinds";
22
+ import { compareDecisionRank, type DecisionRankKey, type WhyRelevance } from "./decision-rank";
23
+ import { decidedCommits } from "./record-decided";
24
+ import { constraintCovers, isWorkspacePath, memberHolding } from "./record-assets";
25
+ import type { LoadedRecordKind, RecordEntry } from "./records";
26
+ import { joinPath } from "./tree";
27
+
28
+ interface ReadKind {
29
+ loaded: LoadedRecordKind;
30
+ top: string | undefined;
31
+ /** Where the kind's `path:` entries resolve, from the git top. */
32
+ prefix: string;
33
+ records: RecordEntry[];
34
+ byId: Map<string, RecordEntry>;
35
+ }
36
+
37
+ /** Reads one kind's records, current and superseded; `readRecordsFor` in the working tree unless a test swaps it. */
38
+ export type DecisionKindReader = (file: string, root: string) => Promise<{ loaded: LoadedRecordKind; top: string | undefined; workspaceRoot: string; records: RecordEntry[] }>;
39
+
40
+ const defaultReader: DecisionKindReader = async (file, root) => {
41
+ const { readRecordsFor } = await import("./records-cli");
42
+ const read = await readRecordsFor({ kind: file, cwd: root });
43
+ return { loaded: read.loaded, top: read.top, workspaceRoot: read.workspaceRoot, records: read.result.records };
44
+ };
45
+
46
+ /**
47
+ * Read the declared decision kinds once and return the covering decisions of
48
+ * a member. `root` is the workspace root on disk and `rootDir` the same from
49
+ * the git top (`""` at the top).
50
+ */
51
+ export async function memberDecisionsReader(
52
+ declaration: Declaration,
53
+ root: string,
54
+ rootDir: string,
55
+ read: DecisionKindReader = defaultReader,
56
+ ): Promise<(member: { name: string; dir: string }) => string[]> {
57
+ const kinds: ReadKind[] = [];
58
+ for (const d of declaredRecordKinds(declaration)) {
59
+ try {
60
+ const r = await read(declaredKindFile(d, root), root);
61
+ if (r.loaded.kind.work) continue;
62
+ const byId = new Map<string, RecordEntry>();
63
+ for (const v of r.records) if (v.id !== null) byId.set(v.id, v);
64
+ kinds.push({ loaded: r.loaded, top: r.top, prefix: r.workspaceRoot === "." ? "" : r.workspaceRoot, records: r.records, byId });
65
+ } catch {
66
+ // A kind that can't be read covers nothing here; workspace check and records report why.
67
+ }
68
+ }
69
+ // When each current decision was decided, read once per kind on first need.
70
+ const decidedOn = new Map<ReadKind, Map<string, string | null>>();
71
+ const decidedDate = (k: ReadKind, v: RecordEntry): string | null => {
72
+ if (!k.loaded.kind.approval || !k.top) return null;
73
+ let dates = decidedOn.get(k);
74
+ if (!dates) {
75
+ const current = k.records.filter((r) => r.id !== null && r.supersededBy === null).map((r) => r.path);
76
+ const decided = decidedCommits(k.top, "HEAD", current, k.loaded.kind);
77
+ dates = new Map(current.map((p) => [p, decided.get(p)?.date ?? null]));
78
+ decidedOn.set(k, dates);
79
+ }
80
+ return dates.get(v.path) ?? null;
81
+ };
82
+
83
+ return (member) => {
84
+ const memberFull = joinPath(rootDir, member.dir);
85
+ const ranked: { key: DecisionRankKey; label: string }[] = [];
86
+ for (const k of kinds) {
87
+ const field = k.loaded.kind.constrains?.field;
88
+ if (!field) continue;
89
+ const name = k.loaded.kind.name;
90
+ /** How each record covers the member: the relevance and the longest covering path entry. */
91
+ const covers = (v: RecordEntry): { relevance: WhyRelevance; specificity: number } | null => {
92
+ const list = v.data?.[field];
93
+ if (!Array.isArray(list)) return null;
94
+ let path = 0;
95
+ let byMember = false;
96
+ let inside = false;
97
+ for (const entry of list) {
98
+ if (typeof entry !== "string") continue;
99
+ if (entry.startsWith("member:")) {
100
+ if (entry.slice("member:".length) === member.name) byMember = true;
101
+ } else if (entry.startsWith("path:")) {
102
+ const p = entry.slice("path:".length);
103
+ if (!isWorkspacePath(p)) continue;
104
+ const full = joinPath(k.prefix, p);
105
+ if (memberFull !== "" && constraintCovers(full, memberFull)) path = Math.max(path, entry.length);
106
+ else if (memberFull === "" || full.startsWith(`${memberFull}/`)) {
107
+ // Inside the member's directory, and not in a member nested in it.
108
+ const inWorkspace = rootDir === "" ? full : full.startsWith(`${rootDir}/`) ? full.slice(rootDir.length + 1) : null;
109
+ if (inWorkspace !== null && memberHolding(inWorkspace, declaration.members) === member.name) inside = true;
110
+ }
111
+ }
112
+ }
113
+ if (path > 0) return { relevance: "path", specificity: path };
114
+ if (byMember) return { relevance: "member", specificity: 0 };
115
+ return inside ? { relevance: "related", specificity: 0 } : null;
116
+ };
117
+ // Each covering record, and the current record at the end of its supersession chain.
118
+ const current = new Set<string>();
119
+ for (const v of k.records) {
120
+ if (v.id === null || covers(v) === null) continue;
121
+ let at: RecordEntry = v;
122
+ const seen = new Set<string>();
123
+ while (at.supersededBy !== null && !seen.has(at.id!)) {
124
+ seen.add(at.id!);
125
+ const next = k.byId.get(at.supersededBy);
126
+ if (!next) break;
127
+ at = next;
128
+ }
129
+ if (at.supersededBy === null && at.id !== null) current.add(at.id);
130
+ }
131
+ for (const id of current) {
132
+ const v = k.byId.get(id)!;
133
+ // A decision in the list only through the chain is related, as in why.
134
+ const c = covers(v) ?? { relevance: "related" as const, specificity: 0 };
135
+ ranked.push({
136
+ label: `${name}/${id}`,
137
+ key: {
138
+ id: `record:${name}/${id}`,
139
+ current: true,
140
+ relevance: c.relevance,
141
+ lines: 0,
142
+ closed: v.state !== null && (k.loaded.kind.closedStates ?? []).includes(v.state),
143
+ specificity: c.specificity,
144
+ decidedOn: decidedDate(k, v),
145
+ },
146
+ });
147
+ }
148
+ }
149
+ return ranked.sort((a, b) => compareDecisionRank(a.key, b.key)).map((r) => r.label);
150
+ };
151
+ }
@@ -82,6 +82,15 @@ export interface StatusGate {
82
82
  * `release-revoked`. Added by `workspace status`; absent otherwise.
83
83
  */
84
84
  revoked?: ReleaseRevocation;
85
+ /**
86
+ * The current decisions covering the gate's member (#3616), as
87
+ * `<kind>/<id>`, most relevant first: by a `path:` at or above the
88
+ * member's directory, by `member:`, then by a path inside it or through
89
+ * supersession, ranked as the intent graph's `why.decisions`. Read from
90
+ * the records only, without history. Added by `workspace status`; absent
91
+ * otherwise.
92
+ */
93
+ decisions?: string[];
85
94
  /** When the run recorded the pending fact the state is read against. */
86
95
  recordedAt: string;
87
96
  /** The commit the run reached the gate at (#4020), from the pending fact. Absent on a fact written before chant 0.124.28. */
@@ -104,6 +104,7 @@
104
104
  "required": ["name", "root", "file"],
105
105
  "properties": {
106
106
  "name": { "type": "string" },
107
+ "repo": { "type": ["string", "null"], "description": "Added in contract 1 by #3168. The repository the document came from: the URL of the remote origin of the git repository that holds the workspace, with any user name or password removed, or null when there is no origin or the workspace is outside git. With name, root and the revision, it says which repository's workspace a document describes, so a reader that gathers several repositories' documents, such as an org workspace's repo members, tells them apart." },
107
108
  "root": { "type": "string", "description": "The workspace root relative to the git root, with / separators, \".\" for the git root itself." },
108
109
  "file": { "enum": ["chant.workspace.json", "chant.workspace.jsonc"] }
109
110
  }
@@ -987,6 +988,11 @@
987
988
  "changeSet": { "type": "string", "pattern": "^(?:jcs1-)?sha256:[0-9a-f]{64}$", "description": "Added in contract 1 by #4022, written by chant 0.124.28 and newer. The change set the gate binds: planDigest, when a change-set document is stored under it in the member's _change-sets/ on chant/lifecycle, as a pull-request gate's apply and a release gate (#4045) store it. chant change-set show <changeSet> --json prints it, each entry with its graph node. Absent when none is stored, as for a component gate, which binds a lifecycle-plan digest." },
988
989
  "releaseDigest": { "type": "string", "pattern": "^(?:jcs1-)?sha256:[0-9a-f]{64}$", "description": "Added in contract 1 by #4045, written by chant 0.124.28 and newer. The release the gate binds, from the pending fact: set on a release gate, the gate chant components promote --release passes for an environment, whose component is the op release. An approval counts toward it only when it was recorded for this release, environment and planDigest. Absent on every other gate." },
989
990
  "revoked": { "$ref": "#/$defs/revocation", "description": "Added in contract 1 by #4033 (ws-105), written by chant 0.124.29 and newer. Set on a release gate whose release was cut from a commit with a ci/revoked/<sha> tag, judged as release cut judges it: the tag. The gate's state is then superseded, or expired when its pending fact has expired. Absent otherwise." },
991
+ "decisions": {
992
+ "type": "array",
993
+ "items": { "type": "string", "pattern": "^[^/]+/.+$" },
994
+ "description": "Added in contract 1 by #3616, written by chant 0.124.47 and newer. The current decisions covering the gate's member, as <kind>/<id>, most relevant first, so a reader names the decision a gate enforces without a graph --intent walk. A decision covers the member by a path: constraint that is the member's directory or a directory above it, by member:<name>, or by a path: inside the directory that the member holds; a superseded covering decision stands for the current one at the end of its chain. Ranked as the intent graph's why.decisions ranks them (path, then member, then the rest; then closed, longer path, newest decided, id), without carried, issue or contract, which need history. Read from every declared record kind but work kinds, in the working tree; a kind that can't be read is left out. The same list on every gate of a member, and empty when nothing covers it. Absent from an older chant."
995
+ },
990
996
  "expiresAt": { "type": "string" },
991
997
  "approvals": {
992
998
  "description": "The approvals that count toward the gate for its plan, oldest first.",
@@ -50,6 +50,7 @@ import { findWorkspaceRoot } from "../project-root";
50
50
  import { resolveBoxes, type ResolvedIsolation } from "./box-isolation";
51
51
  import { resolveBoxIntents, unresolvedIntent, type BoxIntent } from "./box-intent";
52
52
  import { loadKindRegistry, resolveMemberFields, type MemberFields } from "./kinds";
53
+ import { repoIdentity } from "./repo-ref";
53
54
  import { factoryView, listingView, plantability, treeBytes, type FactoryView, type ListingView, type Plantable } from "./box-factory";
54
55
  import { declaredRecordKinds, extensionsAt, readDeclaration, readerVersion, WorkspaceReadError, type Declaration, type ErrorLocation, type Member } from "./declaration";
55
56
  import type { ReasonCode } from "./reason-codes";
@@ -63,6 +64,7 @@ import { scopeSource } from "./write-scope";
63
64
  import { GATE_REASON_CODES, readMemberGates, type GateLedgerReader, type StatusGate, type StatusGateLedger } from "./status-gates";
64
65
  import { trustQuorumJudge } from "../op/gate-quorum";
65
66
  import { STEWARD_REASON_CODES, readMemberStewards, type StatusSteward, type StewardReasonCode } from "./status-stewards";
67
+ import { memberDecisionsReader, type DecisionKindReader } from "./status-decisions";
66
68
  import { gitTop, workingTree } from "./tree";
67
69
  import { handToRootChant } from "./which-chant";
68
70
 
@@ -387,7 +389,7 @@ export type StatusDocument =
387
389
  lifecycle: { ref: string; commit: string | null };
388
390
  /** The branch, head and base of the checkout read (#3160). `records --uncommitted` lists the records it holds uncommitted. */
389
391
  checkout: CheckoutHead;
390
- workspace: { name: string; root: string; file: string };
392
+ workspace: { name: string; root: string; file: string; repo: string | null };
391
393
  /** Whether a host can plant the workspace as one box (#3146): exactly one member's box block declares services. */
392
394
  plantable: Plantable;
393
395
  members: StatusMember[];
@@ -433,6 +435,8 @@ export interface StatusQuery {
433
435
  readStewards?: typeof readMemberStewards;
434
436
  /** Reads one member's promotion order from its directory; its chant.config.ts unless a test swaps it. */
435
437
  readPromotionOrder?: (dir: string) => Promise<PromotionStep[] | null>;
438
+ /** Reads one record kind for the decisions covering each gate's member (#3616); the working tree unless a test swaps it. */
439
+ readDecisionKind?: DecisionKindReader;
436
440
  }
437
441
 
438
442
  /** A factory view with the declaration's x- keys on it and on its check, publish and tiers (#3595). */
@@ -727,6 +731,9 @@ export async function workspaceStatus(query: StatusQuery): Promise<StatusDocumen
727
731
  const revoked = () => (revokedCommits ??= revokedCommitsAt(top, ruleSource.declaration?.ci?.green ?? null).catch(() => new Map()));
728
732
  // #4024: the flag records, read once for every member. Unreadable records list as null.
729
733
  const flags = await readStatusFlags(declaration, found.dir);
734
+ // #3616: the decisions covering each member, read from the records once and only when a gate is met.
735
+ let coveringReader: ReturnType<typeof memberDecisionsReader> | undefined;
736
+ const covering = () => (coveringReader ??= memberDecisionsReader(declaration, found.dir, rootDir, query.readDecisionKind));
730
737
  const members: StatusMember[] = [];
731
738
  for (const m of declaration.members) {
732
739
  const revokedOf = revokedReleaseLookup(resolve(found.dir, m.dir), revoked);
@@ -748,7 +755,7 @@ export async function workspaceStatus(query: StatusQuery): Promise<StatusDocumen
748
755
  compare: query.compareTo === undefined ? null : compareEnvironments(environments[0], environments[environments.length - 1]),
749
756
  readable: environments.every((e) => e.reason === null),
750
757
  gateLedger: gates.ledger,
751
- gates: await supersedeRevokedGates(gates.gates, revokedOf),
758
+ gates: await withDecisions(await supersedeRevokedGates(gates.gates, revokedOf), m, covering),
752
759
  box:
753
760
  m.box === null
754
761
  ? null
@@ -807,7 +814,7 @@ export async function workspaceStatus(query: StatusQuery): Promise<StatusDocumen
807
814
  compareTo: query.compareTo ?? null,
808
815
  lifecycle: { ref: LIFECYCLE_REF, commit },
809
816
  checkout: readCheckoutHead(top),
810
- workspace: { name: declaration.name, root: rootDir === "" ? "." : rootDir, file: declaration.file },
817
+ workspace: { name: declaration.name, root: rootDir === "" ? "." : rootDir, file: declaration.file, repo: repoIdentity(found.dir) },
811
818
  plantable: plantability(declaration),
812
819
  members,
813
820
  leases,
@@ -889,6 +896,13 @@ async function readStatusFlags(declaration: Declaration, root: string): Promise<
889
896
  }
890
897
  }
891
898
 
899
+ /** Each gate with the current decisions covering its member (#3616), when the member has gates. */
900
+ async function withDecisions(gates: StatusGate[], m: { name: string; dir: string }, covering: () => ReturnType<typeof memberDecisionsReader>): Promise<StatusGate[]> {
901
+ if (gates.length === 0) return gates;
902
+ const decisions = (await covering())(m);
903
+ return gates.map((g) => ({ ...g, decisions: [...decisions] }));
904
+ }
905
+
892
906
  /** The overlap report (#4025); null when no work kind is declared or it can't be read. */
893
907
  async function readStatusOverlap(declaration: Declaration, root: string, top: string, changeSets: OverlapChangeSetRef[]): Promise<OverlapReport | null> {
894
908
  if (declaredRecordKinds(declaration).length === 0) return null;
@@ -0,0 +1,39 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { parseArgs } from "../cli/main";
3
+ import { matchesWhere, parseWhere } from "../read-filter";
4
+ import { filterRecordsDocument, RECORD_FILTER_FIELDS, type RecordsDocument } from "./records-cli";
5
+ import { RUN_FILTER_FIELDS, RunsFilterError, workspaceRuns } from "./runs-cli";
6
+
7
+ describe("--where on records and runs (#4363)", () => {
8
+ it("parses --where as a repeatable flag", () => {
9
+ expect(parseArgs(["ledger", "gates", "--where", "op=tf-apply", "--where", "kind=pending"]).where).toEqual(["op=tf-apply", "kind=pending"]);
10
+ expect(() => parseArgs(["ledger", "gates", "--where"])).toThrow(/--where needs a clause/);
11
+ });
12
+
13
+ it("filters the records a read lists and counts summary over them", () => {
14
+ const rec = (id: string, state: string, valid: boolean, data: Record<string, unknown>) => ({ id, path: `d/${id}.md`, state, valid, data, supersededBy: null, provenance: { level: "unattested" } });
15
+ const doc = {
16
+ $schema: "x",
17
+ contract: 1,
18
+ records: [rec("D-1", "accepted", true, { owner: "ops" }), rec("D-2", "proposed", true, { owner: "dev" }), rec("D-3", "accepted", false, { owner: "dev" })],
19
+ summary: { total: 3, valid: 2, invalid: 1, superseded: 4 },
20
+ } as unknown as RecordsDocument;
21
+ const where = ["state=accepted", "data.owner=dev"];
22
+ const out = filterRecordsDocument(doc, parseWhere(where, RECORD_FILTER_FIELDS), where);
23
+ if ("error" in out) throw new Error("failed");
24
+ expect(out.records.map((r) => r.id)).toEqual(["D-3"]);
25
+ expect(out.summary).toEqual({ total: 1, valid: 0, invalid: 1, superseded: 4 });
26
+ expect(out.where).toEqual(where);
27
+ expect(filterRecordsDocument(doc, [], [])).toBe(doc);
28
+ });
29
+
30
+ it("filters runs on their typed fields", () => {
31
+ const run = { id: "r1", state: "ended", startedAt: "2026-10-22T00:00:00.000Z", model: "opus", unit: { id: "W-1", kind: "work" }, cost: { amount: 1.5, currency: "USD" } };
32
+ expect(matchesWhere(run, parseWhere(["cost.amount>1", "unit.id=W-1", "startedAt>=2026-10-21"], RUN_FILTER_FIELDS))).toBe(true);
33
+ expect(matchesWhere(run, parseWhere(["model!=opus"], RUN_FILTER_FIELDS))).toBe(false);
34
+ });
35
+
36
+ it("refuses a runs --where it can't apply before reading anything", async () => {
37
+ await expect(workspaceRuns({ cwd: "/nonexistent", where: ["tokens>5"] })).rejects.toThrow(RunsFilterError);
38
+ });
39
+ });
@@ -436,7 +436,8 @@ export function formatWorkHistory(doc: Extract<WorkHistoryDocument, { claims: un
436
436
  * <file|->` (#3159): the `workEvidence` activity (#2772) as a command, so a
437
437
  * writer that is not an Op, such as hud, attaches evidence through chant under
438
438
  * the lease it holds. `--from` gives the entry as JSON: criterion, result,
439
- * title, a url or a path, and optionally as_of. It always prints the
439
+ * title, a url, a path or an ops result to cite (#3171), and optionally
440
+ * as_of. It always prints the
440
441
  * work-evidence document; exit 0 when the entry was appended, 1 otherwise.
441
442
  */
442
443
  async function runWorkEvidence(ctx: CommandContext): Promise<number> {
@@ -452,7 +453,7 @@ async function runWorkEvidence(ctx: CommandContext): Promise<number> {
452
453
  if (!id) return fail("evidence needs a work item id");
453
454
  if (!args.holder) return fail("evidence needs --holder <name>: the lease's holder");
454
455
  if (!args.token) return fail("evidence needs --token <token>: the lease's fencing token, as its claim printed it");
455
- if (args.migrateFrom === undefined) return fail("evidence needs --from <file|->: the entry as JSON, with criterion, result, title and a url or a path");
456
+ if (args.migrateFrom === undefined) return fail("evidence needs --from <file|->: the entry as JSON, with criterion, result, title and a url, a path or an ops result");
456
457
  for (const [flag, v] of [["--ttl", args.ttl], ["--outcome", args.outcome], ["--note", args.note]] as const) {
457
458
  if (v !== undefined) return fail(`evidence takes no ${flag}`);
458
459
  }
@@ -470,7 +471,7 @@ async function runWorkEvidence(ctx: CommandContext): Promise<number> {
470
471
  1,
471
472
  );
472
473
  }
473
- const known = ["criterion", "result", "title", "url", "path", "as_of"];
474
+ const known = ["criterion", "result", "title", "url", "path", "ops", "as_of"];
474
475
  const unknown = Object.keys(fields).filter((k) => !known.includes(k));
475
476
  if (unknown.length > 0) return fail(`the entry takes ${known.join(", ")}, not ${unknown.join(", ")}`);
476
477
  const str = (k: string): string | undefined => (typeof fields[k] === "string" ? (fields[k] as string) : undefined);
@@ -485,6 +486,7 @@ async function runWorkEvidence(ctx: CommandContext): Promise<number> {
485
486
  title: str("title") ?? "",
486
487
  url: str("url"),
487
488
  path: str("path"),
489
+ ops: fields.ops as never,
488
490
  asOf: str("as_of"),
489
491
  });
490
492
  return print(doc, "error" in doc ? 1 : 0);
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Work evidence that cites an ops result (#3171), on a workspace built from
3
+ * the reference workspace's work kind in a throwaway git repository.
4
+ *
5
+ * W-010 is in progress with a runtime criterion, "the web component runs
6
+ * green in staging". The staging run ledger of `deploy-web` holds an `ok`
7
+ * run, a `fail` run and a gated one. The run holding W-010's lease cites the
8
+ * ok run, the citation verifies against the ledger, and the criterion is met.
9
+ * The others, and citations the ledger does not hold, are refused and
10
+ * nothing is written.
11
+ */
12
+
13
+ import { join } from "node:path";
14
+ import { afterAll, beforeAll, describe, expect, test } from "vitest";
15
+ import { readFileSync } from "node:fs";
16
+ import { cleanScratch, contract, git, REPO, repo } from "./__fixtures__/contract-repo";
17
+ import { queryRecords } from "./records-cli";
18
+ import recordsSchema from "./records.schema.json";
19
+ import { attachWorkEvidence } from "./work-evidence";
20
+ import workEvidenceSchema from "./work-evidence.schema.json";
21
+ import { appendRunRecord } from "../lifecycle/run-ledger";
22
+ import { appendGateResolution } from "../lifecycle/gate-ledger";
23
+ import { persistReleasePlan } from "../lifecycle/plan-ledger";
24
+ import { claimWorkLease, releaseWorkLease } from "../lifecycle/work-lease";
25
+ import type { OpRunRecordInput } from "../op/runtime";
26
+
27
+ const REF = join(REPO, "reference-workspace");
28
+ const KIND = "work/work.kind.mjs";
29
+
30
+ function work(id: string, fields: Record<string, unknown>): string {
31
+ const data = {
32
+ schema: 1,
33
+ id,
34
+ title: `Work ${id}`,
35
+ state: "in-progress",
36
+ implements: [],
37
+ needs: [],
38
+ constrains: ["member:app"],
39
+ evidence: [],
40
+ owner: "alice",
41
+ opened_on: "2026-10-23",
42
+ closed_on: null,
43
+ source: { kind: "workspace", member: "app" },
44
+ supersedes: [],
45
+ ...fields,
46
+ };
47
+ return `---\n${JSON.stringify(data, null, 2)}\n---\n\n# ${id}\n`;
48
+ }
49
+
50
+ const run = (id: string, status: OpRunRecordInput["status"]): OpRunRecordInput => ({
51
+ id,
52
+ op: "deploy-web",
53
+ env: "staging",
54
+ started: "2026-10-23T10:00:00.000Z",
55
+ ended: "2026-10-23T10:05:00.000Z",
56
+ status,
57
+ labels: { Env: "staging" },
58
+ outcomes: {},
59
+ phases: [{ name: "Apply", status: status === "fail" ? "fail" : "ok", steps: [] }],
60
+ });
61
+
62
+ let root: string;
63
+ let token: string;
64
+ const evidenceDoc = contract(workEvidenceSchema);
65
+ const records = contract(recordsSchema);
66
+
67
+ beforeAll(async () => {
68
+ root = repo({
69
+ "chant.workspace.json": JSON.stringify({
70
+ name: "studio",
71
+ schema: 1,
72
+ members: [{ name: "app", dir: "app", kind: "other", because: "a plain Node server" }],
73
+ records: [{ kind: "decisions/decision.kind.mjs" }, { kind: KIND }],
74
+ }),
75
+ "app/server.mjs": "export const port = 8080;\n",
76
+ "decisions/decision.kind.mjs": readFileSync(join(REF, "decisions/decision.kind.mjs"), "utf-8"),
77
+ "decisions/decision.schema.json": readFileSync(join(REF, "decisions/decision.schema.json"), "utf-8"),
78
+ [KIND]: readFileSync(join(REF, KIND), "utf-8"),
79
+ "work/work.schema.json": readFileSync(join(REF, "work/work.schema.json"), "utf-8"),
80
+ "work/W-010-staging.md": work("W-010", {
81
+ acceptance: [
82
+ { id: "AC-1", text: "The web component runs green in staging", verification: "runtime" },
83
+ { id: "AC-2", text: "The release plan for staging has no deletes", verification: "runtime" },
84
+ ],
85
+ }),
86
+ });
87
+ git(root, "config", "user.name", "t");
88
+ git(root, "config", "user.email", "t@example.com");
89
+ git(root, "config", "commit.gpgsign", "false");
90
+ git(root, "add", "-A");
91
+ git(root, "commit", "-q", "-m", "the work");
92
+ for (const [id, status] of [["r-ok", "ok"], ["r-fail", "fail"], ["r-gated", "gated"]] as const) await appendRunRecord(run(id, status), { cwd: root });
93
+ await appendGateResolution({ op: "deploy-web", gate: "promote", resolvedBy: "bob", timestamp: "2026-10-23T09:00:00.000Z" }, { cwd: root });
94
+ await persistReleasePlan({ digest: "sha256:abc", deletes: [] }, { cwd: root });
95
+ const claimed = await claimWorkLease("W-010", "box/verify@h1", { cwd: join(root, "work") });
96
+ if (!claimed.ok) throw new Error(claimed.message);
97
+ token = claimed.lease.token;
98
+ });
99
+
100
+ afterAll(async () => {
101
+ await releaseWorkLease("W-010", "box/verify@h1", { cwd: join(root, "work"), token });
102
+ cleanScratch();
103
+ });
104
+
105
+ const cite = (over: Partial<Parameters<typeof attachWorkEvidence>[0]>) =>
106
+ attachWorkEvidence({ cwd: root, item: "W-010", holder: "box/verify@h1", token, criterion: "AC-1", result: "pass", title: "staging run", ...over });
107
+
108
+ async function w10() {
109
+ const doc = await queryRecords({ kind: join(root, KIND), cwd: root, workGaps: false });
110
+ records.expectValid(doc);
111
+ if ("error" in doc || !("records" in doc)) throw new Error(JSON.stringify(doc));
112
+ return doc.records.find((r) => r.id === "W-010")!;
113
+ }
114
+
115
+ describe("work evidence cites an ops result (#3171)", () => {
116
+ test("refusals write nothing: a run the ledger lacks, a failed run as a pass, a gated run, a wrong hash, two kinds of evidence", async () => {
117
+ const refused = async (over: Partial<Parameters<typeof attachWorkEvidence>[0]>, code: string) => {
118
+ const doc = await cite(over);
119
+ evidenceDoc.expectValid(doc);
120
+ expect(doc).toMatchObject({ error: { code } });
121
+ };
122
+ await refused({ ops: { ledger: "run", env: "staging", op: "deploy-web", id: "r-nope" } }, "work-evidence-ops-missing");
123
+ await refused({ ops: { ledger: "run", env: "prod", op: "deploy-web", id: "r-ok" } }, "work-evidence-ops-missing");
124
+ await refused({ ops: { ledger: "run", env: "staging", op: "deploy-web", id: "r-fail" } }, "work-evidence-ops-mismatch");
125
+ await refused({ ops: { ledger: "run", env: "staging", op: "deploy-web", id: "r-gated" } }, "work-evidence-ops-unfinished");
126
+ await refused({ ops: { ledger: "run", env: "staging", op: "deploy-web", id: "r-ok", sha256: "0".repeat(64) } }, "work-evidence-ops-mismatch");
127
+ await refused({ ops: { ledger: "run", env: "../x", op: "deploy-web", id: "r-ok" } as never }, "write-input-invalid");
128
+ await refused({ ops: { ledger: "run", env: "staging", op: "deploy-web", id: "r-ok" }, url: "https://example.com/x" }, "write-usage-invalid");
129
+ expect((await w10()).acceptance).toMatchObject({ met: 0, total: 2 });
130
+ });
131
+
132
+ test("a failed run cited as failing evidence is written, and does not meet the criterion", async () => {
133
+ const doc = await cite({ result: "fail", ops: { ledger: "run", env: "staging", op: "deploy-web", id: "r-fail" } });
134
+ evidenceDoc.expectValid(doc);
135
+ expect(doc).toMatchObject({ evidence: { ops: { ledger: "run", outcome: "fail" }, result: "fail" }, acceptance: { met: 0 } });
136
+ });
137
+
138
+ test("an ok run in staging ticks the runtime criterion, and the entry names the ledger line by its hash", async () => {
139
+ const doc = await cite({ ops: { ledger: "run", env: "staging", op: "deploy-web", id: "r-ok" } });
140
+ evidenceDoc.expectValid(doc);
141
+ if ("error" in doc) throw new Error(doc.error.message);
142
+ const line = git(root, "show", "chant/lifecycle:staging/runs__deploy-web.jsonl")
143
+ .split("\n")
144
+ .find((l) => l.includes('"r-ok"'))!
145
+ .trim();
146
+ const { createHash } = await import("node:crypto");
147
+ expect(doc.evidence).toMatchObject({
148
+ criterion: "AC-1",
149
+ verification: "runtime",
150
+ result: "pass",
151
+ by: "box/verify@h1",
152
+ ops: { ledger: "run", ref: "chant/lifecycle:staging/runs__deploy-web.jsonl", field: "id", value: "r-ok", env: "staging", outcome: "ok", sha256: createHash("sha256").update(line).digest("hex") },
153
+ });
154
+ expect(doc.acceptance).toMatchObject({ met: 1, total: 2 });
155
+ // The record still validates against the reference work schema with the ops entry in it.
156
+ const rec = await w10();
157
+ expect(rec.valid).toBe(true);
158
+ expect(rec.acceptance).toMatchObject({ met: 1, total: 2 });
159
+
160
+ // Citing it again with the hash it was given verifies too.
161
+ const again = await cite({ ops: { ledger: "run", env: "staging", op: "deploy-web", id: "r-ok", sha256: (doc.evidence.ops as { sha256: string }).sha256 } });
162
+ expect("error" in again).toBe(false);
163
+ });
164
+
165
+ test("a release plan and a gate answer can be cited, and a plan or gate the branch lacks is refused", async () => {
166
+ const plan = await cite({ criterion: "AC-2", title: "the staging plan", ops: { ledger: "plan", digest: "sha256:abc" } });
167
+ evidenceDoc.expectValid(plan);
168
+ expect(plan).toMatchObject({ evidence: { ops: { ledger: "plan", ref: "chant/lifecycle:_plans/sha256_abc.json", field: "digest", value: "sha256:abc" } }, acceptance: { met: 2, total: 2 } });
169
+ const gate = await cite({ criterion: "AC-2", title: "promote approved", ops: { ledger: "gate", op: "deploy-web", gate: "promote", at: "2026-10-23T09:00:00.000Z" } });
170
+ expect(gate).toMatchObject({ evidence: { ops: { ledger: "gate", ref: "chant/lifecycle:_gates/deploy-web.jsonl", field: "timestamp" } } });
171
+ expect(await cite({ ops: { ledger: "plan", digest: "sha256:nope" } })).toMatchObject({ error: { code: "work-evidence-ops-missing" } });
172
+ expect(await cite({ ops: { ledger: "gate", op: "deploy-web", gate: "promote", at: "2026-01-01T00:00:00.000Z" } })).toMatchObject({ error: { code: "work-evidence-ops-missing" } });
173
+ expect(await cite({ ops: { ledger: "deploy", env: "staging", runId: "r-ok" } })).toMatchObject({ error: { code: "work-evidence-ops-missing" } });
174
+ expect((await w10()).valid).toBe(true);
175
+ });
176
+ });