@intentius/chant 0.124.27 → 0.124.29

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 (681) hide show
  1. package/README.md +1 -1
  2. package/dist/__golden__/wave-loop/fan-out-gated.json +8 -0
  3. package/dist/__golden__/wave-loop/fan-out-no-members.json +4 -0
  4. package/dist/__golden__/wave-loop/fan-out-only.json +6 -0
  5. package/dist/__golden__/wave-loop/op-by-apply-hold.json +2 -0
  6. package/dist/__golden__/wave-loop/op-cost-member.json +3 -0
  7. package/dist/__golden__/wave-loop/op-held-on-before-apply.json +1 -0
  8. package/dist/__golden__/wave-loop/op-held-on.json +2 -0
  9. package/dist/__golden__/wave-loop/op-hooks-held.json +2 -0
  10. package/dist/__golden__/wave-loop/op-hooks-hold-before-apply.json +2 -0
  11. package/dist/__golden__/wave-loop/op-hooks-refused-at-base.json +3 -0
  12. package/dist/__golden__/wave-loop/op-hooks-share.json +3 -0
  13. package/dist/__golden__/wave-loop/op-per-run.json +7 -0
  14. package/dist/__golden__/wave-loop/op-pr-review.json +7 -0
  15. package/dist/__golden__/wave-loop/op-sealed.json +3 -0
  16. package/dist/__golden__/wave-loop/op-spend.json +3 -0
  17. package/dist/__golden__/wave-loop/op-wave-commands.json +7 -0
  18. package/dist/__golden__/wave-loop/op-wide-waiting.json +4 -0
  19. package/dist/build-params.d.ts +34 -1
  20. package/dist/build-params.d.ts.map +1 -1
  21. package/dist/build-params.js +31 -1
  22. package/dist/change-set-graph.d.ts +63 -0
  23. package/dist/change-set-graph.d.ts.map +1 -0
  24. package/dist/change-set-graph.js +130 -0
  25. package/dist/change-set.d.ts +62 -3
  26. package/dist/change-set.d.ts.map +1 -1
  27. package/dist/change-set.js +36 -4
  28. package/dist/chant-version.d.ts +9 -0
  29. package/dist/chant-version.d.ts.map +1 -0
  30. package/dist/chant-version.js +16 -0
  31. package/dist/cli/build-params-cli.d.ts +7 -1
  32. package/dist/cli/build-params-cli.d.ts.map +1 -1
  33. package/dist/cli/build-params-cli.js +17 -8
  34. package/dist/cli/commands/build.d.ts +8 -0
  35. package/dist/cli/commands/build.d.ts.map +1 -1
  36. package/dist/cli/commands/build.js +15 -11
  37. package/dist/cli/commands/check-lexicon-docs.d.ts.map +1 -1
  38. package/dist/cli/commands/check-lexicon-docs.js +30 -2
  39. package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
  40. package/dist/cli/commands/check-lexicon.js +13 -2
  41. package/dist/cli/commands/init-lexicon/templates/docs.d.ts +4 -1
  42. package/dist/cli/commands/init-lexicon/templates/docs.d.ts.map +1 -1
  43. package/dist/cli/commands/init-lexicon/templates/docs.js +6 -6
  44. package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
  45. package/dist/cli/commands/init-lexicon.js +17 -8
  46. package/dist/cli/handlers/approve.d.ts.map +1 -1
  47. package/dist/cli/handlers/approve.js +64 -4
  48. package/dist/cli/handlers/build.d.ts.map +1 -1
  49. package/dist/cli/handlers/build.js +43 -4
  50. package/dist/cli/handlers/change-set.d.ts +10 -0
  51. package/dist/cli/handlers/change-set.d.ts.map +1 -1
  52. package/dist/cli/handlers/change-set.js +51 -1
  53. package/dist/cli/handlers/fan-out.d.ts +10 -1
  54. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  55. package/dist/cli/handlers/fan-out.js +54 -6
  56. package/dist/cli/handlers/lifecycle-gc.d.ts +19 -0
  57. package/dist/cli/handlers/lifecycle-gc.d.ts.map +1 -0
  58. package/dist/cli/handlers/lifecycle-gc.js +143 -0
  59. package/dist/cli/handlers/pr.d.ts +16 -2
  60. package/dist/cli/handlers/pr.d.ts.map +1 -1
  61. package/dist/cli/handlers/pr.js +269 -37
  62. package/dist/cli/handlers/promote.d.ts +7 -0
  63. package/dist/cli/handlers/promote.d.ts.map +1 -1
  64. package/dist/cli/handlers/promote.js +440 -28
  65. package/dist/cli/handlers/release.d.ts +91 -0
  66. package/dist/cli/handlers/release.d.ts.map +1 -0
  67. package/dist/cli/handlers/release.js +589 -0
  68. package/dist/cli/handlers/run.d.ts +38 -2
  69. package/dist/cli/handlers/run.d.ts.map +1 -1
  70. package/dist/cli/handlers/run.js +234 -16
  71. package/dist/cli/main.d.ts.map +1 -1
  72. package/dist/cli/main.js +143 -17
  73. package/dist/cli/mcp/workspace-tools.js +1 -1
  74. package/dist/cli/parse-args.d.ts.map +1 -1
  75. package/dist/cli/parse-args.js +73 -5
  76. package/dist/cli/registry.d.ts +43 -5
  77. package/dist/cli/registry.d.ts.map +1 -1
  78. package/dist/cli/run-events.d.ts +30 -0
  79. package/dist/cli/run-events.d.ts.map +1 -0
  80. package/dist/cli/run-events.js +49 -0
  81. package/dist/cli/version.d.ts +3 -5
  82. package/dist/cli/version.d.ts.map +1 -1
  83. package/dist/cli/version.js +3 -13
  84. package/dist/codegen/docs-in-site.d.ts +71 -0
  85. package/dist/codegen/docs-in-site.d.ts.map +1 -0
  86. package/dist/codegen/docs-in-site.js +177 -0
  87. package/dist/codegen/docs-types.d.ts +14 -1
  88. package/dist/codegen/docs-types.d.ts.map +1 -1
  89. package/dist/codegen/docs.d.ts +18 -1
  90. package/dist/codegen/docs.d.ts.map +1 -1
  91. package/dist/codegen/docs.js +40 -7
  92. package/dist/codegen/rehype-base-url.mjs +15 -9
  93. package/dist/components/auto-release.d.ts +6 -1
  94. package/dist/components/auto-release.d.ts.map +1 -1
  95. package/dist/components/auto-release.js +3 -0
  96. package/dist/components/capability.d.ts +9 -0
  97. package/dist/components/capability.d.ts.map +1 -1
  98. package/dist/components/cli-support.d.ts +6 -0
  99. package/dist/components/cli-support.d.ts.map +1 -1
  100. package/dist/components/cli-support.js +10 -9
  101. package/dist/components/driver.d.ts +50 -9
  102. package/dist/components/driver.d.ts.map +1 -1
  103. package/dist/components/driver.js +116 -54
  104. package/dist/components/fan-out-inputs.d.ts +69 -0
  105. package/dist/components/fan-out-inputs.d.ts.map +1 -0
  106. package/dist/components/fan-out-inputs.js +169 -0
  107. package/dist/components/fan-out-record.d.ts +13 -0
  108. package/dist/components/fan-out-record.d.ts.map +1 -1
  109. package/dist/components/fan-out-record.js +16 -0
  110. package/dist/components/fan-out-run.d.ts +7 -0
  111. package/dist/components/fan-out-run.d.ts.map +1 -1
  112. package/dist/components/fan-out-run.js +85 -26
  113. package/dist/components/fan-out.d.ts +4 -0
  114. package/dist/components/fan-out.d.ts.map +1 -1
  115. package/dist/components/pr-pipeline.d.ts +25 -0
  116. package/dist/components/pr-pipeline.d.ts.map +1 -1
  117. package/dist/components/pr-pipeline.js +22 -4
  118. package/dist/components/pr-run.d.ts +11 -0
  119. package/dist/components/pr-run.d.ts.map +1 -1
  120. package/dist/components/pr-run.js +20 -1
  121. package/dist/components/promote-order.d.ts +38 -0
  122. package/dist/components/promote-order.d.ts.map +1 -0
  123. package/dist/components/promote-order.js +62 -0
  124. package/dist/components/promote.d.ts +38 -2
  125. package/dist/components/promote.d.ts.map +1 -1
  126. package/dist/components/promote.js +77 -2
  127. package/dist/components/release-compile.d.ts +142 -0
  128. package/dist/components/release-compile.d.ts.map +1 -0
  129. package/dist/components/release-compile.js +382 -0
  130. package/dist/components/release-gate.d.ts +135 -0
  131. package/dist/components/release-gate.d.ts.map +1 -0
  132. package/dist/components/release-gate.js +288 -0
  133. package/dist/components/release-promote.d.ts +96 -0
  134. package/dist/components/release-promote.d.ts.map +1 -0
  135. package/dist/components/release-promote.js +204 -0
  136. package/dist/components/run-progress.d.ts +7 -0
  137. package/dist/components/run-progress.d.ts.map +1 -1
  138. package/dist/components/wave-plan.d.ts +8 -1
  139. package/dist/components/wave-plan.d.ts.map +1 -1
  140. package/dist/components/wave-plan.js +11 -3
  141. package/dist/config.d.ts +45 -0
  142. package/dist/config.d.ts.map +1 -1
  143. package/dist/config.js +3 -0
  144. package/dist/flag-params.d.ts +54 -0
  145. package/dist/flag-params.d.ts.map +1 -0
  146. package/dist/flag-params.js +130 -0
  147. package/dist/index.d.ts +1 -0
  148. package/dist/index.d.ts.map +1 -1
  149. package/dist/index.js +2 -0
  150. package/dist/lifecycle/__fixtures__/publish-fakes.d.ts +120 -0
  151. package/dist/lifecycle/__fixtures__/publish-fakes.d.ts.map +1 -0
  152. package/dist/lifecycle/__fixtures__/publish-fakes.js +304 -0
  153. package/dist/lifecycle/change-set-store.d.ts +68 -0
  154. package/dist/lifecycle/change-set-store.d.ts.map +1 -0
  155. package/dist/lifecycle/change-set-store.js +97 -0
  156. package/dist/lifecycle/commit-sha.d.ts +13 -0
  157. package/dist/lifecycle/commit-sha.d.ts.map +1 -0
  158. package/dist/lifecycle/commit-sha.js +21 -0
  159. package/dist/lifecycle/delivery-metrics.d.ts +139 -0
  160. package/dist/lifecycle/delivery-metrics.d.ts.map +1 -0
  161. package/dist/lifecycle/delivery-metrics.js +145 -0
  162. package/dist/lifecycle/deploy-outcome.d.ts +81 -0
  163. package/dist/lifecycle/deploy-outcome.d.ts.map +1 -0
  164. package/dist/lifecycle/deploy-outcome.js +101 -0
  165. package/dist/lifecycle/gate-change-set.d.ts +78 -0
  166. package/dist/lifecycle/gate-change-set.d.ts.map +1 -0
  167. package/dist/lifecycle/gate-change-set.js +138 -0
  168. package/dist/lifecycle/gate-ledger.d.ts +51 -4
  169. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  170. package/dist/lifecycle/gate-ledger.js +18 -0
  171. package/dist/lifecycle/git.d.ts +30 -1
  172. package/dist/lifecycle/git.d.ts.map +1 -1
  173. package/dist/lifecycle/git.js +31 -5
  174. package/dist/lifecycle/index.d.ts +4 -0
  175. package/dist/lifecycle/index.d.ts.map +1 -1
  176. package/dist/lifecycle/index.js +4 -0
  177. package/dist/lifecycle/mixed-environment.d.ts +95 -0
  178. package/dist/lifecycle/mixed-environment.d.ts.map +1 -0
  179. package/dist/lifecycle/mixed-environment.js +154 -0
  180. package/dist/lifecycle/publish-attachments.d.ts +79 -0
  181. package/dist/lifecycle/publish-attachments.d.ts.map +1 -0
  182. package/dist/lifecycle/publish-attachments.js +179 -0
  183. package/dist/lifecycle/publish-credentials.d.ts +37 -0
  184. package/dist/lifecycle/publish-credentials.d.ts.map +1 -0
  185. package/dist/lifecycle/publish-credentials.js +69 -0
  186. package/dist/lifecycle/publish-declaration.d.ts +97 -0
  187. package/dist/lifecycle/publish-declaration.d.ts.map +1 -0
  188. package/dist/lifecycle/publish-declaration.js +83 -0
  189. package/dist/lifecycle/publish-forge.d.ts +53 -0
  190. package/dist/lifecycle/publish-forge.d.ts.map +1 -0
  191. package/dist/lifecycle/publish-forge.js +298 -0
  192. package/dist/lifecycle/publish-npm.d.ts +61 -0
  193. package/dist/lifecycle/publish-npm.d.ts.map +1 -0
  194. package/dist/lifecycle/publish-npm.js +242 -0
  195. package/dist/lifecycle/publish-oci.d.ts +64 -0
  196. package/dist/lifecycle/publish-oci.d.ts.map +1 -0
  197. package/dist/lifecycle/publish-oci.js +383 -0
  198. package/dist/lifecycle/publish-record.d.ts +66 -0
  199. package/dist/lifecycle/publish-record.d.ts.map +1 -0
  200. package/dist/lifecycle/publish-record.js +84 -0
  201. package/dist/lifecycle/publish-targets.d.ts +45 -0
  202. package/dist/lifecycle/publish-targets.d.ts.map +1 -0
  203. package/dist/lifecycle/publish-targets.js +85 -0
  204. package/dist/lifecycle/release-cut.d.ts +154 -0
  205. package/dist/lifecycle/release-cut.d.ts.map +1 -0
  206. package/dist/lifecycle/release-cut.js +366 -0
  207. package/dist/lifecycle/release-join.d.ts +95 -0
  208. package/dist/lifecycle/release-join.d.ts.map +1 -0
  209. package/dist/lifecycle/release-join.js +139 -0
  210. package/dist/lifecycle/release-ledger.d.ts +54 -0
  211. package/dist/lifecycle/release-ledger.d.ts.map +1 -1
  212. package/dist/lifecycle/release-ledger.js +53 -20
  213. package/dist/lifecycle/release-publish.d.ts +312 -0
  214. package/dist/lifecycle/release-publish.d.ts.map +1 -0
  215. package/dist/lifecycle/release-publish.js +335 -0
  216. package/dist/lifecycle/release-revocation.d.ts +80 -0
  217. package/dist/lifecycle/release-revocation.d.ts.map +1 -0
  218. package/dist/lifecycle/release-revocation.js +123 -0
  219. package/dist/lifecycle/release-store.d.ts +200 -0
  220. package/dist/lifecycle/release-store.d.ts.map +1 -0
  221. package/dist/lifecycle/release-store.js +320 -0
  222. package/dist/lifecycle/release-verify.d.ts +61 -0
  223. package/dist/lifecycle/release-verify.d.ts.map +1 -0
  224. package/dist/lifecycle/release-verify.js +92 -0
  225. package/dist/lifecycle/release-workstation.d.ts +91 -0
  226. package/dist/lifecycle/release-workstation.d.ts.map +1 -0
  227. package/dist/lifecycle/release-workstation.js +180 -0
  228. package/dist/lifecycle/retention-policy.d.ts +45 -0
  229. package/dist/lifecycle/retention-policy.d.ts.map +1 -0
  230. package/dist/lifecycle/retention-policy.js +43 -0
  231. package/dist/lifecycle/retention.d.ts +185 -0
  232. package/dist/lifecycle/retention.d.ts.map +1 -0
  233. package/dist/lifecycle/retention.js +400 -0
  234. package/dist/lifecycle/run-ledger.d.ts +4 -0
  235. package/dist/lifecycle/run-ledger.d.ts.map +1 -1
  236. package/dist/lifecycle/run-ledger.js +2 -0
  237. package/dist/op/activities/change-coverage.d.ts.map +1 -1
  238. package/dist/op/activities/change-coverage.js +4 -2
  239. package/dist/op/activities/source-release.d.ts.map +1 -1
  240. package/dist/op/activities/source-release.js +7 -3
  241. package/dist/op/activities/source-rollback.d.ts.map +1 -1
  242. package/dist/op/activities/source-rollback.js +7 -3
  243. package/dist/op/gate-name.d.ts +11 -2
  244. package/dist/op/gate-name.d.ts.map +1 -1
  245. package/dist/op/gate-name.js +12 -3
  246. package/dist/op/gate-run-locator.d.ts +22 -0
  247. package/dist/op/gate-run-locator.d.ts.map +1 -1
  248. package/dist/op/gate-run-locator.js +31 -0
  249. package/dist/op/gate.d.ts +27 -3
  250. package/dist/op/gate.d.ts.map +1 -1
  251. package/dist/op/gate.js +57 -3
  252. package/dist/op/local-executor.d.ts +36 -1
  253. package/dist/op/local-executor.d.ts.map +1 -1
  254. package/dist/op/local-executor.js +158 -52
  255. package/dist/op/op-waves-run.d.ts.map +1 -1
  256. package/dist/op/op-waves-run.js +4 -0
  257. package/dist/op/run-live.d.ts +2 -0
  258. package/dist/op/run-live.d.ts.map +1 -1
  259. package/dist/op/run-live.js +1 -1
  260. package/dist/op/runtime.d.ts +46 -0
  261. package/dist/op/runtime.d.ts.map +1 -1
  262. package/dist/op/runtimes/local.d.ts.map +1 -1
  263. package/dist/op/runtimes/local.js +8 -0
  264. package/dist/plan-summary.d.ts.map +1 -1
  265. package/dist/plan-summary.js +8 -2
  266. package/dist/point-decision.d.ts +2 -0
  267. package/dist/point-decision.d.ts.map +1 -1
  268. package/dist/point-decision.js +9 -2
  269. package/dist/pr-loop.d.ts +7 -0
  270. package/dist/pr-loop.d.ts.map +1 -1
  271. package/dist/pr-loop.js +23 -4
  272. package/dist/pr-report-url.d.ts +82 -0
  273. package/dist/pr-report-url.d.ts.map +1 -0
  274. package/dist/pr-report-url.js +174 -0
  275. package/dist/provenance.d.ts +2 -2
  276. package/dist/provenance.d.ts.map +1 -1
  277. package/dist/run-event-store.d.ts +104 -0
  278. package/dist/run-event-store.d.ts.map +1 -0
  279. package/dist/run-event-store.js +251 -0
  280. package/dist/run-event-stream.d.ts +133 -0
  281. package/dist/run-event-stream.d.ts.map +1 -0
  282. package/dist/run-event-stream.js +242 -0
  283. package/dist/run-event.d.ts +285 -0
  284. package/dist/run-event.d.ts.map +1 -0
  285. package/dist/run-event.js +209 -0
  286. package/dist/wave-runtime.mjs +262 -226
  287. package/dist/workspace/__fixtures__/flag-workspace.d.ts +31 -0
  288. package/dist/workspace/__fixtures__/flag-workspace.d.ts.map +1 -0
  289. package/dist/workspace/__fixtures__/flag-workspace.js +114 -0
  290. package/dist/workspace/__fixtures__/run-event-check.d.ts +19 -0
  291. package/dist/workspace/__fixtures__/run-event-check.d.ts.map +1 -0
  292. package/dist/workspace/__fixtures__/run-event-check.js +59 -0
  293. package/dist/workspace/change-set-show.d.ts +56 -0
  294. package/dist/workspace/change-set-show.d.ts.map +1 -0
  295. package/dist/workspace/change-set-show.js +54 -0
  296. package/dist/workspace/change-set-show.schema.json +102 -0
  297. package/dist/workspace/change-set.schema.json +32 -1
  298. package/dist/workspace/changes-cli.d.ts.map +1 -1
  299. package/dist/workspace/changes-cli.js +10 -1
  300. package/dist/workspace/changes.d.ts +37 -0
  301. package/dist/workspace/changes.d.ts.map +1 -1
  302. package/dist/workspace/changes.js +49 -2
  303. package/dist/workspace/changes.schema.json +135 -0
  304. package/dist/workspace/check.schema.json +4 -2
  305. package/dist/workspace/checks/records.d.ts +9 -1
  306. package/dist/workspace/checks/records.d.ts.map +1 -1
  307. package/dist/workspace/checks/records.js +35 -1
  308. package/dist/workspace/checks.d.ts +1 -0
  309. package/dist/workspace/checks.d.ts.map +1 -1
  310. package/dist/workspace/checks.js +2 -1
  311. package/dist/workspace/ci-green-cli.d.ts +19 -4
  312. package/dist/workspace/ci-green-cli.d.ts.map +1 -1
  313. package/dist/workspace/ci-green-cli.js +75 -12
  314. package/dist/workspace/ci-green-forge.d.ts +20 -3
  315. package/dist/workspace/ci-green-forge.d.ts.map +1 -1
  316. package/dist/workspace/ci-green-forge.js +6 -4
  317. package/dist/workspace/ci-green-workflow.d.ts +11 -0
  318. package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
  319. package/dist/workspace/ci-green-workflow.js +6 -0
  320. package/dist/workspace/ci-green.d.ts +281 -13
  321. package/dist/workspace/ci-green.d.ts.map +1 -1
  322. package/dist/workspace/ci-green.js +456 -38
  323. package/dist/workspace/ci-last-green.schema.json +32 -2
  324. package/dist/workspace/ci-list.schema.json +525 -0
  325. package/dist/workspace/commit-links.d.ts +85 -0
  326. package/dist/workspace/commit-links.d.ts.map +1 -0
  327. package/dist/workspace/commit-links.js +163 -0
  328. package/dist/workspace/declaration.d.ts +35 -0
  329. package/dist/workspace/declaration.d.ts.map +1 -1
  330. package/dist/workspace/declaration.js +27 -1
  331. package/dist/workspace/declaration.schema.json +76 -0
  332. package/dist/workspace/declared-kinds.d.ts +16 -0
  333. package/dist/workspace/declared-kinds.d.ts.map +1 -1
  334. package/dist/workspace/declared-kinds.js +25 -0
  335. package/dist/workspace/delivery-metrics.d.ts +91 -0
  336. package/dist/workspace/delivery-metrics.d.ts.map +1 -0
  337. package/dist/workspace/delivery-metrics.js +217 -0
  338. package/dist/workspace/delivery-metrics.schema.json +519 -0
  339. package/dist/workspace/flags-read.d.ts +37 -0
  340. package/dist/workspace/flags-read.d.ts.map +1 -0
  341. package/dist/workspace/flags-read.js +103 -0
  342. package/dist/workspace/flags.d.ts +100 -0
  343. package/dist/workspace/flags.d.ts.map +1 -0
  344. package/dist/workspace/flags.js +98 -0
  345. package/dist/workspace/graph-cli.js +1 -1
  346. package/dist/workspace/identity-gate-rule.d.ts +37 -0
  347. package/dist/workspace/identity-gate-rule.d.ts.map +1 -1
  348. package/dist/workspace/identity-gate-rule.js +72 -7
  349. package/dist/workspace/identity.d.ts +12 -0
  350. package/dist/workspace/identity.d.ts.map +1 -1
  351. package/dist/workspace/identity.js +13 -1
  352. package/dist/workspace/intent-cli.d.ts +11 -2
  353. package/dist/workspace/intent-cli.d.ts.map +1 -1
  354. package/dist/workspace/intent-cli.js +54 -5
  355. package/dist/workspace/intent-trace.d.ts +219 -0
  356. package/dist/workspace/intent-trace.d.ts.map +1 -0
  357. package/dist/workspace/intent-trace.js +314 -0
  358. package/dist/workspace/intent.d.ts +36 -7
  359. package/dist/workspace/intent.d.ts.map +1 -1
  360. package/dist/workspace/intent.js +192 -22
  361. package/dist/workspace/intent.schema.json +702 -6
  362. package/dist/workspace/ls.d.ts +4 -0
  363. package/dist/workspace/ls.d.ts.map +1 -1
  364. package/dist/workspace/ls.js +2 -0
  365. package/dist/workspace/ls.schema.json +3 -1
  366. package/dist/workspace/member-pipeline.d.ts +6 -0
  367. package/dist/workspace/member-pipeline.d.ts.map +1 -1
  368. package/dist/workspace/member-pipeline.js +6 -0
  369. package/dist/workspace/overlap.d.ts +146 -0
  370. package/dist/workspace/overlap.d.ts.map +1 -0
  371. package/dist/workspace/overlap.js +276 -0
  372. package/dist/workspace/promotion-order.d.ts +41 -0
  373. package/dist/workspace/promotion-order.d.ts.map +1 -0
  374. package/dist/workspace/promotion-order.js +95 -0
  375. package/dist/workspace/reason-codes.d.ts +4 -0
  376. package/dist/workspace/reason-codes.d.ts.map +1 -1
  377. package/dist/workspace/reason-codes.js +7 -1
  378. package/dist/workspace/records.d.ts +15 -2
  379. package/dist/workspace/records.d.ts.map +1 -1
  380. package/dist/workspace/records.js +32 -0
  381. package/dist/workspace/records.schema.json +21 -2
  382. package/dist/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
  383. package/dist/workspace/reference-kinds/flag/flag.schema.json +53 -0
  384. package/dist/workspace/releases.d.ts +81 -0
  385. package/dist/workspace/releases.d.ts.map +1 -0
  386. package/dist/workspace/releases.js +103 -0
  387. package/dist/workspace/releases.schema.json +513 -0
  388. package/dist/workspace/run-event.schema.json +295 -0
  389. package/dist/workspace/status-gates.d.ts +34 -1
  390. package/dist/workspace/status-gates.d.ts.map +1 -1
  391. package/dist/workspace/status-gates.js +31 -2
  392. package/dist/workspace/status-stewards.d.ts +4 -0
  393. package/dist/workspace/status-stewards.d.ts.map +1 -1
  394. package/dist/workspace/status-stewards.js +2 -0
  395. package/dist/workspace/status.d.ts +117 -0
  396. package/dist/workspace/status.d.ts.map +1 -1
  397. package/dist/workspace/status.js +218 -4
  398. package/dist/workspace/status.schema.json +268 -2
  399. package/dist/workspace/trust/ci-tag-statement.d.ts +102 -0
  400. package/dist/workspace/trust/ci-tag-statement.d.ts.map +1 -0
  401. package/dist/workspace/trust/ci-tag-statement.js +123 -0
  402. package/dist/workspace/trust/op-run-statement.d.ts +91 -0
  403. package/dist/workspace/trust/op-run-statement.d.ts.map +1 -0
  404. package/dist/workspace/trust/op-run-statement.js +143 -0
  405. package/dist/workspace/trust/rotation.d.ts +18 -2
  406. package/dist/workspace/trust/rotation.d.ts.map +1 -1
  407. package/dist/workspace/trust/rotation.js +65 -16
  408. package/dist/workspace/trust/seal.d.ts +11 -0
  409. package/dist/workspace/trust/seal.d.ts.map +1 -1
  410. package/dist/workspace/trust/seal.js +9 -0
  411. package/dist/workspace/trust/ssh-commit.d.ts.map +1 -1
  412. package/dist/workspace/trust/ssh-commit.js +10 -10
  413. package/dist/workspace/wip.d.ts.map +1 -1
  414. package/dist/workspace/wip.js +11 -3
  415. package/package.json +1 -1
  416. package/src/__golden__/wave-loop/fan-out-gated.json +8 -0
  417. package/src/__golden__/wave-loop/fan-out-no-members.json +4 -0
  418. package/src/__golden__/wave-loop/fan-out-only.json +6 -0
  419. package/src/__golden__/wave-loop/op-by-apply-hold.json +2 -0
  420. package/src/__golden__/wave-loop/op-cost-member.json +3 -0
  421. package/src/__golden__/wave-loop/op-held-on-before-apply.json +1 -0
  422. package/src/__golden__/wave-loop/op-held-on.json +2 -0
  423. package/src/__golden__/wave-loop/op-hooks-held.json +2 -0
  424. package/src/__golden__/wave-loop/op-hooks-hold-before-apply.json +2 -0
  425. package/src/__golden__/wave-loop/op-hooks-refused-at-base.json +3 -0
  426. package/src/__golden__/wave-loop/op-hooks-share.json +3 -0
  427. package/src/__golden__/wave-loop/op-per-run.json +7 -0
  428. package/src/__golden__/wave-loop/op-pr-review.json +7 -0
  429. package/src/__golden__/wave-loop/op-sealed.json +3 -0
  430. package/src/__golden__/wave-loop/op-spend.json +3 -0
  431. package/src/__golden__/wave-loop/op-wave-commands.json +7 -0
  432. package/src/__golden__/wave-loop/op-wide-waiting.json +4 -0
  433. package/src/build-params.test.ts +46 -0
  434. package/src/build-params.ts +55 -1
  435. package/src/change-set-graph.test.ts +72 -0
  436. package/src/change-set-graph.ts +141 -0
  437. package/src/change-set.ts +87 -6
  438. package/src/chant-version.ts +16 -0
  439. package/src/cli/build-params-cli.ts +22 -9
  440. package/src/cli/commands/build.ts +25 -11
  441. package/src/cli/commands/check-lexicon-docs.test.ts +51 -0
  442. package/src/cli/commands/check-lexicon-docs.ts +39 -2
  443. package/src/cli/commands/check-lexicon.ts +14 -2
  444. package/src/cli/commands/init-lexicon/templates/docs.ts +7 -6
  445. package/src/cli/commands/init-lexicon.test.ts +25 -0
  446. package/src/cli/commands/init-lexicon.ts +18 -8
  447. package/src/cli/handlers/approve-release.test.ts +84 -0
  448. package/src/cli/handlers/approve-revoked.test.ts +155 -0
  449. package/src/cli/handlers/approve.ts +65 -2
  450. package/src/cli/handlers/build.test.ts +67 -0
  451. package/src/cli/handlers/build.ts +46 -4
  452. package/src/cli/handlers/change-set.ts +49 -1
  453. package/src/cli/handlers/fan-out.test.ts +228 -1
  454. package/src/cli/handlers/fan-out.ts +53 -6
  455. package/src/cli/handlers/intent-trace.e2e.test.ts +201 -0
  456. package/src/cli/handlers/lifecycle-gc.test.ts +126 -0
  457. package/src/cli/handlers/lifecycle-gc.ts +146 -0
  458. package/src/cli/handlers/operator.test.ts +37 -0
  459. package/src/cli/handlers/pr.test.ts +341 -0
  460. package/src/cli/handlers/pr.ts +292 -47
  461. package/src/cli/handlers/promote.test.ts +669 -7
  462. package/src/cli/handlers/promote.ts +464 -29
  463. package/src/cli/handlers/release-implicit.e2e.test.ts +161 -0
  464. package/src/cli/handlers/release-join.e2e.test.ts +185 -0
  465. package/src/cli/handlers/release-publish-targets.e2e.test.ts +295 -0
  466. package/src/cli/handlers/release-verify.e2e.test.ts +117 -0
  467. package/src/cli/handlers/release-workstation-ci.e2e.test.ts +135 -0
  468. package/src/cli/handlers/release.e2e.test.ts +199 -0
  469. package/src/cli/handlers/release.ts +593 -0
  470. package/src/cli/handlers/run-auto-release.test.ts +80 -0
  471. package/src/cli/handlers/run-events-cmd.test.ts +92 -0
  472. package/src/cli/handlers/run-release.test.ts +55 -0
  473. package/src/cli/handlers/run.test.ts +30 -0
  474. package/src/cli/handlers/run.ts +243 -18
  475. package/src/cli/main.test.ts +8 -0
  476. package/src/cli/main.ts +143 -17
  477. package/src/cli/mcp/never-over-mcp.test.ts +2 -0
  478. package/src/cli/mcp/workspace-tools.ts +1 -1
  479. package/src/cli/parse-args.ts +56 -5
  480. package/src/cli/registry.ts +43 -5
  481. package/src/cli/run-events.ts +57 -0
  482. package/src/cli/version.ts +3 -13
  483. package/src/codegen/docs-in-site.test.ts +166 -0
  484. package/src/codegen/docs-in-site.ts +182 -0
  485. package/src/codegen/docs-site/AgentPrompt.astro +26 -1
  486. package/src/codegen/docs-types.ts +14 -1
  487. package/src/codegen/docs.ts +60 -7
  488. package/src/codegen/rehype-base-url.d.mts +3 -1
  489. package/src/codegen/rehype-base-url.mjs +15 -9
  490. package/src/codegen/rehype-base-url.test.ts +34 -0
  491. package/src/components/auto-release.test.ts +30 -0
  492. package/src/components/auto-release.ts +9 -1
  493. package/src/components/capability.ts +9 -0
  494. package/src/components/cli-support.test.ts +82 -0
  495. package/src/components/cli-support.ts +16 -9
  496. package/src/components/driver.test.ts +24 -0
  497. package/src/components/driver.ts +165 -56
  498. package/src/components/fan-out-inputs.test.ts +85 -0
  499. package/src/components/fan-out-inputs.ts +192 -0
  500. package/src/components/fan-out-record.test.ts +10 -0
  501. package/src/components/fan-out-record.ts +19 -0
  502. package/src/components/fan-out-run.ts +104 -35
  503. package/src/components/fan-out.ts +4 -0
  504. package/src/components/pr-pipeline.ts +35 -4
  505. package/src/components/pr-run.ts +30 -1
  506. package/src/components/promote-order.test.ts +90 -0
  507. package/src/components/promote-order.ts +75 -0
  508. package/src/components/promote.test.ts +10 -1
  509. package/src/components/promote.ts +94 -3
  510. package/src/components/release-compile.test.ts +147 -0
  511. package/src/components/release-compile.ts +459 -0
  512. package/src/components/release-gate.test.ts +215 -0
  513. package/src/components/release-gate.ts +355 -0
  514. package/src/components/release-promote.test.ts +159 -0
  515. package/src/components/release-promote.ts +232 -0
  516. package/src/components/run-progress.ts +7 -0
  517. package/src/components/wave-plan.ts +16 -3
  518. package/src/config.ts +15 -1
  519. package/src/flag-params.test.ts +144 -0
  520. package/src/flag-params.ts +134 -0
  521. package/src/index.ts +2 -0
  522. package/src/lifecycle/__fixtures__/publish-fakes.ts +351 -0
  523. package/src/lifecycle/change-set-store.test.ts +217 -0
  524. package/src/lifecycle/change-set-store.ts +113 -0
  525. package/src/lifecycle/commit-sha.ts +23 -0
  526. package/src/lifecycle/delivery-metrics.ts +263 -0
  527. package/src/lifecycle/deploy-outcome.ts +147 -0
  528. package/src/lifecycle/gate-change-set.e2e.test.ts +122 -0
  529. package/src/lifecycle/gate-change-set.ts +164 -0
  530. package/src/lifecycle/gate-ledger.test.ts +33 -0
  531. package/src/lifecycle/gate-ledger.ts +68 -3
  532. package/src/lifecycle/git.ts +61 -8
  533. package/src/lifecycle/index.ts +4 -0
  534. package/src/lifecycle/mixed-environment.test.ts +97 -0
  535. package/src/lifecycle/mixed-environment.ts +204 -0
  536. package/src/lifecycle/publish-attachments.ts +196 -0
  537. package/src/lifecycle/publish-credentials.ts +84 -0
  538. package/src/lifecycle/publish-declaration.ts +95 -0
  539. package/src/lifecycle/publish-forge.ts +325 -0
  540. package/src/lifecycle/publish-npm.ts +259 -0
  541. package/src/lifecycle/publish-oci.ts +396 -0
  542. package/src/lifecycle/publish-record.ts +113 -0
  543. package/src/lifecycle/publish-resume.test.ts +168 -0
  544. package/src/lifecycle/publish-targets.test.ts +494 -0
  545. package/src/lifecycle/publish-targets.ts +100 -0
  546. package/src/lifecycle/release-cut-signed.test.ts +180 -0
  547. package/src/lifecycle/release-cut.test.ts +483 -0
  548. package/src/lifecycle/release-cut.ts +503 -0
  549. package/src/lifecycle/release-flag.test.ts +112 -0
  550. package/src/lifecycle/release-join.test.ts +68 -0
  551. package/src/lifecycle/release-join.ts +182 -0
  552. package/src/lifecycle/release-ledger.test.ts +14 -0
  553. package/src/lifecycle/release-ledger.ts +85 -17
  554. package/src/lifecycle/release-publish.test.ts +225 -0
  555. package/src/lifecycle/release-publish.ts +568 -0
  556. package/src/lifecycle/release-revocation.ts +147 -0
  557. package/src/lifecycle/release-store.ts +434 -0
  558. package/src/lifecycle/release-verify.test.ts +110 -0
  559. package/src/lifecycle/release-verify.ts +120 -0
  560. package/src/lifecycle/release-workstation.test.ts +204 -0
  561. package/src/lifecycle/release-workstation.ts +216 -0
  562. package/src/lifecycle/retention-growth.e2e.test.ts +279 -0
  563. package/src/lifecycle/retention-policy.ts +63 -0
  564. package/src/lifecycle/retention.test.ts +363 -0
  565. package/src/lifecycle/retention.ts +508 -0
  566. package/src/lifecycle/run-ledger.test.ts +10 -0
  567. package/src/lifecycle/run-ledger.ts +6 -0
  568. package/src/op/activities/change-coverage.ts +4 -1
  569. package/src/op/activities/source-release.ts +8 -3
  570. package/src/op/activities/source-rollback.ts +8 -4
  571. package/src/op/commit-run-gate-deploy-join.test.ts +117 -0
  572. package/src/op/gate-name.ts +13 -3
  573. package/src/op/gate-release.test.ts +126 -0
  574. package/src/op/gate-run-locator.test.ts +110 -0
  575. package/src/op/gate-run-locator.ts +35 -0
  576. package/src/op/gate.ts +88 -6
  577. package/src/op/local-executor.ts +207 -52
  578. package/src/op/op-waves-run.ts +4 -0
  579. package/src/op/run-events.test.ts +166 -0
  580. package/src/op/run-live.ts +1 -1
  581. package/src/op/runtime.ts +39 -0
  582. package/src/op/runtimes/local.test.ts +16 -0
  583. package/src/op/runtimes/local.ts +9 -0
  584. package/src/plan-summary.test.ts +20 -0
  585. package/src/plan-summary.ts +8 -1
  586. package/src/point-decision.ts +12 -2
  587. package/src/pr-loop.ts +25 -4
  588. package/src/pr-report-url.test.ts +131 -0
  589. package/src/pr-report-url.ts +209 -0
  590. package/src/provenance.ts +2 -2
  591. package/src/run-event-store.test.ts +144 -0
  592. package/src/run-event-store.ts +273 -0
  593. package/src/run-event-stream.test.ts +175 -0
  594. package/src/run-event-stream.ts +309 -0
  595. package/src/run-event.test.ts +277 -0
  596. package/src/run-event.ts +471 -0
  597. package/src/wave-loop-golden.test.ts +9 -0
  598. package/src/workspace/__fixtures__/flag-workspace.ts +132 -0
  599. package/src/workspace/__fixtures__/older-records/change-set-984534ea9.json +100 -0
  600. package/src/workspace/__fixtures__/older-records/older-records.json +326 -0
  601. package/src/workspace/__fixtures__/run-event-check.ts +58 -0
  602. package/src/workspace/ancestry-index.test.ts +93 -0
  603. package/src/workspace/change-set-show.schema.json +102 -0
  604. package/src/workspace/change-set-show.ts +72 -0
  605. package/src/workspace/change-set.schema.json +32 -1
  606. package/src/workspace/changes-cli.ts +10 -1
  607. package/src/workspace/changes.schema.json +135 -0
  608. package/src/workspace/changes.test.ts +1 -1
  609. package/src/workspace/changes.ts +84 -2
  610. package/src/workspace/check.schema.json +4 -2
  611. package/src/workspace/checks/records.ts +35 -1
  612. package/src/workspace/checks.ts +2 -1
  613. package/src/workspace/ci-green-cli.ts +75 -13
  614. package/src/workspace/ci-green-forge.ts +24 -4
  615. package/src/workspace/ci-green-runs.test.ts +242 -0
  616. package/src/workspace/ci-green-signed.test.ts +302 -0
  617. package/src/workspace/ci-green-workflow.test.ts +16 -0
  618. package/src/workspace/ci-green-workflow.ts +12 -0
  619. package/src/workspace/ci-green.test.ts +131 -2
  620. package/src/workspace/ci-green.ts +635 -42
  621. package/src/workspace/ci-last-green.schema.json +32 -2
  622. package/src/workspace/ci-list.schema.json +525 -0
  623. package/src/workspace/commit-links.test.ts +279 -0
  624. package/src/workspace/commit-links.ts +183 -0
  625. package/src/workspace/declaration.schema.json +76 -0
  626. package/src/workspace/declaration.test.ts +28 -0
  627. package/src/workspace/declaration.ts +60 -2
  628. package/src/workspace/declared-kinds.ts +40 -1
  629. package/src/workspace/delivery-metrics.schema.json +519 -0
  630. package/src/workspace/delivery-metrics.test.ts +236 -0
  631. package/src/workspace/delivery-metrics.ts +286 -0
  632. package/src/workspace/flags-read.ts +121 -0
  633. package/src/workspace/flags.test.ts +141 -0
  634. package/src/workspace/flags.ts +157 -0
  635. package/src/workspace/graph-cli.ts +1 -1
  636. package/src/workspace/identity-gate-rule.ts +91 -7
  637. package/src/workspace/identity.ts +14 -1
  638. package/src/workspace/intent-cli.ts +46 -5
  639. package/src/workspace/intent-trace.test.ts +173 -0
  640. package/src/workspace/intent-trace.ts +484 -0
  641. package/src/workspace/intent.schema.json +702 -6
  642. package/src/workspace/intent.ts +196 -26
  643. package/src/workspace/ls.schema.json +3 -1
  644. package/src/workspace/ls.ts +6 -0
  645. package/src/workspace/member-pipeline.test.ts +17 -0
  646. package/src/workspace/member-pipeline.ts +9 -0
  647. package/src/workspace/older-records.test.ts +412 -0
  648. package/src/workspace/overlap.test.ts +165 -0
  649. package/src/workspace/overlap.ts +340 -0
  650. package/src/workspace/promotion-order.ts +97 -0
  651. package/src/workspace/read-contract.e2e.test.ts +5 -1
  652. package/src/workspace/reason-codes.test.ts +7 -1
  653. package/src/workspace/reason-codes.ts +7 -1
  654. package/src/workspace/record-assets.test.ts +2 -1
  655. package/src/workspace/records-contract.e2e.test.ts +2 -1
  656. package/src/workspace/records.schema.json +21 -2
  657. package/src/workspace/records.ts +36 -2
  658. package/src/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
  659. package/src/workspace/reference-kinds/flag/flag.schema.json +53 -0
  660. package/src/workspace/releases.schema.json +513 -0
  661. package/src/workspace/releases.test.ts +108 -0
  662. package/src/workspace/releases.ts +133 -0
  663. package/src/workspace/run-event.schema.json +295 -0
  664. package/src/workspace/status-contract.test.ts +81 -1
  665. package/src/workspace/status-gates.ts +64 -1
  666. package/src/workspace/status-mixed.test.ts +152 -0
  667. package/src/workspace/status-revoked.test.ts +153 -0
  668. package/src/workspace/status-stewards.ts +6 -0
  669. package/src/workspace/status.schema.json +268 -2
  670. package/src/workspace/status.ts +334 -4
  671. package/src/workspace/trust/ci-tag-statement.ts +151 -0
  672. package/src/workspace/trust/op-run-statement.ts +148 -0
  673. package/src/workspace/trust/rotation.ts +61 -14
  674. package/src/workspace/trust/seal.ts +10 -0
  675. package/src/workspace/trust/ssh-commit.ts +10 -9
  676. package/src/workspace/wip.ts +11 -2
  677. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/astro.config.mjs +0 -14
  678. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/package.json +0 -16
  679. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/index.mdx +0 -8
  680. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content.config.ts +0 -15
  681. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/tsconfig.json +0 -10
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The intent graph over one region: `chant workspace graph --intent
3
- * <path[:start-end]|path#symbol>` (#2651; #2650 sections B and C1; #2524 D8,
3
+ * <path[:start-end]|path#symbol|member/node|sha>` (#2651; #2650 sections B and C1; #2524 D8,
4
4
  * D15; #3034).
5
5
  *
6
6
  * A person points at some code and asks how it got this way and who decided
@@ -77,6 +77,7 @@ import { followSquashes, squashLineOrigins, type Forge, type SquashFollow } from
77
77
  import { memberDirOnDisk, memberSymbolResolvers, type SymbolPluginLoader } from "./lexicon-symbols";
78
78
  import { resolveSymbol, symbolResolverFor, type SymbolResolver } from "./symbols";
79
79
  import { readTrailerJoins, type CommitTrailerJoins } from "./trailer-joins";
80
+ import { buildTrace, type IntentTrace } from "./intent-trace";
80
81
  import { joinPath, skippedDir, workingTree, type WorkspaceTree } from "./tree";
81
82
  import { activeAttestors, type ProvenanceLevel } from "./trust/attestor";
82
83
  import { commitProvenance, policyAtBase, resolveBase } from "./trust/provenance";
@@ -204,9 +205,12 @@ export interface RegionNode {
204
205
  /** The member whose directory holds the path, or null. */
205
206
  member: string | null;
206
207
  at: string | null;
207
- type: "file" | "dir";
208
- /** For a file region, whether the file is generated (#2524 D14); null for a directory. */
208
+ /** `commit` for a region given as a commit sha (#4023): the files that commit changed. */
209
+ type: "file" | "dir" | "commit";
210
+ /** For a file region, whether the file is generated (#2524 D14); null for a directory or a commit. */
209
211
  generated: boolean | null;
212
+ /** Set when the region was given as a commit sha (#4023): its full sha. `path` is then `.`. */
213
+ commit?: string;
210
214
  /** Set when the region was given as a graph node id. */
211
215
  node: string | null;
212
216
  /** Set when the region was given as `path#symbol` (#3034): the symbol as given, the declaration it resolved to, and its kind. `lines` holds its current lines. */
@@ -444,6 +448,16 @@ export interface WorkNode {
444
448
  * to the review through it. Empty when none does. Added within contract 1.
445
449
  */
446
450
  reviews?: WorkReview[];
451
+ /**
452
+ * The other in-flight work items whose linked commits touch a file this
453
+ * item's linked commits touch (#4025), as `workspace status --json` reports
454
+ * in `overlap`, each with the shared files. Empty when none does, or when
455
+ * this item is not in flight. Absent on a read with `--at` and when the
456
+ * overlap can't be read. The change-set entries two items share are in
457
+ * `status`, which reads the gates. Added within contract 1 by chant
458
+ * 0.124.29.
459
+ */
460
+ overlaps?: { with: string; files: string[] }[];
447
461
  }
448
462
 
449
463
  /**
@@ -582,20 +596,22 @@ export type IntentDocument =
582
596
  at: string | null;
583
597
  workspace: { name: string; root: string };
584
598
  region: string;
585
- history: { rev: string | null; follows: "line-range" | "file" | "directory"; shallow: boolean };
599
+ history: { rev: string | null; follows: "line-range" | "file" | "directory" | "commit"; shallow: boolean };
586
600
  kinds: { file: string; name: string; records: string | null; joins: "function" | "data" | null }[];
587
601
  nodes: IntentNode[];
588
602
  edges: IntentEdge[];
589
603
  reasons: IntentReason[];
590
604
  summary: { commits: number; decisions: number; artifacts: number; findings: number };
591
605
  why: WhyAnswer;
606
+ /** The why chain (#4023): each commit's work item, ci tag, release, gate resolutions and deploys. Added within contract 1 by chant 0.124.29. */
607
+ trace?: IntentTrace;
592
608
  })
593
609
  | (Head & { error: { code: IntentErrorCode; message: string } });
594
610
 
595
611
  export interface IntentQuery {
596
612
  /** Where the walk up to the declaration starts, and what the region path is relative to. */
597
613
  cwd: string;
598
- /** `path`, `path:line`, `path:start-end` or `path#symbol`, or a graph node id `<member>/<id>`. */
614
+ /** `path`, `path:line`, `path:start-end` or `path#symbol`, a graph node id `<member>/<id>`, or a commit sha (#4023). */
599
615
  region: string;
600
616
  at?: string;
601
617
  /** Follow squash merges to their pull requests' original commits, fetching a missing pull request ref from `origin` (#3035). */
@@ -767,14 +783,77 @@ export function closingCommit(top: string, rev: string, path: string, stateField
767
783
  return closing;
768
784
  }
769
785
 
786
+ /**
787
+ * The files a commit changed, from the workspace root (#4023): against its
788
+ * first parent, or every file for a root commit. Files outside the workspace
789
+ * are left out.
790
+ */
791
+ export function changedFiles(top: string, sha: string, workspacePrefix: string): string[] {
792
+ const out = tryGit(top, ["diff-tree", "--no-commit-id", "-r", "--name-only", "--root", "-m", "--first-parent", "-z", sha]) ?? "";
793
+ const files = new Set<string>();
794
+ for (const f of out.split("\0")) {
795
+ if (!f) continue;
796
+ if (workspacePrefix === "") files.add(f);
797
+ else if (f.startsWith(`${workspacePrefix}/`)) files.add(f.slice(workspacePrefix.length + 1));
798
+ }
799
+ return [...files].sort();
800
+ }
801
+
770
802
  /** The commit a decision record's window opens at: the one that decided it, or, when it is not approved, the one that added it. */
771
803
  export function windowOpening(top: string, rev: string, view: Pick<RecordView, "path" | "decidedIn">): string | null {
772
804
  return view.decidedIn?.sha ?? addingCommit(top, rev, view.path);
773
805
  }
774
806
 
775
- /** `commit` and every commit between it and `rev` that has it as an ancestor. */
807
+ /** A full commit id, as git prints it: lowercase hex, sha1 or sha256. */
808
+ const FULL_COMMIT_ID = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
809
+
810
+ /**
811
+ * The children of every commit `rev` reaches, from one `git rev-list
812
+ * --parents` walk (#4032). Kept in memory for the last repository and commit
813
+ * read, since a walk asks for the windows of many records at one revision. A
814
+ * commit id names the same history for good, so the entry never goes stale.
815
+ */
816
+ let childIndex: { key: string; children: Map<string, string[]> } | undefined;
817
+
818
+ function childrenWithin(top: string, rev: string): Map<string, string[]> | undefined {
819
+ const key = `${top}\0${rev}`;
820
+ if (childIndex?.key === key) return childIndex.children;
821
+ const text = tryGit(top, ["rev-list", "--parents", rev]);
822
+ if (text === undefined) return undefined;
823
+ const children = new Map<string, string[]>();
824
+ for (const row of text.split("\n")) {
825
+ const [commit, ...parents] = row.trim().split(" ");
826
+ if (!commit) continue;
827
+ for (const p of parents) {
828
+ const list = children.get(p);
829
+ if (list) list.push(commit);
830
+ else children.set(p, [commit]);
831
+ }
832
+ }
833
+ childIndex = { key, children };
834
+ return children;
835
+ }
836
+
837
+ /**
838
+ * `commit` and every commit between it and `rev` that has it as an ancestor:
839
+ * what `git rev-list --ancestry-path <commit>..<rev>` lists, plus `commit`.
840
+ * With full commit ids it is read from the children of what `rev` reaches,
841
+ * walked once per revision, instead of one `git rev-list` per record (#4032).
842
+ */
776
843
  export function descendants(top: string, commit: string, rev: string): Set<string> {
777
844
  const out = new Set<string>([commit]);
845
+ const children = FULL_COMMIT_ID.test(commit) && FULL_COMMIT_ID.test(rev) ? childrenWithin(top, rev) : undefined;
846
+ if (children) {
847
+ const stack = [commit];
848
+ while (stack.length > 0) {
849
+ for (const c of children.get(stack.pop()!) ?? []) {
850
+ if (out.has(c)) continue;
851
+ out.add(c);
852
+ stack.push(c);
853
+ }
854
+ }
855
+ return out;
856
+ }
778
857
  const text = tryGit(top, ["rev-list", "--ancestry-path", `${commit}..${rev}`]);
779
858
  for (const line of (text ?? "").split("\n")) if (line.trim()) out.add(line.trim());
780
859
  return out;
@@ -926,8 +1005,13 @@ interface ResolvedRegion {
926
1005
  lines: LineRange | null;
927
1006
  node: string | null;
928
1007
  symbol: { name: string; qualified: string; kind: string } | null;
1008
+ /** The full sha, for a region given as a commit (#4023). */
1009
+ commit?: string;
929
1010
  }
930
1011
 
1012
+ /** A commit sha as a region may give it: 7 to 64 hex digits. */
1013
+ const COMMIT_REGION = /^[0-9a-fA-F]{7,64}$/;
1014
+
931
1015
  async function resolveRegion(query: IntentQuery, located: LocatedWorkspace, declaration: Declaration): Promise<ResolvedRegion> {
932
1016
  const parsed = parseRegion(query.region);
933
1017
  if ("error" in parsed) throw new IntentError("intent-region-invalid", parsed.error);
@@ -972,10 +1056,38 @@ async function resolveRegion(query: IntentQuery, located: LocatedWorkspace, decl
972
1056
  return { path: file === "" ? "." : file, lines: loc.line ? { start: loc.line, end: loc.line } : null, node: query.region, symbol: null };
973
1057
  }
974
1058
  }
975
- throw new IntentError("intent-region-invalid", `${query.region} is not a path in the workspace${located.tree.label}, a path#symbol, or a graph node id`);
1059
+ // A commit sha, when no path or node has the name (#4023): the commit and the files it changed.
1060
+ if (COMMIT_REGION.test(query.region) && located.top) {
1061
+ const sha = tryGit(located.top, ["rev-parse", "--verify", "--quiet", `${query.region}^{commit}`])?.trim();
1062
+ if (sha) return { path: ".", lines: null, node: null, symbol: null, commit: sha };
1063
+ }
1064
+ throw new IntentError("intent-region-invalid", `${query.region} is not a path in the workspace${located.tree.label}, a path#symbol, a graph node id or a commit`);
976
1065
  }
977
1066
 
978
1067
  /** Build the intent document. Never throws an {@link IntentError}, {@link WorkspaceReadError} or {@link RecordReadError}. */
1068
+ /**
1069
+ * Give each work node the in-flight items it overlaps by file (#4025). Only a
1070
+ * working-tree read, since the overlap is about the branches as they are now;
1071
+ * a read that fails leaves the field off.
1072
+ */
1073
+ async function addOverlaps(work: WorkNode[], declaration: Declaration, root: string, top: string, at: string | null): Promise<void> {
1074
+ if (work.length === 0 || at !== null) return;
1075
+ let report: import("./overlap").OverlapReport | null;
1076
+ try {
1077
+ const { readOverlap } = await import("./overlap");
1078
+ report = await readOverlap({ declaration, root, top, changeSets: [] });
1079
+ } catch {
1080
+ return;
1081
+ }
1082
+ if (report === null) return;
1083
+ for (const n of work) {
1084
+ const self = `${n.recordKind}:${n.record}`;
1085
+ n.overlaps = report.pairs
1086
+ .filter((p) => p.items.includes(self) && p.files.length > 0)
1087
+ .map((p) => ({ with: p.items[0] === self ? p.items[1] : p.items[0], files: p.files }));
1088
+ }
1089
+ }
1090
+
979
1091
  export async function intentGraph(query: IntentQuery): Promise<IntentResult> {
980
1092
  const head: Head = { $schema: INTENT_OUTPUT_SCHEMA_ID, contract: INTENT_CONTRACT_VERSION, chant: readerVersion() };
981
1093
  try {
@@ -994,7 +1106,7 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
994
1106
  if (!top) throw new IntentError("not-a-git-repository", "the intent graph reads git history, and this directory is not in a git repository");
995
1107
  const declaration = readDeclaration(located.tree, "", { rootChant: true });
996
1108
  const region = await resolveRegion(query, located, declaration);
997
- const type = region.path === "." ? "dir" : located.tree.stat(region.path);
1109
+ const type: "file" | "dir" | "commit" | undefined = region.commit ? "commit" : region.path === "." ? "dir" : located.tree.stat(region.path);
998
1110
  if (!type) throw new IntentError("intent-region-invalid", `${region.path} does not exist${located.tree.label}`);
999
1111
  if (region.lines) {
1000
1112
  if (type !== "file") throw new IntentError("intent-region-invalid", `${region.path} is a directory, and a line range needs a file`);
@@ -1018,8 +1130,21 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
1018
1130
  };
1019
1131
 
1020
1132
  // The region, its files and its member.
1021
- const member = region.path === "." ? (declaration.members.find((m) => m.dir === ".")?.name ?? null) : memberHolding(region.path, declaration.members);
1022
- const rid = regionId(region.path, region.lines, region.symbol?.name);
1133
+ // A commit region (#4023) covers the files the commit changed, from the workspace root, and the members holding them.
1134
+ const workspacePrefix = located.root === "." ? "" : located.root;
1135
+ const changed = region.commit ? changedFiles(top, region.commit, workspacePrefix) : [];
1136
+ const changedMembers = [...new Set(changed.map((f) => memberHolding(f, declaration.members)).filter((m): m is string => m !== null))];
1137
+ const member = region.commit
1138
+ ? changedMembers.length === 1
1139
+ ? changedMembers[0]
1140
+ : null
1141
+ : region.path === "."
1142
+ ? (declaration.members.find((m) => m.dir === ".")?.name ?? null)
1143
+ : memberHolding(region.path, declaration.members);
1144
+ const regionMembers = region.commit ? changedMembers : member ? [member] : [];
1145
+ const rid = region.commit ? `region:commit:${region.commit}` : regionId(region.path, region.lines, region.symbol?.name);
1146
+ /** How messages name the region: its path, or the commit. */
1147
+ const regionLabel = region.commit ? `commit ${region.commit.slice(0, 8)}` : region.path;
1023
1148
  add<RegionNode>({
1024
1149
  id: rid,
1025
1150
  kind: "region",
@@ -1031,6 +1156,7 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
1031
1156
  generated: type === "file" ? isGeneratedPath(declaration, region.path) : null,
1032
1157
  node: region.node,
1033
1158
  symbol: region.symbol,
1159
+ ...(region.commit ? { commit: region.commit } : {}),
1034
1160
  });
1035
1161
  const files = type === "dir" ? filesUnder(located.tree, region.path) : [];
1036
1162
  for (const f of files) add<FileNode>({ id: `file:${f}`, kind: "file", path: f, member: memberHolding(f, declaration.members), generated: isGeneratedPath(declaration, f) });
@@ -1044,9 +1170,8 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
1044
1170
  const rev = located.at ?? (tryGit(top, ["rev-parse", "--verify", "--quiet", "HEAD^{commit}"])?.trim() || null);
1045
1171
  const shallow = tryGit(top, ["rev-parse", "--is-shallow-repository"])?.trim() === "true";
1046
1172
  if (shallow) reasons.push({ code: "intent-history-shallow", message: "this is a shallow clone, so the history stops at its boundary and the oldest commit listed may stand for many" });
1047
- const workspacePrefix = located.root === "." ? "" : located.root;
1048
1173
  const gitPath = joinPath(workspacePrefix, region.path);
1049
- const touched = rev ? regionHistory(top, rev, gitPath, type, region.lines) : [];
1174
+ const touched = region.commit ? [{ sha: region.commit, lines: null }] : rev && type !== "commit" ? regionHistory(top, rev, gitPath, type, region.lines) : [];
1050
1175
  // Who last wrote each current line of a file region (#3034). A commit blame
1051
1176
  // names and the history walk missed, as when a move confuses git log -L,
1052
1177
  // joins the commits that touched the region.
@@ -1262,14 +1387,16 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
1262
1387
  const p = entry.slice("path:".length);
1263
1388
  if (!isWorkspacePath(p)) continue;
1264
1389
  const full = joinPath(kindPrefix, p);
1265
- if (regionFull !== "" && constraintCovers(full, regionFull)) {
1390
+ if (type === "commit") {
1391
+ if (changed.some((f) => constraintCovers(full, joinPath(workspacePrefix, f)))) matched.push({ entry, granularity: "path", to: rid });
1392
+ } else if (regionFull !== "" && constraintCovers(full, regionFull)) {
1266
1393
  matched.push({ entry, granularity: "path", to: rid });
1267
1394
  } else if (type === "dir" && full !== regionFull && (regionFull === "" || full.startsWith(`${regionFull}/`))) {
1268
1395
  // A path below a directory region constrains the files under it, not the whole region.
1269
1396
  for (const f of fileNodesUnder(full)) matched.push({ entry, granularity: "path", to: `file:${f}` });
1270
1397
  }
1271
1398
  } else if (entry.startsWith("member:")) {
1272
- if (member !== null && entry.slice("member:".length) === member) matched.push({ entry, granularity: "member", to: rid });
1399
+ if (regionMembers.includes(entry.slice("member:".length))) matched.push({ entry, granularity: "member", to: rid });
1273
1400
  } else {
1274
1401
  const issue = entry.match(ISSUE);
1275
1402
  if (issue && origin !== null && issue[1].toLowerCase() === origin && commitRefs.has(issue[2])) matched.push({ entry, granularity: "issue", to: rid });
@@ -1644,7 +1771,7 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
1644
1771
  if (h.node!.constrains.some((x) => x.granularity === "path") && inWorkWindow(h, t.sha)) edges.push({ kind: "within", from: c.id, to: h.node!.id, state: "worked" });
1645
1772
  }
1646
1773
  if (readsRecords && c.state === "undecided") {
1647
- find("intent-commit-undecided", `${t.sha.slice(0, 8)} changed the region when no decision constrained ${region.path} by path`, [c.id, rid]);
1774
+ find("intent-commit-undecided", `${t.sha.slice(0, 8)} changed the region when no decision constrained ${regionLabel} by path`, [c.id, rid]);
1648
1775
  }
1649
1776
  if (readsRecords && !j.unit && j.carries.length === 0 && c.pullRequest === null && coveredAt(t.sha, ["path", "member", "contract", "issue"]).length === 0) {
1650
1777
  find("intent-commit-bare", `${t.sha.slice(0, 8)} names no unit, no record, no pull request and no decision`, [c.id]);
@@ -1659,11 +1786,11 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
1659
1786
  if (readsRecords) {
1660
1787
  const current = covering.filter((c) => c.node.supersededBy === null);
1661
1788
  if (covering.length === 0) {
1662
- find("intent-region-unconstrained", `no decision constrains ${region.path}`, [rid]);
1789
+ find("intent-region-unconstrained", `no decision constrains ${regionLabel}`, [rid]);
1663
1790
  } else if (current.length === 0) {
1664
- find("intent-decision-superseded-live", `every decision constraining ${region.path} is superseded: ${covering.map((c) => `${c.node.record} by ${c.node.supersededBy}`).join(", ")}`, [rid, ...covering.map((c) => c.node.id)]);
1791
+ find("intent-decision-superseded-live", `every decision constraining ${regionLabel} is superseded: ${covering.map((c) => `${c.node.record} by ${c.node.supersededBy}`).join(", ")}`, [rid, ...covering.map((c) => c.node.id)]);
1665
1792
  } else if (current.every((c) => !c.node.closed)) {
1666
- find("intent-decision-provisional", `the decisions constraining ${region.path} are ${[...new Set(current.map((c) => c.node.state ?? "stateless"))].join(" or ")}, and none is in a closed state`, [rid, ...current.map((c) => c.node.id)]);
1793
+ find("intent-decision-provisional", `the decisions constraining ${regionLabel} are ${[...new Set(current.map((c) => c.node.state ?? "stateless"))].join(" or ")}, and none is in a closed state`, [rid, ...current.map((c) => c.node.id)]);
1667
1794
  }
1668
1795
  // A current decision with a dissent nobody addressed or withdrew (#2686), from its quorum.
1669
1796
  for (const c of current) {
@@ -1674,14 +1801,14 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
1674
1801
  id: `finding:intent-decision-contested:${findings.filter((f) => f.code === "intent-decision-contested").length + 1}`,
1675
1802
  kind: "finding",
1676
1803
  code: "intent-decision-contested",
1677
- message: `${c.node.record} constrains ${region.path} and has ${open.length} open ${open.length === 1 ? "concern" : "concerns"}, from ${open.map((o) => o.reviewer).join(", ")}: a dissent neither addressed nor withdrawn`,
1804
+ message: `${c.node.record} constrains ${regionLabel} and has ${open.length} open ${open.length === 1 ? "concern" : "concerns"}, from ${open.map((o) => o.reviewer).join(", ")}: a dissent neither addressed nor withdrawn`,
1678
1805
  concerns: [c.node.id, rid],
1679
1806
  openConcerns: { count: open.length, principals },
1680
1807
  });
1681
1808
  }
1682
1809
  const granularities = new Set(covering.flatMap((c) => c.node.constrains.map((x) => x.granularity)));
1683
1810
  if (covering.length > 0 && !granularities.has("path") && granularities.has("member")) {
1684
- find("intent-constraint-coarse", `${region.path} is constrained only through its member, ${member}`, [rid, ...covering.map((c) => c.node.id)]);
1811
+ find("intent-constraint-coarse", `${regionLabel} is constrained only through its member, ${regionMembers.join(", ")}`, [rid, ...covering.map((c) => c.node.id)]);
1685
1812
  }
1686
1813
  }
1687
1814
 
@@ -1692,7 +1819,7 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
1692
1819
  if (implementersOf(c.node).length > 0) continue;
1693
1820
  const w = windows.get(c.node.id);
1694
1821
  if (w && touched.some((t) => w.from.has(t.sha) && !w.until?.has(t.sha))) continue;
1695
- find("intent-decision-unimplemented", `${c.node.record} is ${c.node.state} and constrains ${region.path}, and no work item that is not dropped implements it, and no commit changed the region in its window`, [c.node.id, rid]);
1822
+ find("intent-decision-unimplemented", `${c.node.record} is ${c.node.state} and constrains ${regionLabel}, and no work item that is not dropped implements it, and no commit changed the region in its window`, [c.node.id, rid]);
1696
1823
  }
1697
1824
  for (const h of workCovering) {
1698
1825
  const node = h.node!;
@@ -1710,7 +1837,7 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
1710
1837
  if (impl.length === 0 || impl.some((h) => h.view.state === workSpec(h).done)) continue;
1711
1838
  find(
1712
1839
  "intent-work-open-decided-code",
1713
- `${own.length} ${own.length === 1 ? "commit" : "commits"} in ${region.path} ${own.length === 1 ? "is" : "are"} ${d.record}'s own work, and ${impl.map((h) => `${h.view.id} is ${h.view.state ?? "stateless"}`).join(", ")}: the code says done and the queue says not`,
1840
+ `${own.length} ${own.length === 1 ? "commit" : "commits"} in ${regionLabel} ${own.length === 1 ? "is" : "are"} ${d.record}'s own work, and ${impl.map((h) => `${h.view.id} is ${h.view.state ?? "stateless"}`).join(", ")}: the code says done and the queue says not`,
1714
1841
  [d.id, ...own, ...impl.map((h) => workNode(h).id)],
1715
1842
  );
1716
1843
  }
@@ -1780,6 +1907,7 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
1780
1907
  const kindPrefix = h.kind.records!.workspaceRoot === "." ? "" : h.kind.records!.workspaceRoot;
1781
1908
  const full = joinPath(kindPrefix, parsed.path);
1782
1909
  const contains = (a: string, b: string) => a === b || a === "" || b.startsWith(`${a}/`);
1910
+ if (type === "commit") return changed.some((f) => contains(full, joinPath(workspacePrefix, f)) || contains(joinPath(workspacePrefix, f), full));
1783
1911
  if (!contains(full, regionFull) && !contains(regionFull, full)) return false;
1784
1912
  if (full === regionFull && parsed.lines && region.lines) return parsed.lines.start <= region.lines.end && region.lines.start <= parsed.lines.end;
1785
1913
  return true;
@@ -1793,7 +1921,7 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
1793
1921
  if (gap && h.view.state === workSpec(h).done) {
1794
1922
  const node = workNode(h);
1795
1923
  if (!node.warnings.some((w) => w.code === "work-done-gap-open")) {
1796
- node.warnings.push({ code: "work-done-gap-open", message: `${node.record} is ${h.view.state}, and ${f.code}, the gap it came from, still fires on ${region.path}` });
1924
+ node.warnings.push({ code: "work-done-gap-open", message: `${node.record} is ${h.view.state}, and ${f.code}, the gap it came from, still fires on ${regionLabel}` });
1797
1925
  }
1798
1926
  }
1799
1927
  }
@@ -1832,15 +1960,56 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
1832
1960
  }
1833
1961
  }
1834
1962
  for (const f of findings) nodes.set(f.id, f);
1963
+ await addOverlaps([...nodes.values()].filter((n): n is WorkNode => n.kind === "work"), declaration, located.rootOnDisk, top, located.at);
1835
1964
  const why = explain();
1836
1965
  const all = [...nodes.values()];
1966
+
1967
+ // The why chain (#4023): each commit's work item, ci tag, release, gate resolutions and deploys.
1968
+ const workIds = new Map<string, Set<string>>();
1969
+ const workNodeIds = new Map<string, string>();
1970
+ for (const h of workAll) {
1971
+ const name = h.kind.records!.loaded.kind.name;
1972
+ (workIds.get(name) ?? workIds.set(name, new Set()).get(name)!).add(h.view.id!);
1973
+ if (h.node) workNodeIds.set(`${name}:${h.view.id}`, h.node.id);
1974
+ }
1975
+ for (const k of workKinds) if (!workIds.has(k.records!.loaded.kind.name)) workIds.set(k.records!.loaded.kind.name, new Set());
1976
+ const windowsOf = new Map<string, { id: string; node: string; path: string }[]>();
1977
+ for (const e of edges) {
1978
+ if (e.kind !== "within" || e.state !== "worked") continue;
1979
+ const w = nodes.get(e.to) as WorkNode | undefined;
1980
+ if (!w) continue;
1981
+ const sha = e.from.slice("commit:".length);
1982
+ (windowsOf.get(sha) ?? windowsOf.set(sha, []).get(sha)!).push({ id: `${w.recordKind}:${w.record}`, node: w.id, path: w.path });
1983
+ }
1984
+ const later = new Map<string, Set<string>>();
1985
+ const trace = await buildTrace({
1986
+ top,
1987
+ commits: all.filter((n): n is CommitNode => n.kind === "commit").map((c) => ({ id: c.id, sha: c.sha, trailers: c.trailers, joins: c.joins })),
1988
+ work: readsWork ? { index: { kinds: workIds }, nodes: workNodeIds } : null,
1989
+ windows: windowsOf,
1990
+ // The region's member's ledger; for a region in no member, every member's and the workspace's own.
1991
+ ledgers: (regionMembers.length > 0 ? regionMembers : declaration.members.map((m) => m.name))
1992
+ .map((name) => {
1993
+ const dir = declaration.members.find((m) => m.name === name)!.dir;
1994
+ return { member: name as string | null, cwd: memberDirOnDisk(located.rootOnDisk, dir), within: dir === "." ? null : `_members/${name}/` };
1995
+ })
1996
+ .concat(regionMembers.length > 0 ? [] : [{ member: null, cwd: located.rootOnDisk, within: null }]),
1997
+ green: declaration.ci?.green ?? null,
1998
+ contains: (sha, commit) => {
1999
+ if (commit === sha) return true;
2000
+ if (!rev || !FULL_COMMIT_ID.test(commit)) return false;
2001
+ let set = later.get(sha);
2002
+ if (!set) later.set(sha, (set = descendants(top, sha, rev)));
2003
+ return set.has(commit);
2004
+ },
2005
+ });
1837
2006
  return {
1838
2007
  doc: {
1839
2008
  ...head,
1840
2009
  at: located.at,
1841
2010
  workspace: { name: declaration.name, root: located.root },
1842
2011
  region: rid,
1843
- history: { rev, follows: region.lines ? "line-range" : type === "file" ? "file" : "directory", shallow },
2012
+ history: { rev, follows: region.commit ? "commit" : region.lines ? "line-range" : type === "file" ? "file" : "directory", shallow },
1844
2013
  kinds: kinds.map((k) => ({ file: k.display, name: k.name, records: k.records?.loaded.kind.name ?? null, joins: k.joins?.form ?? null })),
1845
2014
  nodes: all,
1846
2015
  edges,
@@ -1852,6 +2021,7 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
1852
2021
  findings: findings.length,
1853
2022
  },
1854
2023
  why,
2024
+ trace,
1855
2025
  },
1856
2026
  failed: reasons.some((r) => r.code === "intent-plugin-failed"),
1857
2027
  };
@@ -1972,12 +2142,12 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
1972
2142
  if (!explained) {
1973
2143
  gaps.push({
1974
2144
  code: "intent-why-no-decision",
1975
- message: readsRecords ? `no current decision governs ${region.path}, and none is carried out by what made its lines` : `no record kind was read, so no decision can account for ${region.path}`,
2145
+ message: readsRecords ? `no current decision governs ${regionLabel}, and none is carried out by what made its lines` : `no record kind was read, so no decision can account for ${regionLabel}`,
1976
2146
  });
1977
2147
  }
1978
2148
  const committed = who.filter((w) => w.sha !== null);
1979
2149
  if (fileLines !== null ? committed.length > 0 && committed.every((w) => w.runs.length === 0) : touched.length > 0 && runNodes.length === 0) {
1980
- gaps.push({ code: "intent-why-no-run", message: `no agent run is joined to the commits that made ${fileLines !== null ? "the current lines of " : ""}${region.path}` });
2150
+ gaps.push({ code: "intent-why-no-run", message: `no agent run is joined to the commits that made ${fileLines !== null ? "the current lines of " : ""}${regionLabel}` });
1981
2151
  }
1982
2152
  const uncommitted = who.filter((w) => w.sha === null).map((w) => w.line);
1983
2153
  if (uncommitted.length > 0) gaps.push({ code: "intent-why-uncommitted", message: `${uncommitted.length} ${uncommitted.length === 1 ? "line is" : "lines are"} not committed yet`, lines: ranges(uncommitted) });
@@ -25,7 +25,9 @@
25
25
  }
26
26
  }
27
27
  },
28
- "require": { "type": "array", "items": { "type": "string" } }
28
+ "require": { "type": "array", "items": { "type": "string" } },
29
+ "signedSince": { "type": "string", "description": "Added in contract 1 by #4031, written by chant 0.124.28 and newer, and only when the declaration sets it: the full sha of the first commit whose ci tags count only with a statement signed by a runner key. Tags on its strict first-parent ancestors count unsigned." },
30
+ "source": { "const": "runs", "description": "Added in contract 1 by #4044, written by chant 0.124.28 and newer, and only when the declaration sets ci.green.source to runs: ci tick reads each phase's results from chant's own Op runs on chant/lifecycle, each signed by a runner key, in place of the forge's check runs. Absent means check runs." }
29
31
  }
30
32
  },
31
33
  "result": {
@@ -144,6 +144,10 @@ export interface LsCiGreen {
144
144
  window: string;
145
145
  phases: { name: string; runs: string[]; skipped: "pass" | "fail" }[];
146
146
  require: string[];
147
+ /** The first commit whose ci tags need a signed statement (#4031). Present only when the declaration sets it. */
148
+ signedSince?: string;
149
+ /** Where the tick reads phase results (#4044). Present only when it is `runs`; absent means the forge's check runs. */
150
+ source?: "runs";
147
151
  }
148
152
 
149
153
  export interface LsGroup {
@@ -322,6 +326,8 @@ function readListing(query: LsQuery): { doc: LsDocument; declaration?: Declarati
322
326
  window: declaration.ci.green.window,
323
327
  phases: declaration.ci.green.phases.map((p) => ({ name: p.name, runs: p.runs, skipped: p.skipped })),
324
328
  require: declaration.ci.green.require,
329
+ ...(declaration.ci.green.signedSince !== null ? { signedSince: declaration.ci.green.signedSince } : {}),
330
+ ...(declaration.ci.green.source === "runs" ? { source: "runs" as const } : {}),
325
331
  }
326
332
  : null,
327
333
  }
@@ -95,6 +95,23 @@ describe("the component pipeline's file and command", () => {
95
95
  expect(plan.command).toBe("chant build --components --generate github --env staging --pr-loop --gate infra-apply --branch trunk");
96
96
  });
97
97
 
98
+ test("the recorded command keeps --image and each --before-script, in order (#4002)", () => {
99
+ const dir = join(repo, "services/api");
100
+ const plan = planMemberComponentPipeline({
101
+ projectDir: dir,
102
+ found: findWorkspaceRoot(dir)!,
103
+ lexicon: "forgejo",
104
+ env: "staging",
105
+ output: join(dir, "pr.yml"),
106
+ prLoop: {},
107
+ image: "node:22-bookworm",
108
+ beforeScript: ["npm ci", 'echo "$PWD/node_modules/.bin" >> "$GITHUB_PATH"'],
109
+ })!;
110
+ expect(plan.command).toBe(
111
+ `chant build --components --generate forgejo --env staging --pr-loop --image node:22-bookworm --before-script 'npm ci' --before-script 'echo "$PWD/node_modules/.bin" >> "$GITHUB_PATH"' --output pr.yml`,
112
+ );
113
+ });
114
+
98
115
  test("the recorded command keeps --wave-gate, --canary and --base (#3049)", () => {
99
116
  const dir = join(repo, "services/api");
100
117
  const plan = planMemberComponentPipeline({
@@ -205,6 +205,8 @@ export interface ComponentPlanInput {
205
205
  /** `--env`, or undefined for the generator's default. */
206
206
  env?: string;
207
207
  promoteTo?: string;
208
+ /** `--allow-skip` (#4030): the generated promote job may skip a step of the promotion order. */
209
+ allowSkip?: boolean;
208
210
  /** `--output`, relative to the current directory. */
209
211
  output?: string;
210
212
  params?: string[];
@@ -213,6 +215,10 @@ export interface ComponentPlanInput {
213
215
  prLoop?: { gate?: string; branch?: string };
214
216
  /** `--wave-gate` with its `--canary` and `--base` (#3049). */
215
217
  waveGate?: { gate: string; canary?: string[]; base?: string };
218
+ /** `--image` (#4002). */
219
+ image?: string;
220
+ /** `--before-script`, in order (#4002). */
221
+ beforeScript?: string[];
216
222
  }
217
223
 
218
224
  /** The generators' own default environment when `--env` is not given. */
@@ -242,6 +248,7 @@ export function planMemberComponentPipeline(input: ComponentPlanInput): MemberCo
242
248
 
243
249
  const parts = ["chant", "build", "--components", "--generate", input.lexicon, "--env", env];
244
250
  if (input.promoteTo) parts.push("--promote-to", input.promoteTo);
251
+ if (input.promoteTo && input.allowSkip) parts.push("--allow-skip");
245
252
  if (input.waveGate) {
246
253
  parts.push("--wave-gate", input.waveGate.gate);
247
254
  if (input.waveGate.canary?.length) parts.push("--canary", input.waveGate.canary.join(","));
@@ -252,6 +259,8 @@ export function planMemberComponentPipeline(input: ComponentPlanInput): MemberCo
252
259
  if (input.prLoop.gate) parts.push("--gate", input.prLoop.gate);
253
260
  if (input.prLoop.branch) parts.push("--branch", input.prLoop.branch);
254
261
  }
262
+ if (input.image) parts.push("--image", input.image);
263
+ for (const line of input.beforeScript ?? []) parts.push("--before-script", line);
255
264
  for (const p of input.params ?? []) parts.push("--param", p);
256
265
  if (input.paramsFile) parts.push("--params-file", relPath(ctx.memberRoot, resolve(input.paramsFile)));
257
266
  if (input.output) parts.push("--output", relPath(ctx.memberRoot, target));