@intentius/chant 0.101.0 → 0.104.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (739) hide show
  1. package/dist/agents/checks.d.ts.map +1 -1
  2. package/dist/attrref.d.ts.map +1 -1
  3. package/dist/audit/core.d.ts +2 -0
  4. package/dist/audit/core.d.ts.map +1 -1
  5. package/dist/audit/discover.d.ts.map +1 -1
  6. package/dist/audit/nginx.d.ts.map +1 -1
  7. package/dist/audit/prior-art.d.ts +48 -0
  8. package/dist/audit/prior-art.d.ts.map +1 -1
  9. package/dist/behaviour-kinds.d.ts.map +1 -1
  10. package/dist/builder.d.ts.map +1 -1
  11. package/dist/change-set.d.ts +232 -0
  12. package/dist/change-set.d.ts.map +1 -0
  13. package/dist/cli/commands/audit.d.ts +7 -0
  14. package/dist/cli/commands/audit.d.ts.map +1 -1
  15. package/dist/cli/commands/build.d.ts.map +1 -1
  16. package/dist/cli/commands/carve-emit.d.ts.map +1 -1
  17. package/dist/cli/commands/check-lexicon.d.ts +16 -0
  18. package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
  19. package/dist/cli/commands/import.d.ts +8 -0
  20. package/dist/cli/commands/import.d.ts.map +1 -1
  21. package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
  22. package/dist/cli/commands/init.d.ts +19 -0
  23. package/dist/cli/commands/init.d.ts.map +1 -1
  24. package/dist/cli/commands/onboard.d.ts +9 -0
  25. package/dist/cli/commands/onboard.d.ts.map +1 -1
  26. package/dist/cli/commands/pinned-upgrade.d.ts +19 -5
  27. package/dist/cli/commands/pinned-upgrade.d.ts.map +1 -1
  28. package/dist/cli/handlers/build.d.ts.map +1 -1
  29. package/dist/cli/handlers/change-set.d.ts +11 -0
  30. package/dist/cli/handlers/change-set.d.ts.map +1 -0
  31. package/dist/cli/handlers/dev.d.ts.map +1 -1
  32. package/dist/cli/handlers/fan-out.d.ts +33 -0
  33. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  34. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  35. package/dist/cli/handlers/misc.d.ts.map +1 -1
  36. package/dist/cli/handlers/operator.d.ts +16 -0
  37. package/dist/cli/handlers/operator.d.ts.map +1 -1
  38. package/dist/cli/handlers/pr.d.ts +31 -0
  39. package/dist/cli/handlers/pr.d.ts.map +1 -0
  40. package/dist/cli/handlers/run.d.ts.map +1 -1
  41. package/dist/cli/lsp/server.d.ts.map +1 -1
  42. package/dist/cli/main.d.ts.map +1 -1
  43. package/dist/cli/mcp/op-tools.d.ts.map +1 -1
  44. package/dist/cli/mcp/server.d.ts.map +1 -1
  45. package/dist/cli/mcp/tools/build.d.ts.map +1 -1
  46. package/dist/cli/mcp/tools/composites.d.ts.map +1 -1
  47. package/dist/cli/mcp/tools/explain.d.ts.map +1 -1
  48. package/dist/cli/mcp/tools/import.d.ts.map +1 -1
  49. package/dist/cli/mcp/tools/lint.d.ts.map +1 -1
  50. package/dist/cli/mcp/tools/scaffold.d.ts.map +1 -1
  51. package/dist/cli/mcp/tools/search.d.ts.map +1 -1
  52. package/dist/cli/mcp/workspace-tools.d.ts +4 -3
  53. package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
  54. package/dist/cli/registry.d.ts +74 -3
  55. package/dist/cli/registry.d.ts.map +1 -1
  56. package/dist/codegen/fetch.d.ts.map +1 -1
  57. package/dist/codegen/naming.d.ts.map +1 -1
  58. package/dist/codegen/pinned-upgrade.d.ts +67 -3
  59. package/dist/codegen/pinned-upgrade.d.ts.map +1 -1
  60. package/dist/codegen/registry.d.ts.map +1 -1
  61. package/dist/components/auto-release.d.ts +16 -0
  62. package/dist/components/auto-release.d.ts.map +1 -1
  63. package/dist/components/capability-plugin-loader.d.ts.map +1 -1
  64. package/dist/components/capability.d.ts +53 -0
  65. package/dist/components/capability.d.ts.map +1 -1
  66. package/dist/components/changed-files.d.ts +31 -0
  67. package/dist/components/changed-files.d.ts.map +1 -0
  68. package/dist/components/cli-support.d.ts +9 -1
  69. package/dist/components/cli-support.d.ts.map +1 -1
  70. package/dist/components/component.d.ts +24 -0
  71. package/dist/components/component.d.ts.map +1 -1
  72. package/dist/components/deploy-units.d.ts.map +1 -1
  73. package/dist/components/driver.d.ts +13 -0
  74. package/dist/components/driver.d.ts.map +1 -1
  75. package/dist/components/fan-out-output.d.ts +6 -0
  76. package/dist/components/fan-out-output.d.ts.map +1 -1
  77. package/dist/components/fan-out-record.d.ts +41 -0
  78. package/dist/components/fan-out-record.d.ts.map +1 -0
  79. package/dist/components/fan-out-run.d.ts +65 -0
  80. package/dist/components/fan-out-run.d.ts.map +1 -1
  81. package/dist/components/fan-out-support.d.ts +2 -0
  82. package/dist/components/fan-out-support.d.ts.map +1 -1
  83. package/dist/components/fan-out.d.ts +6 -0
  84. package/dist/components/fan-out.d.ts.map +1 -1
  85. package/dist/components/gated-wave-pipeline.d.ts +50 -0
  86. package/dist/components/gated-wave-pipeline.d.ts.map +1 -0
  87. package/dist/components/index.d.ts +1 -1
  88. package/dist/components/index.d.ts.map +1 -1
  89. package/dist/components/layers.d.ts +25 -0
  90. package/dist/components/layers.d.ts.map +1 -0
  91. package/dist/components/pr-pipeline.d.ts +61 -0
  92. package/dist/components/pr-pipeline.d.ts.map +1 -0
  93. package/dist/components/pr-run.d.ts +119 -0
  94. package/dist/components/pr-run.d.ts.map +1 -0
  95. package/dist/components/promote.d.ts.map +1 -1
  96. package/dist/components/starter-plugin.d.ts.map +1 -1
  97. package/dist/components/verbs/process-runner.d.ts.map +1 -1
  98. package/dist/components/verbs/r2-sync.d.ts.map +1 -1
  99. package/dist/components/verbs/run-agent.d.ts.map +1 -1
  100. package/dist/components/verbs/sbom-generator.d.ts.map +1 -1
  101. package/dist/components/verbs/shell.d.ts.map +1 -1
  102. package/dist/components/verbs/sign.d.ts.map +1 -1
  103. package/dist/components/verbs/verify.d.ts.map +1 -1
  104. package/dist/components/verbs/vuln-gate.d.ts.map +1 -1
  105. package/dist/components/verbs/vuln-scan.d.ts.map +1 -1
  106. package/dist/components/verbs/wrangler.d.ts.map +1 -1
  107. package/dist/components/wave-plan.d.ts +49 -0
  108. package/dist/components/wave-plan.d.ts.map +1 -0
  109. package/dist/composite.d.ts.map +1 -1
  110. package/dist/config.d.ts +19 -7
  111. package/dist/config.d.ts.map +1 -1
  112. package/dist/declarable.d.ts +16 -0
  113. package/dist/declarable.d.ts.map +1 -1
  114. package/dist/discovery/cache.d.ts.map +1 -1
  115. package/dist/discovery/fold-import.d.ts +15 -5
  116. package/dist/discovery/fold-import.d.ts.map +1 -1
  117. package/dist/discovery/param-deps.d.ts +16 -0
  118. package/dist/discovery/param-deps.d.ts.map +1 -1
  119. package/dist/discovery/sandbox/policy-wire.d.ts.map +1 -1
  120. package/dist/discovery/specifier-resolve.d.ts +37 -0
  121. package/dist/discovery/specifier-resolve.d.ts.map +1 -0
  122. package/dist/errors.d.ts.map +1 -1
  123. package/dist/fold/fold.d.ts +3 -1
  124. package/dist/fold/fold.d.ts.map +1 -1
  125. package/dist/gated-waves.d.ts +147 -0
  126. package/dist/gated-waves.d.ts.map +1 -0
  127. package/dist/graph-detail.d.ts.map +1 -1
  128. package/dist/graph-ir.d.ts.map +1 -1
  129. package/dist/graph-layout.d.ts.map +1 -1
  130. package/dist/import/base-parser.d.ts.map +1 -1
  131. package/dist/import/embedded.d.ts +7 -0
  132. package/dist/import/embedded.d.ts.map +1 -1
  133. package/dist/import/parser-options.d.ts +14 -0
  134. package/dist/import/parser-options.d.ts.map +1 -0
  135. package/dist/import/parser.d.ts +17 -0
  136. package/dist/import/parser.d.ts.map +1 -1
  137. package/dist/index.d.ts +1 -0
  138. package/dist/index.d.ts.map +1 -1
  139. package/dist/kubectl-context.d.ts.map +1 -1
  140. package/dist/lexicon-output.d.ts.map +1 -1
  141. package/dist/lexicon-schema.d.ts +3 -3
  142. package/dist/lexicon.d.ts +103 -3
  143. package/dist/lexicon.d.ts.map +1 -1
  144. package/dist/lifecycle/assert-live.d.ts.map +1 -1
  145. package/dist/lifecycle/gate-ledger.d.ts +22 -0
  146. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  147. package/dist/lifecycle/git.d.ts +46 -1
  148. package/dist/lifecycle/git.d.ts.map +1 -1
  149. package/dist/lifecycle/member-ledger.d.ts.map +1 -1
  150. package/dist/lifecycle/observe.d.ts.map +1 -1
  151. package/dist/lifecycle/plan-ledger.d.ts.map +1 -1
  152. package/dist/lifecycle/release-ledger.d.ts.map +1 -1
  153. package/dist/lifecycle/scenario.d.ts.map +1 -1
  154. package/dist/lifecycle/status.d.ts.map +1 -1
  155. package/dist/lifecycle/work-lease.d.ts +61 -2
  156. package/dist/lifecycle/work-lease.d.ts.map +1 -1
  157. package/dist/lint/config.d.ts +8 -8
  158. package/dist/lint/engine.d.ts +6 -0
  159. package/dist/lint/engine.d.ts.map +1 -1
  160. package/dist/lint/post-synth.d.ts +18 -0
  161. package/dist/lint/post-synth.d.ts.map +1 -1
  162. package/dist/lsp/lexicon-providers.d.ts.map +1 -1
  163. package/dist/op/activities/activity-contracts.d.ts +137 -11
  164. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  165. package/dist/op/activities/apply.d.ts +79 -9
  166. package/dist/op/activities/apply.d.ts.map +1 -1
  167. package/dist/op/activities/change-set.d.ts +62 -0
  168. package/dist/op/activities/change-set.d.ts.map +1 -0
  169. package/dist/op/activities/decide.d.ts +2 -0
  170. package/dist/op/activities/decide.d.ts.map +1 -1
  171. package/dist/op/activities/factory.d.ts +137 -0
  172. package/dist/op/activities/factory.d.ts.map +1 -0
  173. package/dist/op/activities/index.d.ts +5 -1
  174. package/dist/op/activities/index.d.ts.map +1 -1
  175. package/dist/op/activities/lexicon-upgrade.d.ts +44 -12
  176. package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -1
  177. package/dist/op/activity-contract.d.ts +1 -1
  178. package/dist/op/activity-contract.d.ts.map +1 -1
  179. package/dist/op/activity-failure.d.ts.map +1 -1
  180. package/dist/op/activity-profiles.d.ts.map +1 -1
  181. package/dist/op/builders.d.ts +11 -0
  182. package/dist/op/builders.d.ts.map +1 -1
  183. package/dist/op/composites/apply-op.d.ts +12 -5
  184. package/dist/op/composites/apply-op.d.ts.map +1 -1
  185. package/dist/op/composites/lexicon-upgrade-op.d.ts +22 -6
  186. package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -1
  187. package/dist/op/decide-backend.d.ts +5 -1
  188. package/dist/op/decide-backend.d.ts.map +1 -1
  189. package/dist/op/decide-read-inputs.d.ts +4 -2
  190. package/dist/op/decide-read-inputs.d.ts.map +1 -1
  191. package/dist/op/emulator-lifecycle.d.ts +21 -2
  192. package/dist/op/emulator-lifecycle.d.ts.map +1 -1
  193. package/dist/op/factory.d.ts +83 -0
  194. package/dist/op/factory.d.ts.map +1 -0
  195. package/dist/op/gate-approval.d.ts +5 -1
  196. package/dist/op/gate-approval.d.ts.map +1 -1
  197. package/dist/op/gate-wait.d.ts.map +1 -1
  198. package/dist/op/gate.d.ts +19 -3
  199. package/dist/op/gate.d.ts.map +1 -1
  200. package/dist/op/index.d.ts +6 -1
  201. package/dist/op/index.d.ts.map +1 -1
  202. package/dist/op/local-executor.d.ts +6 -0
  203. package/dist/op/local-executor.d.ts.map +1 -1
  204. package/dist/op/op-ir.d.ts +2 -0
  205. package/dist/op/op-ir.d.ts.map +1 -1
  206. package/dist/op/op-verb-class.d.ts.map +1 -1
  207. package/dist/op/operator.d.ts.map +1 -1
  208. package/dist/op/run-context.d.ts +39 -0
  209. package/dist/op/run-context.d.ts.map +1 -0
  210. package/dist/op/run-live.d.ts.map +1 -1
  211. package/dist/op/runtimes/local.d.ts.map +1 -1
  212. package/dist/op/source-archive.d.ts.map +1 -1
  213. package/dist/op/steward-beside.d.ts.map +1 -1
  214. package/dist/op/steward-points.d.ts.map +1 -1
  215. package/dist/op/steward.d.ts.map +1 -1
  216. package/dist/op/types.d.ts +16 -3
  217. package/dist/op/types.d.ts.map +1 -1
  218. package/dist/op/work-lease-run.d.ts +24 -4
  219. package/dist/op/work-lease-run.d.ts.map +1 -1
  220. package/dist/plan-summary.d.ts +207 -0
  221. package/dist/plan-summary.d.ts.map +1 -0
  222. package/dist/pr-forge.d.ts +105 -0
  223. package/dist/pr-forge.d.ts.map +1 -0
  224. package/dist/pr-loop.d.ts +178 -0
  225. package/dist/pr-loop.d.ts.map +1 -0
  226. package/dist/project-root.d.ts.map +1 -1
  227. package/dist/provenance.d.ts +21 -0
  228. package/dist/provenance.d.ts.map +1 -1
  229. package/dist/pseudo-parameter.d.ts.map +1 -1
  230. package/dist/reconcile.d.ts.map +1 -1
  231. package/dist/secret-materialization.d.ts.map +1 -1
  232. package/dist/telemetry-attribution.d.ts +31 -0
  233. package/dist/telemetry-attribution.d.ts.map +1 -1
  234. package/dist/terraform/emit-source.d.ts.map +1 -1
  235. package/dist/terraform/hcl2json.d.ts +45 -0
  236. package/dist/terraform/hcl2json.d.ts.map +1 -0
  237. package/dist/terraform/parse.d.ts +4 -40
  238. package/dist/terraform/parse.d.ts.map +1 -1
  239. package/dist/testing.d.ts.map +1 -1
  240. package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -1
  241. package/dist/workspace/__fixtures__/decide-workspace.d.ts +1 -1
  242. package/dist/workspace/__fixtures__/decide-workspace.d.ts.map +1 -1
  243. package/dist/workspace/agent-cli.d.ts +5 -0
  244. package/dist/workspace/agent-cli.d.ts.map +1 -1
  245. package/dist/workspace/box-cli.d.ts +12 -0
  246. package/dist/workspace/box-cli.d.ts.map +1 -0
  247. package/dist/workspace/box-factory.d.ts +97 -0
  248. package/dist/workspace/box-factory.d.ts.map +1 -0
  249. package/dist/workspace/box-listing.d.ts +85 -0
  250. package/dist/workspace/box-listing.d.ts.map +1 -0
  251. package/dist/workspace/box-publish.d.ts +147 -0
  252. package/dist/workspace/box-publish.d.ts.map +1 -0
  253. package/dist/workspace/broker-protocol.d.ts +190 -0
  254. package/dist/workspace/broker-protocol.d.ts.map +1 -0
  255. package/dist/workspace/changes.d.ts.map +1 -1
  256. package/dist/workspace/chant-migrations/chud-lexicon-exit.d.ts.map +1 -1
  257. package/dist/workspace/checks/boxes.d.ts.map +1 -1
  258. package/dist/workspace/checks.d.ts +5 -0
  259. package/dist/workspace/checks.d.ts.map +1 -1
  260. package/dist/workspace/compose-graph.d.ts +10 -0
  261. package/dist/workspace/compose-graph.d.ts.map +1 -1
  262. package/dist/workspace/composites.d.ts +2 -2
  263. package/dist/workspace/composites.d.ts.map +1 -1
  264. package/dist/workspace/conformance/__fixtures__/reference-broker.d.ts +17 -0
  265. package/dist/workspace/conformance/__fixtures__/reference-broker.d.ts.map +1 -0
  266. package/dist/workspace/conformance/broker.d.ts +202 -0
  267. package/dist/workspace/conformance/broker.d.ts.map +1 -0
  268. package/dist/workspace/conformance/index.d.ts +68 -6
  269. package/dist/workspace/conformance/index.d.ts.map +1 -1
  270. package/dist/workspace/conformance/vitest.d.ts +19 -3
  271. package/dist/workspace/conformance/vitest.d.ts.map +1 -1
  272. package/dist/workspace/conformance/writer.d.ts +462 -0
  273. package/dist/workspace/conformance/writer.d.ts.map +1 -0
  274. package/dist/workspace/decide.d.ts +126 -12
  275. package/dist/workspace/decide.d.ts.map +1 -1
  276. package/dist/workspace/decision-points.schema.json +11 -4
  277. package/dist/workspace/declaration.d.ts +156 -4
  278. package/dist/workspace/declaration.d.ts.map +1 -1
  279. package/dist/workspace/declaration.schema.json +383 -5
  280. package/dist/workspace/environments.d.ts +36 -10
  281. package/dist/workspace/environments.d.ts.map +1 -1
  282. package/dist/workspace/factory-rules.d.ts +180 -0
  283. package/dist/workspace/factory-rules.d.ts.map +1 -0
  284. package/dist/workspace/generated-files.d.ts.map +1 -1
  285. package/dist/workspace/graph-cache.d.ts.map +1 -1
  286. package/dist/workspace/graph-cli.d.ts +2 -0
  287. package/dist/workspace/graph-cli.d.ts.map +1 -1
  288. package/dist/workspace/identity.d.ts +111 -0
  289. package/dist/workspace/identity.d.ts.map +1 -0
  290. package/dist/workspace/init.d.ts +21 -0
  291. package/dist/workspace/init.d.ts.map +1 -1
  292. package/dist/workspace/intent-cli.d.ts +7 -4
  293. package/dist/workspace/intent-cli.d.ts.map +1 -1
  294. package/dist/workspace/intent-record.d.ts +36 -2
  295. package/dist/workspace/intent-record.d.ts.map +1 -1
  296. package/dist/workspace/intent.d.ts +249 -8
  297. package/dist/workspace/intent.d.ts.map +1 -1
  298. package/dist/workspace/json-edit.d.ts +24 -0
  299. package/dist/workspace/json-edit.d.ts.map +1 -0
  300. package/dist/workspace/jsonc.d.ts +11 -0
  301. package/dist/workspace/jsonc.d.ts.map +1 -1
  302. package/dist/workspace/kinds.d.ts +66 -4
  303. package/dist/workspace/kinds.d.ts.map +1 -1
  304. package/dist/workspace/lexicon-symbols.d.ts +29 -0
  305. package/dist/workspace/lexicon-symbols.d.ts.map +1 -0
  306. package/dist/workspace/lineage-hash-index.d.ts.map +1 -1
  307. package/dist/workspace/lineage-lock.d.ts +2 -2
  308. package/dist/workspace/lineage-lock.d.ts.map +1 -1
  309. package/dist/workspace/lineage-upgrade.d.ts +31 -2
  310. package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
  311. package/dist/workspace/links.d.ts.map +1 -1
  312. package/dist/workspace/point-answer.schema.json +42 -2
  313. package/dist/workspace/points-cli.d.ts +10 -3
  314. package/dist/workspace/points-cli.d.ts.map +1 -1
  315. package/dist/workspace/points.d.ts +66 -5
  316. package/dist/workspace/points.d.ts.map +1 -1
  317. package/dist/workspace/principal-classes.d.ts +114 -0
  318. package/dist/workspace/principal-classes.d.ts.map +1 -0
  319. package/dist/workspace/reason-codes.d.ts +53 -4
  320. package/dist/workspace/reason-codes.d.ts.map +1 -1
  321. package/dist/workspace/record-assets.d.ts.map +1 -1
  322. package/dist/workspace/record-sessions.d.ts +5 -7
  323. package/dist/workspace/record-sessions.d.ts.map +1 -1
  324. package/dist/workspace/records-checkout.d.ts +54 -0
  325. package/dist/workspace/records-checkout.d.ts.map +1 -0
  326. package/dist/workspace/records-cli.d.ts +34 -1
  327. package/dist/workspace/records-cli.d.ts.map +1 -1
  328. package/dist/workspace/records-close.d.ts +7 -3
  329. package/dist/workspace/records-close.d.ts.map +1 -1
  330. package/dist/workspace/records-since.d.ts.map +1 -1
  331. package/dist/workspace/records-write.d.ts +68 -7
  332. package/dist/workspace/records-write.d.ts.map +1 -1
  333. package/dist/workspace/records.d.ts +43 -9
  334. package/dist/workspace/records.d.ts.map +1 -1
  335. package/dist/workspace/runs-cli.d.ts +296 -0
  336. package/dist/workspace/runs-cli.d.ts.map +1 -0
  337. package/dist/workspace/runs.d.ts +544 -0
  338. package/dist/workspace/runs.d.ts.map +1 -0
  339. package/dist/workspace/session-kinds.d.ts +1 -1
  340. package/dist/workspace/session-kinds.d.ts.map +1 -1
  341. package/dist/workspace/source-block.d.ts +1 -1
  342. package/dist/workspace/source-block.d.ts.map +1 -1
  343. package/dist/workspace/squash.d.ts +80 -0
  344. package/dist/workspace/squash.d.ts.map +1 -0
  345. package/dist/workspace/status-gates.d.ts +17 -3
  346. package/dist/workspace/status-gates.d.ts.map +1 -1
  347. package/dist/workspace/status.d.ts +64 -0
  348. package/dist/workspace/status.d.ts.map +1 -1
  349. package/dist/workspace/symbols.d.ts +63 -0
  350. package/dist/workspace/symbols.d.ts.map +1 -0
  351. package/dist/workspace/trailer-joins.d.ts +46 -0
  352. package/dist/workspace/trailer-joins.d.ts.map +1 -0
  353. package/dist/workspace/trailers.d.ts +89 -0
  354. package/dist/workspace/trailers.d.ts.map +1 -0
  355. package/dist/workspace/trust/attestor.d.ts.map +1 -1
  356. package/dist/workspace/trust/rotation.d.ts.map +1 -1
  357. package/dist/workspace/trust/run-commits.d.ts +46 -0
  358. package/dist/workspace/trust/run-commits.d.ts.map +1 -0
  359. package/dist/workspace/trust/run-statement.d.ts +128 -0
  360. package/dist/workspace/trust/run-statement.d.ts.map +1 -0
  361. package/dist/workspace/trust/seal.d.ts +38 -0
  362. package/dist/workspace/trust/seal.d.ts.map +1 -1
  363. package/dist/workspace/trust/test-repo.d.ts.map +1 -1
  364. package/dist/workspace/trust/verify-cli.d.ts +1 -1
  365. package/dist/workspace/trust/verify.d.ts +16 -2
  366. package/dist/workspace/trust/verify.d.ts.map +1 -1
  367. package/dist/workspace/wip-cli.d.ts +21 -0
  368. package/dist/workspace/wip-cli.d.ts.map +1 -0
  369. package/dist/workspace/wip.d.ts +268 -0
  370. package/dist/workspace/wip.d.ts.map +1 -0
  371. package/dist/workspace/work-cli.d.ts +39 -3
  372. package/dist/workspace/work-cli.d.ts.map +1 -1
  373. package/dist/workspace/work-evidence.d.ts +1 -1
  374. package/dist/workspace/work-evidence.d.ts.map +1 -1
  375. package/dist/workspace/work-size.d.ts +55 -0
  376. package/dist/workspace/work-size.d.ts.map +1 -0
  377. package/dist/workspace/work.d.ts +26 -1
  378. package/dist/workspace/work.d.ts.map +1 -1
  379. package/dist/workspace/workspace-kinds.schema.json +122 -0
  380. package/dist/workspace/workspace-principals.schema.json +60 -0
  381. package/dist/workspace/write-lock-cli.d.ts +60 -0
  382. package/dist/workspace/write-lock-cli.d.ts.map +1 -0
  383. package/dist/workspace/write-lock.d.ts +152 -0
  384. package/dist/workspace/write-lock.d.ts.map +1 -0
  385. package/dist/workspace/write-scope.d.ts +83 -19
  386. package/dist/workspace/write-scope.d.ts.map +1 -1
  387. package/dist/yaml.d.ts +21 -39
  388. package/dist/yaml.d.ts.map +1 -1
  389. package/package.json +7 -2
  390. package/src/__fixtures__/change-set/lifecycle-plan.floci.json +59 -0
  391. package/src/__fixtures__/change-set/lifecycle-v1.ts.txt +4 -0
  392. package/src/__fixtures__/change-set/lifecycle-v2.ts.txt +4 -0
  393. package/src/__fixtures__/change-set/lifecycle.change-set.golden.json +95 -0
  394. package/src/__fixtures__/change-set/warden.change-set.golden.json +106 -0
  395. package/src/__fixtures__/change-set/warden.plan.json +44 -0
  396. package/src/__fixtures__/plan-summary/normalization-vectors.json +1593 -0
  397. package/src/__fixtures__/pr-loop/pr-apply-applied.note.golden.md +31 -0
  398. package/src/__fixtures__/pr-loop/pr-apply-refused.note.golden.md +31 -0
  399. package/src/__fixtures__/pr-loop/pr-plan.golden.json +127 -0
  400. package/src/__fixtures__/pr-loop/pr-plan.note.golden.md +37 -0
  401. package/src/audit/core.test.ts +15 -0
  402. package/src/audit/core.ts +8 -2
  403. package/src/audit/discover.test.ts +11 -0
  404. package/src/audit/discover.ts +7 -2
  405. package/src/audit/prior-art.ts +15 -0
  406. package/src/change-set-bundle.test.ts +57 -0
  407. package/src/change-set.test.ts +215 -0
  408. package/src/change-set.ts +445 -0
  409. package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +1 -1
  410. package/src/cli/commands/audit-opt-in.test.ts +54 -0
  411. package/src/cli/commands/audit.ts +29 -2
  412. package/src/cli/commands/build.test.ts +19 -0
  413. package/src/cli/commands/build.ts +3 -2
  414. package/src/cli/commands/carve-emit-state.test.ts +4 -0
  415. package/src/cli/commands/carve-emit.ts +5 -19
  416. package/src/cli/commands/check-lexicon.test.ts +62 -1
  417. package/src/cli/commands/check-lexicon.ts +36 -6
  418. package/src/cli/commands/import-live.test.ts +18 -0
  419. package/src/cli/commands/import-parser-options.test.ts +133 -0
  420. package/src/cli/commands/import.ts +21 -3
  421. package/src/cli/commands/init-lexicon/templates/project.ts +1 -1
  422. package/src/cli/commands/init.test.ts +8 -1
  423. package/src/cli/commands/init.ts +23 -8
  424. package/src/cli/commands/lint.test.ts +37 -0
  425. package/src/cli/commands/onboard.test.ts +53 -3
  426. package/src/cli/commands/onboard.ts +52 -4
  427. package/src/cli/commands/pinned-upgrade.ts +73 -33
  428. package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +2 -2
  429. package/src/cli/handlers/build.ts +13 -1
  430. package/src/cli/handlers/change-set.test.ts +56 -0
  431. package/src/cli/handlers/change-set.ts +44 -0
  432. package/src/cli/handlers/components.ts +2 -2
  433. package/src/cli/handlers/dev.ts +8 -6
  434. package/src/cli/handlers/fan-out.test.ts +221 -2
  435. package/src/cli/handlers/fan-out.ts +165 -75
  436. package/src/cli/handlers/graph.ts +3 -0
  437. package/src/cli/handlers/lifecycle.test.ts +67 -0
  438. package/src/cli/handlers/lifecycle.ts +6 -5
  439. package/src/cli/handlers/misc.ts +2 -0
  440. package/src/cli/handlers/operator.test.ts +21 -0
  441. package/src/cli/handlers/operator.ts +121 -6
  442. package/src/cli/handlers/pr.ts +373 -0
  443. package/src/cli/handlers/run.test.ts +111 -0
  444. package/src/cli/handlers/run.ts +86 -24
  445. package/src/cli/handlers/scenario.ts +2 -2
  446. package/src/cli/main.test.ts +21 -0
  447. package/src/cli/main.ts +246 -30
  448. package/src/cli/mcp/never-over-mcp.test.ts +101 -0
  449. package/src/cli/mcp/op-tools.ts +24 -2
  450. package/src/cli/mcp/workspace-tools.test.ts +5 -2
  451. package/src/cli/mcp/workspace-tools.ts +62 -7
  452. package/src/cli/registry.ts +74 -3
  453. package/src/codegen/pinned-upgrade-multi.test.ts +157 -0
  454. package/src/codegen/pinned-upgrade-tags.test.ts +112 -0
  455. package/src/codegen/pinned-upgrade.test.ts +104 -0
  456. package/src/codegen/pinned-upgrade.ts +250 -56
  457. package/src/codegen/testdata/postgres-tags.json +242 -0
  458. package/src/components/auto-release.ts +1 -1
  459. package/src/components/capability.ts +55 -0
  460. package/src/components/changed-files.ts +66 -0
  461. package/src/components/cli-support.ts +28 -5
  462. package/src/components/component.schema.json +17 -0
  463. package/src/components/component.ts +25 -0
  464. package/src/components/deploy-units.ts +3 -0
  465. package/src/components/driver.test.ts +52 -0
  466. package/src/components/driver.ts +31 -13
  467. package/src/components/fan-out-output.ts +42 -1
  468. package/src/components/fan-out-record.ts +69 -0
  469. package/src/components/fan-out-run.test.ts +17 -0
  470. package/src/components/fan-out-run.ts +304 -1
  471. package/src/components/fan-out-support.ts +3 -0
  472. package/src/components/fan-out-waves.test.ts +375 -0
  473. package/src/components/fan-out.ts +18 -17
  474. package/src/components/gated-wave-pipeline.ts +82 -0
  475. package/src/components/index.ts +1 -0
  476. package/src/components/layers.ts +30 -0
  477. package/src/components/pr-pipeline.ts +91 -0
  478. package/src/components/pr-run.test.ts +205 -0
  479. package/src/components/pr-run.ts +280 -0
  480. package/src/components/promote.ts +5 -0
  481. package/src/components/verbs/shell.test.ts +11 -0
  482. package/src/components/verbs/shell.ts +7 -3
  483. package/src/components/wave-plan.ts +124 -0
  484. package/src/config.test.ts +21 -0
  485. package/src/config.ts +15 -0
  486. package/src/declarable.test.ts +21 -0
  487. package/src/declarable.ts +19 -0
  488. package/src/discovery/fold-composite.test.ts +41 -5
  489. package/src/discovery/fold-import.test.ts +129 -0
  490. package/src/discovery/fold-import.ts +643 -131
  491. package/src/discovery/fold-package-composite.test.ts +264 -0
  492. package/src/discovery/fold-specifiers.test.ts +191 -0
  493. package/src/discovery/param-deps.test.ts +66 -1
  494. package/src/discovery/param-deps.ts +45 -0
  495. package/src/discovery/sandbox/bundle.ts +1 -1
  496. package/src/discovery/specifier-resolve.ts +344 -0
  497. package/src/fold/fold.test.ts +39 -1
  498. package/src/fold/fold.ts +83 -9
  499. package/src/fold-provenance.test.ts +140 -0
  500. package/src/gated-waves-bundle.test.ts +62 -0
  501. package/src/gated-waves.test.ts +118 -0
  502. package/src/gated-waves.ts +240 -0
  503. package/src/graph-ir.test.ts +83 -0
  504. package/src/graph-ir.ts +29 -0
  505. package/src/import/embedded.test.ts +20 -0
  506. package/src/import/embedded.ts +13 -0
  507. package/src/import/parser-options.ts +46 -0
  508. package/src/import/parser.ts +20 -0
  509. package/src/index.ts +1 -0
  510. package/src/lexicon.ts +106 -3
  511. package/src/lifecycle/gate-ledger.ts +23 -0
  512. package/src/lifecycle/git.ts +102 -16
  513. package/src/lifecycle/observe.test.ts +18 -0
  514. package/src/lifecycle/observe.ts +6 -4
  515. package/src/lifecycle/snapshot.ts +2 -2
  516. package/src/lifecycle/work-lease.test.ts +1 -1
  517. package/src/lifecycle/work-lease.ts +93 -2
  518. package/src/lint/engine.test.ts +55 -0
  519. package/src/lint/engine.ts +30 -4
  520. package/src/lint/output-docs.test.ts +59 -56
  521. package/src/lint/post-synth.test.ts +19 -0
  522. package/src/lint/post-synth.ts +19 -1
  523. package/src/meta/declared-imports.test.ts +1 -1
  524. package/src/op/activities/activity-contracts.ts +84 -0
  525. package/src/op/activities/apply.test.ts +189 -5
  526. package/src/op/activities/apply.ts +230 -8
  527. package/src/op/activities/change-set.ts +114 -0
  528. package/src/op/activities/decide.ts +3 -0
  529. package/src/op/activities/factory.ts +675 -0
  530. package/src/op/activities/index.ts +9 -0
  531. package/src/op/activities/lexicon-upgrade.test.ts +160 -6
  532. package/src/op/activities/lexicon-upgrade.ts +154 -51
  533. package/src/op/activity-contract.test.ts +21 -1
  534. package/src/op/activity-contract.ts +19 -9
  535. package/src/op/builders.ts +23 -0
  536. package/src/op/composites/apply-op.ts +12 -5
  537. package/src/op/composites/composites.test.ts +30 -3
  538. package/src/op/composites/lexicon-upgrade-op.test.ts +7 -2
  539. package/src/op/composites/lexicon-upgrade-op.ts +29 -8
  540. package/src/op/decide-backend.ts +5 -1
  541. package/src/op/decide-read-inputs.ts +33 -5
  542. package/src/op/emulator-lifecycle.test.ts +23 -0
  543. package/src/op/emulator-lifecycle.ts +52 -14
  544. package/src/op/factory.e2e.test.ts +232 -0
  545. package/src/op/factory.ts +118 -0
  546. package/src/op/gate-approval.test.ts +74 -0
  547. package/src/op/gate-approval.ts +5 -1
  548. package/src/op/gate.ts +31 -5
  549. package/src/op/index.ts +11 -1
  550. package/src/op/local-executor.test.ts +15 -0
  551. package/src/op/local-executor.ts +40 -12
  552. package/src/op/local-output.ts +1 -1
  553. package/src/op/op-ir.test.ts +9 -0
  554. package/src/op/op-ir.ts +4 -0
  555. package/src/op/op-verb-class.ts +3 -0
  556. package/src/op/operator.ts +4 -0
  557. package/src/op/run-context.test.ts +141 -0
  558. package/src/op/run-context.ts +51 -0
  559. package/src/op/runtimes/local.test.ts +14 -0
  560. package/src/op/runtimes/local.ts +2 -0
  561. package/src/op/steward-beside.ts +2 -0
  562. package/src/op/types.ts +16 -3
  563. package/src/op/work-lease-run.test.ts +47 -0
  564. package/src/op/work-lease-run.ts +76 -11
  565. package/src/plan-summary.test.ts +263 -0
  566. package/src/plan-summary.ts +845 -0
  567. package/src/pr-forge.test.ts +237 -0
  568. package/src/pr-forge.ts +332 -0
  569. package/src/pr-loop-bundle.test.ts +46 -0
  570. package/src/pr-loop.test.ts +171 -0
  571. package/src/pr-loop.ts +324 -0
  572. package/src/provenance.ts +35 -0
  573. package/src/telemetry-attribution.test.ts +23 -1
  574. package/src/telemetry-attribution.ts +51 -0
  575. package/src/terraform/hcl2json.test.ts +21 -0
  576. package/src/terraform/hcl2json.ts +81 -0
  577. package/src/terraform/parse.ts +11 -74
  578. package/src/workspace/agent-cli.ts +7 -3
  579. package/src/workspace/agent.schema.json +22 -0
  580. package/src/workspace/app-kind.test.ts +196 -0
  581. package/src/workspace/box-cli.ts +113 -0
  582. package/src/workspace/box-factory.test.ts +323 -0
  583. package/src/workspace/box-factory.ts +136 -0
  584. package/src/workspace/box-listing-write.schema.json +204 -0
  585. package/src/workspace/box-listing.test.ts +234 -0
  586. package/src/workspace/box-listing.ts +324 -0
  587. package/src/workspace/box-publish.schema.json +498 -0
  588. package/src/workspace/box-publish.test.ts +204 -0
  589. package/src/workspace/box-publish.ts +420 -0
  590. package/src/workspace/box-ship.test.ts +96 -0
  591. package/src/workspace/broker-protocol.schema.json +186 -0
  592. package/src/workspace/broker-protocol.test.ts +122 -0
  593. package/src/workspace/broker-protocol.ts +317 -0
  594. package/src/workspace/builder-tiers.test.ts +146 -0
  595. package/src/workspace/change-set.schema.json +460 -0
  596. package/src/workspace/changes.schema.json +12 -20
  597. package/src/workspace/changes.ts +4 -1
  598. package/src/workspace/chant-migrations.e2e.test.ts +17 -5
  599. package/src/workspace/checks/boxes.test.ts +5 -0
  600. package/src/workspace/checks.ts +39 -8
  601. package/src/workspace/component-environments.test.ts +105 -0
  602. package/src/workspace/compose-graph.ts +7 -0
  603. package/src/workspace/composites.schema.json +23 -4
  604. package/src/workspace/composites.test.ts +8 -8
  605. package/src/workspace/composites.ts +13 -2
  606. package/src/workspace/conformance/__fixture__/decisions/decision.schema.json +2 -2
  607. package/src/workspace/conformance/__fixtures__/reference-broker.ts +207 -0
  608. package/src/workspace/conformance/__writer_fixture__/answers/answer.kind.mjs +32 -0
  609. package/src/workspace/conformance/__writer_fixture__/answers/answer.schema.json +135 -0
  610. package/src/workspace/conformance/__writer_fixture__/contracts/contract.kind.mjs +37 -0
  611. package/src/workspace/conformance/__writer_fixture__/contracts/contract.schema.json +213 -0
  612. package/src/workspace/conformance/__writer_fixture__/decisions/points.json +228 -0
  613. package/src/workspace/conformance/__writer_fixture__/drivers/driver.kind.mjs +15 -0
  614. package/src/workspace/conformance/__writer_fixture__/drivers/driver.schema.json +38 -0
  615. package/src/workspace/conformance/__writer_fixture__/sessions/session.kind.mjs +54 -0
  616. package/src/workspace/conformance/__writer_fixture__/sessions/session.schema.json +289 -0
  617. package/src/workspace/conformance/__writer_fixture__/work/W-001-the-writer-suite-works-this-item.md +25 -0
  618. package/src/workspace/conformance/__writer_fixture__/work/work.kind.mjs +60 -0
  619. package/src/workspace/conformance/__writer_fixture__/work/work.schema.json +603 -0
  620. package/src/workspace/conformance/broker.test.ts +122 -0
  621. package/src/workspace/conformance/broker.ts +792 -0
  622. package/src/workspace/conformance/conformance.e2e.test.ts +36 -3
  623. package/src/workspace/conformance/index.mjs +51 -3
  624. package/src/workspace/conformance/index.ts +107 -15
  625. package/src/workspace/conformance/vitest.ts +76 -2
  626. package/src/workspace/conformance/writer-conformance.e2e.test.ts +177 -0
  627. package/src/workspace/conformance/writer.test.ts +186 -0
  628. package/src/workspace/conformance/writer.ts +1191 -0
  629. package/src/workspace/decide.test.ts +208 -4
  630. package/src/workspace/decide.ts +433 -73
  631. package/src/workspace/decision-points.schema.json +11 -4
  632. package/src/workspace/declaration.schema.json +383 -5
  633. package/src/workspace/declaration.ts +315 -10
  634. package/src/workspace/environments.ts +66 -14
  635. package/src/workspace/factory-rules.test.ts +62 -0
  636. package/src/workspace/factory-rules.ts +218 -0
  637. package/src/workspace/graph-cli.ts +6 -3
  638. package/src/workspace/graph.schema.json +314 -0
  639. package/src/workspace/identity.test.ts +219 -0
  640. package/src/workspace/identity.ts +186 -0
  641. package/src/workspace/init.ts +82 -2
  642. package/src/workspace/intent-cli.ts +84 -13
  643. package/src/workspace/intent-patch-id.test.ts +162 -0
  644. package/src/workspace/intent-record.schema.json +336 -1
  645. package/src/workspace/intent-record.ts +96 -16
  646. package/src/workspace/intent-reviews.test.ts +76 -0
  647. package/src/workspace/intent-squash.test.ts +213 -0
  648. package/src/workspace/intent-why.test.ts +274 -0
  649. package/src/workspace/intent.schema.json +950 -9
  650. package/src/workspace/intent.test.ts +11 -2
  651. package/src/workspace/intent.ts +722 -65
  652. package/src/workspace/json-edit.test.ts +121 -0
  653. package/src/workspace/json-edit.ts +142 -0
  654. package/src/workspace/jsonc.ts +20 -0
  655. package/src/workspace/kinds.ts +201 -39
  656. package/src/workspace/lexicon-symbols.test.ts +148 -0
  657. package/src/workspace/lexicon-symbols.ts +125 -0
  658. package/src/workspace/lineage-upgrade.test.ts +63 -0
  659. package/src/workspace/lineage-upgrade.ts +146 -5
  660. package/src/workspace/plan-summary.schema.json +155 -0
  661. package/src/workspace/point-answer.schema.json +42 -2
  662. package/src/workspace/points-cli.ts +42 -16
  663. package/src/workspace/points-write.schema.json +163 -4
  664. package/src/workspace/points.schema.json +157 -2
  665. package/src/workspace/points.test.ts +13 -1
  666. package/src/workspace/points.ts +118 -12
  667. package/src/workspace/pr-report.schema.json +95 -0
  668. package/src/workspace/principal-classes.test.ts +175 -0
  669. package/src/workspace/principal-classes.ts +307 -0
  670. package/src/workspace/profiles.test.ts +159 -0
  671. package/src/workspace/read-contract.test.ts +2 -2
  672. package/src/workspace/reason-codes.test.ts +23 -3
  673. package/src/workspace/reason-codes.ts +61 -4
  674. package/src/workspace/record-sessions.ts +9 -8
  675. package/src/workspace/records-amend.schema.json +80 -0
  676. package/src/workspace/records-checkout.ts +106 -0
  677. package/src/workspace/records-cli.ts +123 -17
  678. package/src/workspace/records-close.schema.json +79 -0
  679. package/src/workspace/records-close.ts +31 -7
  680. package/src/workspace/records-concurrent.e2e.test.ts +135 -0
  681. package/src/workspace/records-concurrent.test.ts +145 -0
  682. package/src/workspace/records-formats.test.ts +46 -10
  683. package/src/workspace/records-new.schema.json +9 -0
  684. package/src/workspace/records-review.schema.json +80 -0
  685. package/src/workspace/records-sessions-write.test.ts +69 -0
  686. package/src/workspace/records-sessions.test.ts +22 -0
  687. package/src/workspace/records-uncommitted.test.ts +171 -0
  688. package/src/workspace/records-write.ts +219 -30
  689. package/src/workspace/records.schema.json +88 -1
  690. package/src/workspace/records.ts +76 -8
  691. package/src/workspace/reference-kinds/app/package.json +10 -0
  692. package/src/workspace/reference-kinds/app/workspace-kinds.json +45 -0
  693. package/src/workspace/run-statement.schema.json +622 -0
  694. package/src/workspace/runs-cli.ts +710 -0
  695. package/src/workspace/runs-statement.test.ts +239 -0
  696. package/src/workspace/runs-write.schema.json +994 -0
  697. package/src/workspace/runs.schema.json +1257 -0
  698. package/src/workspace/runs.test.ts +281 -0
  699. package/src/workspace/runs.ts +970 -0
  700. package/src/workspace/session-kinds.ts +3 -3
  701. package/src/workspace/squash.ts +198 -0
  702. package/src/workspace/status-contract.test.ts +10 -2
  703. package/src/workspace/status-gates.ts +25 -9
  704. package/src/workspace/status.schema.json +252 -4
  705. package/src/workspace/status.ts +110 -1
  706. package/src/workspace/symbols.ts +191 -0
  707. package/src/workspace/trailer-joins.test.ts +171 -0
  708. package/src/workspace/trailer-joins.ts +82 -0
  709. package/src/workspace/trailers.ts +148 -0
  710. package/src/workspace/trust/run-commits.ts +115 -0
  711. package/src/workspace/trust/run-statement.ts +226 -0
  712. package/src/workspace/trust/seal.ts +58 -1
  713. package/src/workspace/trust/verify-cli.ts +6 -5
  714. package/src/workspace/trust/verify.ts +39 -6
  715. package/src/workspace/wip-cli.ts +97 -0
  716. package/src/workspace/wip-write.schema.json +504 -0
  717. package/src/workspace/wip.schema.json +334 -0
  718. package/src/workspace/wip.test.ts +294 -0
  719. package/src/workspace/wip.ts +753 -0
  720. package/src/workspace/work-cli.e2e.test.ts +28 -1
  721. package/src/workspace/work-cli.ts +136 -8
  722. package/src/workspace/work-evidence.schema.json +5 -0
  723. package/src/workspace/work-history.schema.json +73 -1
  724. package/src/workspace/work-history.test.ts +48 -5
  725. package/src/workspace/work-kind-fields.test.ts +154 -0
  726. package/src/workspace/work-lease.schema.json +14 -1
  727. package/src/workspace/work-size.test.ts +77 -0
  728. package/src/workspace/work-size.ts +93 -0
  729. package/src/workspace/work.ts +82 -0
  730. package/src/workspace/workspace-kinds.schema.json +122 -0
  731. package/src/workspace/workspace-principals.schema.json +60 -0
  732. package/src/workspace/write-lock-cli.ts +131 -0
  733. package/src/workspace/write-lock.schema.json +172 -0
  734. package/src/workspace/write-lock.test.ts +189 -0
  735. package/src/workspace/write-lock.ts +474 -0
  736. package/src/workspace/write-scope.test.ts +144 -3
  737. package/src/workspace/write-scope.ts +192 -45
  738. package/src/yaml.test.ts +111 -1
  739. package/src/yaml.ts +141 -549
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * The intent graph over one region: `chant workspace graph --intent
3
- * <path[:start-end]>` (#2651; #2650 sections B and C1; #2524 D8, D15).
3
+ * <path[:start-end]|path#symbol>` (#2651; #2650 sections B and C1; #2524 D8,
4
+ * D15; #3034).
4
5
  *
5
6
  * A person points at some code and asks how it got this way and who decided
6
7
  * it should be this way. This module gathers what the workspace records about
@@ -30,7 +31,30 @@
30
31
  * record kind the declaration names (#2680), and each one's `commitJoins`
31
32
  * runs for every commit in that order. chant emits the
32
33
  * graph and hud renders it (#2524 D8, D15). Git is read through a local
33
- * `git` subprocess only: no fetch, no network.
34
+ * `git` subprocess only: no fetch, no network, except to follow a squash
35
+ * when asked (below).
36
+ *
37
+ * A region can name a symbol, `path#symbol`, which `symbols.ts` resolves to
38
+ * its current lines (#3034). For a file region the document also carries
39
+ * `why`: who made the region's current lines, from `git blame`, the agent run
40
+ * behind each (narrowed by the hunks a run recorded when several share a
41
+ * commit), and the decisions governing the region, most relevant first, with
42
+ * `explained: false` and closed gap codes when nothing accounts for it.
43
+ *
44
+ * A commit joins an agent run by its `Chant-Run` trailer or the run's own
45
+ * list of commits, and, failing both, by content (#3036): a commit whose
46
+ * `git patch-id --stable` equals one the run recorded, as after a rebase or a
47
+ * cherry-pick that dropped the trailer. Its `made-by` edge says `patch-id`,
48
+ * and a commit joined one way whose content matches another run's recorded
49
+ * commit is `intent-commit-join-conflict`.
50
+ *
51
+ * Asked to (`followSquash`, #3035), the walk follows a squash merge to its
52
+ * pull request's original commits (`squash.ts`): the squash commit node lists
53
+ * them with their trailers, joins and signatures, carries what their trailers
54
+ * and plugin joins carry, and joins their runs, marked `squash`. That is the
55
+ * one part of the walk that may reach the network, to fetch a pull request
56
+ * ref the clone lacks; a ref that can't be read is `squash-unfollowed` in
57
+ * `reasons`, and the walk keeps its answer without it.
34
58
  */
35
59
 
36
60
  import { execFileSync } from "node:child_process";
@@ -48,6 +72,11 @@ import type { DecidedIn } from "./record-decided";
48
72
  import { importKindModule, loadRecordKind, parseFrontMatter, RecordReadError, supersedesTargets, type LoadedRecordKind } from "./records";
49
73
  import { queryRecords, type RecordView } from "./records-cli";
50
74
  import { isPluginCode, type PluginCode, type ReasonCode } from "./reason-codes";
75
+ import { addSquashRuns, RUN_JOIN_ORDER, runsForCommits, type RunCost, type RunJoin, type RunPin, type RunUsage, type RunView } from "./runs";
76
+ import { followSquashes, squashLineOrigins, type Forge, type SquashFollow } from "./squash";
77
+ import { memberDirOnDisk, memberSymbolResolvers, type SymbolPluginLoader } from "./lexicon-symbols";
78
+ import { resolveSymbol, symbolResolverFor, type SymbolResolver } from "./symbols";
79
+ import { readTrailerJoins, type CommitTrailerJoins } from "./trailer-joins";
51
80
  import { joinPath, skippedDir, type WorkspaceTree } from "./tree";
52
81
  import { activeAttestors, type ProvenanceLevel } from "./trust/attestor";
53
82
  import { commitProvenance, policyAtBase, resolveBase } from "./trust/provenance";
@@ -66,7 +95,7 @@ export const INTENT_OUTPUT_SCHEMA_ID = "https://intentius.io/chant/schemas/works
66
95
  export const INTENT_FINDING_CODES = [
67
96
  /** A commit touched the region when no decision constrained it at path granularity. */
68
97
  "intent-commit-undecided",
69
- /** A commit has no unit, no pull request reference and no decision covering the region at its time. */
98
+ /** A commit has no unit, carries no record through chant's trailers, has no pull request reference and no decision covering the region at its time. */
70
99
  "intent-commit-bare",
71
100
  /** A pinned artifact's bytes no longer hash to the pin. */
72
101
  "intent-pin-drifted",
@@ -98,6 +127,8 @@ export const INTENT_FINDING_CODES = [
98
127
  "intent-work-blocked",
99
128
  /** Commits in the region are a decision's own work while the work item implementing it is still open: the code says done and the queue says not (#2683). */
100
129
  "intent-work-open-decided-code",
130
+ /** A commit joined to an agent run by its trailer or the run's record has the patch-id of a commit another run recorded; the content join is not made (#3036). */
131
+ "intent-commit-join-conflict",
101
132
  ] as const satisfies readonly ReasonCode[];
102
133
  export type IntentFindingCode = (typeof INTENT_FINDING_CODES)[number];
103
134
 
@@ -107,6 +138,8 @@ export const INTENT_REASON_CODES = [
107
138
  "intent-history-shallow",
108
139
  /** A plugin's commitJoins failed for a commit. */
109
140
  "intent-plugin-failed",
141
+ /** A squash commit's pull request ref could not be read or fetched, so its original commits are not followed (#3035). */
142
+ "squash-unfollowed",
110
143
  ] as const satisfies readonly ReasonCode[];
111
144
  export type IntentReasonCode = (typeof INTENT_REASON_CODES)[number];
112
145
 
@@ -123,9 +156,28 @@ export const INTENT_ERROR_CODES = [
123
156
  "intent-region-invalid",
124
157
  /** --record names an id no record of a decision kind read has. */
125
158
  "intent-record-unknown",
159
+ /** The region names a symbol in a file no resolver reads (#3034). */
160
+ "intent-symbol-unsupported",
161
+ /** The region names a symbol the file does not declare (#3034). */
162
+ "intent-symbol-unknown",
163
+ /** The region names a symbol that matches more than one declaration (#3034). */
164
+ "intent-symbol-ambiguous",
126
165
  ] as const satisfies readonly ReasonCode[];
127
166
  export type IntentErrorCode = (typeof INTENT_ERROR_CODES)[number];
128
167
 
168
+ /** Why `why` can't account for some or all of a region (#3034). Closed. */
169
+ export const INTENT_WHY_CODES = [
170
+ /** No current decision governs the region, at any granularity, and none is carried out by the commits or runs that made its lines. */
171
+ "intent-why-no-decision",
172
+ /** None of the region's current lines was made by a commit an agent run is joined to. */
173
+ "intent-why-no-run",
174
+ /** Some of the region's lines are not committed, so nothing made them yet. */
175
+ "intent-why-uncommitted",
176
+ /** Some lines come from a commit that several runs made, and no run's recorded hunks say which wrote them. */
177
+ "intent-why-run-ambiguous",
178
+ ] as const satisfies readonly ReasonCode[];
179
+ export type IntentWhyCode = (typeof INTENT_WHY_CODES)[number];
180
+
129
181
  export class IntentError extends Error {
130
182
  constructor(
131
183
  readonly code: IntentErrorCode,
@@ -157,6 +209,8 @@ export interface RegionNode {
157
209
  generated: boolean | null;
158
210
  /** Set when the region was given as a graph node id. */
159
211
  node: string | null;
212
+ /** 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. */
213
+ symbol?: { name: string; qualified: string; kind: string } | null;
160
214
  }
161
215
 
162
216
  export interface FileNode {
@@ -196,6 +250,46 @@ export interface CommitNode {
196
250
  * null when no record kind was read.
197
251
  */
198
252
  state: CommitState | null;
253
+ /**
254
+ * What chant's own trailers join the commit to (#3149, ws-075): the agent
255
+ * session, the work lease and the item it was taken on, the agent run, the
256
+ * records it names and the apply it records. A record or a lease item that
257
+ * a kind read has is linked by a `carries` edge.
258
+ */
259
+ joins: CommitTrailerJoins;
260
+ /**
261
+ * Set only when the walk follows squashes (#3035) and the commit squashes
262
+ * a pull request: its original commits, read from the forge's pull request
263
+ * ref. The commit carries what they carry and joins their runs.
264
+ */
265
+ squash?: SquashNode;
266
+ }
267
+
268
+ /** A squash commit's pull request, followed to its original commits (#3035). */
269
+ export interface SquashNode {
270
+ pullRequest: number;
271
+ /** The forge `origin` points at, or null for one chant doesn't know. */
272
+ forge: Forge | null;
273
+ /** The local ref the pull request's head was read from, or null. */
274
+ ref: string | null;
275
+ /** The pull request's head commit, or null. */
276
+ head: string | null;
277
+ /** Whether this read fetched the ref. */
278
+ fetched: boolean;
279
+ /** Whether the original commits were read; when false, `squash-unfollowed` in `reasons` says why. */
280
+ followed: boolean;
281
+ /** The original commits, oldest first, each with what its own trailers join and its provenance level. */
282
+ commits: SquashedCommit[];
283
+ }
284
+
285
+ export interface SquashedCommit {
286
+ sha: string;
287
+ subject: string;
288
+ author: { name: string; email: string };
289
+ date: string;
290
+ trailers: Record<string, string[]>;
291
+ signature: { level: ProvenanceLevel; reason: string; principal?: string };
292
+ joins: CommitTrailerJoins;
199
293
  }
200
294
 
201
295
  export type CommitState = "decided" | "decided-by-window" | "undecided";
@@ -299,6 +393,18 @@ export interface WorkGapSource {
299
393
  artifact?: string;
300
394
  }
301
395
 
396
+ /** A review session that led to a work item (#3154, E21). */
397
+ export interface WorkReview {
398
+ /** The session record's node-style id, `record:<kind>/<id>`. */
399
+ id: string;
400
+ recordKind: string;
401
+ record: string;
402
+ path: string;
403
+ state: string | null;
404
+ /** The comments whose follow_ups name the item, numbered from 1; empty when only the session's own follow_ups do. */
405
+ comments: number[];
406
+ }
407
+
302
408
  export interface WorkNode {
303
409
  id: string;
304
410
  kind: "work";
@@ -329,9 +435,47 @@ export interface WorkNode {
329
435
  constrains: { entry: string; granularity: Granularity }[];
330
436
  /** The item's work warnings, as records reports them, and work-done-gap-open. */
331
437
  warnings: { code: WorkWarningCode; message: string }[];
438
+ /**
439
+ * The review sessions whose comments led to this item (#3154, E21 in
440
+ * arugula-salad/hud#823): each session record of a session kind read whose
441
+ * `comments[].follow_ups` (or its own `follow_ups`) names the item as
442
+ * `<kind>:<id>`, with the 1-based numbers of those comments. A commit that
443
+ * carries the item, by its `Chant-Record: work:<id>` trailer, joins back
444
+ * to the review through it. Empty when none does. Added within contract 1.
445
+ */
446
+ reviews?: WorkReview[];
447
+ }
448
+
449
+ /**
450
+ * An agent run that made a commit in the walk (#3033): from the run ledger on
451
+ * the local `chant/lifecycle` branch. A run a `Chant-Run` trailer names and
452
+ * the ledger lacks is listed with `recorded: false` and nothing else known.
453
+ */
454
+ export interface RunNode {
455
+ id: string;
456
+ kind: "run";
457
+ run: string;
458
+ recorded: boolean;
459
+ state: "running" | "ended" | null;
460
+ harness: { name: string; version: string | null } | null;
461
+ model: string | null;
462
+ provider: string | null;
463
+ by: string | null;
464
+ agent: string | null;
465
+ unit: string | null;
466
+ startedAt: string | null;
467
+ endedAt: string | null;
468
+ outcome: string | null;
469
+ usage: RunUsage | null;
470
+ cost: RunCost | null;
471
+ transcript: RunPin | null;
472
+ /** The instruction the run was given, pinned by hash, with the writer's excerpt when it gave one (#3034). */
473
+ instruction?: RunPin | null;
474
+ /** The fencing token of the work lease it worked under (#3034). */
475
+ lease?: string | null;
332
476
  }
333
477
 
334
- export type IntentNode = RegionNode | FileNode | MemberNode | CommitNode | JoinedNode | EvidenceEntryNode | DecisionNode | WorkNode | ArtifactNode | LinkNode | FindingNode;
478
+ export type IntentNode = RegionNode | FileNode | MemberNode | CommitNode | JoinedNode | EvidenceEntryNode | DecisionNode | WorkNode | ArtifactNode | LinkNode | FindingNode | RunNode;
335
479
 
336
480
  export type Granularity = "path" | "member" | "contract" | "issue";
337
481
 
@@ -340,7 +484,87 @@ export type IntentEdge =
340
484
  | { kind: "pins"; from: string; to: string; pinnedSha256: string | null; pinState: PinState }
341
485
  | { kind: "touched-by"; from: string; to: string; lines: LineRange[] | null }
342
486
  | { kind: "within"; from: string; to: string; state: "decided" | "decided-by-window" | "worked" }
343
- | { kind: "produced-by" | "serves" | "cites-evidence" | "supersedes" | "links" | "implements" | "needs" | "addressed-by"; from: string; to: string };
487
+ | { kind: "made-by"; from: string; to: string; joinedBy: RunJoin[]; recordedAs?: string; via?: string[] }
488
+ | { kind: "produced-by" | "serves" | "cites-evidence" | "supersedes" | "links" | "implements" | "needs" | "addressed-by" | "carries" | "worked-on"; from: string; to: string };
489
+
490
+ /** Lines of the region that one commit, and the same runs, last wrote (#3034). */
491
+ export interface WhySpan {
492
+ start: number;
493
+ end: number;
494
+ /** The commit node, or null for lines not committed yet. */
495
+ commit: string | null;
496
+ sha: string | null;
497
+ /** The run nodes behind these lines: the run the commit's Chant-Run names first, then those whose record lists it. */
498
+ runs: string[];
499
+ /** `hunks` when a run's recorded hunks chose among the commit's runs; null when every run that made the commit is listed. */
500
+ narrowedBy: "hunks" | null;
501
+ /** How the runs in `runs` join the commit, as their `made-by` edges say, without repeats (#3036). Empty with no runs. */
502
+ joinedBy: RunJoin[];
503
+ /**
504
+ * For lines of a followed squash (#3035): the sha of the original commit
505
+ * that last wrote them, one of the commit node's `squash.commits`, by blame
506
+ * at the pull request's head; `runs` keeps that commit's runs and the
507
+ * squash's own. Empty when the file at the head is not the squash's, so
508
+ * blame there can't say. Absent for any other commit.
509
+ */
510
+ via?: string[];
511
+ }
512
+
513
+ /** Why a decision is in `why`, most relevant first (#3034). */
514
+ export type WhyRelevance = "carried" | "path" | "contract" | "issue" | "member" | "related";
515
+
516
+ export interface WhyDecision {
517
+ decision: string;
518
+ /**
519
+ * carried: a commit or run that made the region's current lines carries it
520
+ * out. path, contract, issue, member: it constrains the region that way.
521
+ * related: it is in the graph only through supersession, or because a
522
+ * commit that no longer made any current line carried it out.
523
+ */
524
+ relevance: WhyRelevance;
525
+ /** Not superseded. */
526
+ current: boolean;
527
+ /** Its state is one its kind closes, such as ratified. */
528
+ closed: boolean;
529
+ /** How many of the region's current lines were made as its own work. */
530
+ lines: number;
531
+ }
532
+
533
+ export interface WhyRun {
534
+ run: string;
535
+ /** How many of the region's current lines it wrote. */
536
+ lines: number;
537
+ /** The commit nodes through which it made them. */
538
+ commits: string[];
539
+ /** The work item it worked on, as the run record names it, and its node when a work kind read has it. */
540
+ unit: { id: string; kind: string | null; node: string | null } | null;
541
+ /** The decision nodes it carried out: through its work item's implements, and the decision records it names. */
542
+ decisions: string[];
543
+ /** How it joins the commits in `commits`, as their `made-by` edges say, without repeats (#3036). */
544
+ joinedBy: RunJoin[];
545
+ }
546
+
547
+ export interface WhyGap {
548
+ code: IntentWhyCode;
549
+ message: string;
550
+ /** The lines the gap is about, when it is about some of them. */
551
+ lines?: LineRange[];
552
+ }
553
+
554
+ /** The answer to "why is it like this" over the region (#3034, hud#700). */
555
+ export interface WhyAnswer {
556
+ /** The lines accounted for: the region's range, the whole file, or null for a directory, which is not blamed. */
557
+ lines: LineRange | null;
558
+ /** The current lines, top to bottom, each span with the commit and runs that last wrote it. Empty for a directory. */
559
+ blame: WhySpan[];
560
+ /** The decisions governing the region, most relevant first. */
561
+ decisions: WhyDecision[];
562
+ /** The runs that wrote the current lines, most lines first; for a directory, every run in the walk, newest first. */
563
+ runs: WhyRun[];
564
+ /** Whether a current decision governs the region or is carried out by what made its lines. */
565
+ explained: boolean;
566
+ gaps: WhyGap[];
567
+ }
344
568
 
345
569
  export interface IntentReason {
346
570
  code: IntentReasonCode;
@@ -364,15 +588,18 @@ export type IntentDocument =
364
588
  edges: IntentEdge[];
365
589
  reasons: IntentReason[];
366
590
  summary: { commits: number; decisions: number; artifacts: number; findings: number };
591
+ why: WhyAnswer;
367
592
  })
368
593
  | (Head & { error: { code: IntentErrorCode; message: string } });
369
594
 
370
595
  export interface IntentQuery {
371
596
  /** Where the walk up to the declaration starts, and what the region path is relative to. */
372
597
  cwd: string;
373
- /** `path`, `path:line` or `path:start-end`, or a graph node id `<member>/<id>`. */
598
+ /** `path`, `path:line`, `path:start-end` or `path#symbol`, or a graph node id `<member>/<id>`. */
374
599
  region: string;
375
600
  at?: string;
601
+ /** Follow squash merges to their pull requests' original commits, fetching a missing pull request ref from `origin` (#3035). */
602
+ followSquash?: boolean;
376
603
  /**
377
604
  * Kind files: record kinds, plugins with `commitJoins`, or both. Relative to
378
605
  * `cwd`. Left out, every record kind the declaration names, in its order
@@ -385,6 +612,11 @@ export interface IntentQuery {
385
612
  * --member` does.
386
613
  */
387
614
  resolveNode?: (cwd: string, at: string | undefined, member: string, id: string) => Promise<{ file: string; line: number | null } | null | undefined>;
615
+ /**
616
+ * Load a lexicon plugin by name, for the symbol resolvers a member's
617
+ * lexicons contribute (#3313). The default is the CLI's `loadPlugin`.
618
+ */
619
+ loadLexicon?: SymbolPluginLoader;
388
620
  }
389
621
 
390
622
  export interface IntentResult {
@@ -442,6 +674,45 @@ export function regionHistory(top: string, rev: string, gitPath: string, type: "
442
674
  .map((sha) => ({ sha, lines: null }));
443
675
  }
444
676
 
677
+ /** One current line of a file, and the commit that last wrote it. */
678
+ export interface BlamedLine {
679
+ line: number;
680
+ /** Null for a line not committed yet. */
681
+ sha: string | null;
682
+ /** The file's path in that commit, from the repository root. */
683
+ origPath: string;
684
+ /** The line's number in that commit's version of the file. */
685
+ origLine: number;
686
+ }
687
+
688
+ const ZERO_SHA = /^0+$/;
689
+
690
+ /**
691
+ * `git blame` over a file's lines (#3034): at `rev`, or in the working tree
692
+ * when `rev` is null, where a line not committed yet has no commit. Undefined
693
+ * when git can't blame the file, such as one never committed.
694
+ */
695
+ export function blameLines(top: string, rev: string | null, gitPath: string, lines: LineRange | null): BlamedLine[] | undefined {
696
+ const out = tryGit(top, ["blame", "--line-porcelain", ...(lines ? [`-L${lines.start},${lines.end}`] : []), ...(rev ? [rev] : []), "--", gitPath]);
697
+ if (out === undefined) return undefined;
698
+ const result: BlamedLine[] = [];
699
+ let current: { sha: string; origLine: number; line: number; origPath: string } | undefined;
700
+ for (const l of out.split("\n")) {
701
+ if (l.startsWith("\t")) {
702
+ if (current) result.push({ line: current.line, sha: ZERO_SHA.test(current.sha) ? null : current.sha, origPath: current.origPath, origLine: current.origLine });
703
+ current = undefined;
704
+ continue;
705
+ }
706
+ const head = l.match(/^([0-9a-f]{40,64}) (\d+) (\d+)(?: \d+)?$/);
707
+ if (head) {
708
+ current = { sha: head[1], origLine: Number(head[2]), line: Number(head[3]), origPath: gitPath };
709
+ continue;
710
+ }
711
+ if (current && l.startsWith("filename ")) current.origPath = l.slice("filename ".length);
712
+ }
713
+ return result;
714
+ }
715
+
445
716
  /** Subject, body, author, date and trailers of each commit. */
446
717
  export function commitDetails(top: string, shas: string[]): Map<string, IntentCommit> {
447
718
  const out = new Map<string, IntentCommit>();
@@ -538,7 +809,8 @@ export function parseRegion(arg: string): { path: string; lines: LineRange | nul
538
809
  return { path: m[1], lines: { start, end } };
539
810
  }
540
811
 
541
- function regionId(path: string, lines: LineRange | null): string {
812
+ function regionId(path: string, lines: LineRange | null, symbol?: string): string {
813
+ if (symbol !== undefined) return `region:${path}#${symbol}`;
542
814
  return `region:${path}${lines ? `:${lines.start}${lines.end === lines.start ? "" : `-${lines.end}`}` : ""}`;
543
815
  }
544
816
 
@@ -649,13 +921,46 @@ export async function loadKinds(query: IntentQuery, top: string): Promise<Loaded
649
921
  return out;
650
922
  }
651
923
 
652
- async function resolveRegion(query: IntentQuery, located: LocatedWorkspace, declaration: Declaration): Promise<{ path: string; lines: LineRange | null; node: string | null }> {
924
+ interface ResolvedRegion {
925
+ path: string;
926
+ lines: LineRange | null;
927
+ node: string | null;
928
+ symbol: { name: string; qualified: string; kind: string } | null;
929
+ }
930
+
931
+ async function resolveRegion(query: IntentQuery, located: LocatedWorkspace, declaration: Declaration): Promise<ResolvedRegion> {
653
932
  const parsed = parseRegion(query.region);
654
933
  if ("error" in parsed) throw new IntentError("intent-region-invalid", parsed.error);
655
- const rel = toPosix(relative(realpathOr(located.rootOnDisk), resolve(realpathOr(query.cwd), parsed.path)));
656
- const path = rel === "" ? "." : rel;
657
- if (path === "." || (isWorkspacePath(path) && !path.startsWith("../") && located.tree.stat(path) !== undefined)) {
658
- return { path, lines: parsed.lines, node: null };
934
+ const inWorkspace = (p: string): string | undefined => {
935
+ const rel = toPosix(relative(realpathOr(located.rootOnDisk), resolve(realpathOr(query.cwd), p)));
936
+ const path = rel === "" ? "." : rel;
937
+ return path === "." || (isWorkspacePath(path) && !path.startsWith("../") && located.tree.stat(path) !== undefined) ? path : undefined;
938
+ };
939
+ const path = inWorkspace(parsed.path);
940
+ if (path !== undefined) return { path, lines: parsed.lines, node: null, symbol: null };
941
+ // path#symbol (#3034): the symbol's current lines in the tree read.
942
+ const hash = parsed.lines ? -1 : query.region.lastIndexOf("#");
943
+ if (hash > 0) {
944
+ const name = query.region.slice(hash + 1);
945
+ const file = inWorkspace(query.region.slice(0, hash));
946
+ if (file !== undefined && name !== "") {
947
+ if (file === "." || located.tree.stat(file) !== "file") throw new IntentError("intent-region-invalid", `${file} is a directory, and a symbol needs a file`);
948
+ // A file core's resolver doesn't read may have one from its member's lexicons (#3313).
949
+ let extra: SymbolResolver[] = [];
950
+ let why = "";
951
+ if (!symbolResolverFor(file)) {
952
+ const holder = memberHolding(file, declaration.members);
953
+ const m = holder ? declaration.members.find((x) => x.name === holder) : undefined;
954
+ if (m && m.kind === "chant") {
955
+ const got = await memberSymbolResolvers(memberDirOnDisk(located.rootOnDisk, m.dir), query.loadLexicon);
956
+ extra = got.resolvers;
957
+ if (got.problems.length > 0) why = `; member ${m.name}: ${got.problems.join("; ")}`;
958
+ }
959
+ }
960
+ const r = resolveSymbol(file, located.tree.read(file), name, extra);
961
+ if (!r.ok) throw new IntentError(r.reason === "unsupported" ? "intent-symbol-unsupported" : r.reason === "ambiguous" ? "intent-symbol-ambiguous" : "intent-symbol-unknown", `${r.message}${r.reason === "unsupported" ? why : ""}${located.tree.label}`);
962
+ return { path: file, lines: r.declaration.lines, node: null, symbol: { name, qualified: r.declaration.qualified, kind: r.declaration.kind } };
963
+ }
659
964
  }
660
965
  // A graph node id, <member>/<id>, when no such path exists (#2650 B1).
661
966
  const slash = query.region.indexOf("/");
@@ -664,10 +969,10 @@ async function resolveRegion(query: IntentQuery, located: LocatedWorkspace, decl
664
969
  const loc = await (query.resolveNode ?? resolveNodeFromGraph)(query.cwd, query.at, member.name, query.region);
665
970
  if (loc) {
666
971
  const file = joinPath(member.dir, loc.file);
667
- return { path: file === "" ? "." : file, lines: loc.line ? { start: loc.line, end: loc.line } : null, node: query.region };
972
+ return { path: file === "" ? "." : file, lines: loc.line ? { start: loc.line, end: loc.line } : null, node: query.region, symbol: null };
668
973
  }
669
974
  }
670
- throw new IntentError("intent-region-invalid", `${query.region} is not a path in the workspace${located.tree.label}, or a graph node id`);
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`);
671
976
  }
672
977
 
673
978
  /** Build the intent document. Never throws an {@link IntentError}, {@link WorkspaceReadError} or {@link RecordReadError}. */
@@ -714,8 +1019,19 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
714
1019
 
715
1020
  // The region, its files and its member.
716
1021
  const member = region.path === "." ? (declaration.members.find((m) => m.dir === ".")?.name ?? null) : memberHolding(region.path, declaration.members);
717
- const rid = regionId(region.path, region.lines);
718
- add<RegionNode>({ id: rid, kind: "region", path: region.path, lines: region.lines, member, at: located.at, type, generated: type === "file" ? isGeneratedPath(declaration, region.path) : null, node: region.node });
1022
+ const rid = regionId(region.path, region.lines, region.symbol?.name);
1023
+ add<RegionNode>({
1024
+ id: rid,
1025
+ kind: "region",
1026
+ path: region.path,
1027
+ lines: region.lines,
1028
+ member,
1029
+ at: located.at,
1030
+ type,
1031
+ generated: type === "file" ? isGeneratedPath(declaration, region.path) : null,
1032
+ node: region.node,
1033
+ symbol: region.symbol,
1034
+ });
719
1035
  const files = type === "dir" ? filesUnder(located.tree, region.path) : [];
720
1036
  for (const f of files) add<FileNode>({ id: `file:${f}`, kind: "file", path: f, member: memberHolding(f, declaration.members), generated: isGeneratedPath(declaration, f) });
721
1037
  const memberNode = (name: string) => {
@@ -731,7 +1047,27 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
731
1047
  const workspacePrefix = located.root === "." ? "" : located.root;
732
1048
  const gitPath = joinPath(workspacePrefix, region.path);
733
1049
  const touched = rev ? regionHistory(top, rev, gitPath, type, region.lines) : [];
1050
+ // Who last wrote each current line of a file region (#3034). A commit blame
1051
+ // names and the history walk missed, as when a move confuses git log -L,
1052
+ // joins the commits that touched the region.
1053
+ const fileLines = type === "file" ? (region.lines ?? { start: 1, end: Math.max(1, located.tree.read(region.path).replace(/\n$/, "").split("\n").length) }) : null;
1054
+ const blamed: BlamedLine[] =
1055
+ fileLines === null
1056
+ ? []
1057
+ : ((rev ? blameLines(top, located.at, gitPath, region.lines) : undefined) ??
1058
+ // A file git can't blame in the working tree has never been committed: every line is new.
1059
+ (located.at === null ? Array.from({ length: fileLines.end - fileLines.start + 1 }, (_, i) => ({ line: fileLines.start + i, sha: null, origPath: gitPath, origLine: fileLines.start + i })) : []));
1060
+ for (const sha of new Set(blamed.map((b) => b.sha).filter((x): x is string => x !== null))) {
1061
+ if (!touched.some((t) => t.sha === sha)) touched.push({ sha, lines: null });
1062
+ }
734
1063
  const details = commitDetails(top, touched.map((t) => t.sha));
1064
+ // Squash merges, followed to the pull request's original commits when asked (#3035).
1065
+ const follows = query.followSquash ? followSquashes(top, [...details.values()], { fetchMissing: true }) : new Map<string, SquashFollow>();
1066
+ for (const f of follows.values()) {
1067
+ if (f.problem) reasons.push({ code: "squash-unfollowed", message: `${f.sha.slice(0, 8)} squashes pull request #${f.pullRequest}, and ${f.problem}, so its original commits are not followed` });
1068
+ }
1069
+ const originalDetails = commitDetails(top, [...new Set([...follows.values()].flatMap((f) => f.commits))]);
1070
+ const originalsOf = (sha: string): IntentCommit[] => (follows.get(sha)?.commits ?? []).map((o) => originalDetails.get(o)).filter((x): x is IntentCommit => x !== undefined);
735
1071
 
736
1072
  // Each commit's provenance, judged by the policy at base as records judges a record's (#2547).
737
1073
  const policy = policyAtBase(top, resolveBase(top));
@@ -752,60 +1088,128 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
752
1088
  unit?: string;
753
1089
  contracts: string[];
754
1090
  authorship: string[];
755
- /** The record ids the commit's units and contracts say they carry out. */
1091
+ /** The record ids the commit's units and contracts say they carry out, and the ones its Chant-Record and Chant-Lease trailers name or implement. */
756
1092
  decisions: string[];
1093
+ /** The nodes of the records the commit's own trailers name (#3149). */
1094
+ carries: string[];
757
1095
  }
758
1096
  const joined = new Map<string, Joined>();
1097
+ // chant's own trailers on each commit (#3149), with the items their lease tokens name.
1098
+ const trailerJoins = readTrailerJoins(top, [...details.values(), ...originalDetails.values()]);
759
1099
  const pluginFindings: { commit: string; plugin: string; finding: PluginFinding }[] = [];
1100
+ const signatureOf = (sha: string): CommitNode["signature"] => {
1101
+ if (!policy.active) return { level: "unattested", reason: policy.problems.length ? policy.problems.join("; ") : `no signers file (${policy.signersPath}) at base; attestation is off` };
1102
+ const p = commitProvenance(top, policy, sha, attestors);
1103
+ return { level: p.level, reason: p.reason, ...(p.principal ? { principal: p.principal } : {}) };
1104
+ };
760
1105
  const failedPlugins = new Set<string>();
761
1106
  for (const t of touched) {
762
1107
  const c = details.get(t.sha);
763
1108
  if (!c) continue;
764
- const signature = policy.active
765
- ? (() => {
766
- const p = commitProvenance(top, policy, c.sha, attestors);
767
- return { level: p.level, reason: p.reason, ...(p.principal ? { principal: p.principal } : {}) };
768
- })()
769
- : { level: "unattested" as const, reason: policy.problems.length ? policy.problems.join("; ") : `no signers file (${policy.signersPath}) at base; attestation is off` };
1109
+ const signature = signatureOf(c.sha);
770
1110
  const pr = c.subject.match(/\(#([0-9]+)\)\s*$/);
771
1111
  const cid = `commit:${c.sha}`;
772
- add<CommitNode>({ id: cid, kind: "commit", sha: c.sha, subject: c.subject, author: c.author, date: c.date, trailers: c.trailers, pullRequest: pr ? Number(pr[1]) : null, signature, lines: t.lines, state: null });
1112
+ const f = follows.get(c.sha);
1113
+ const squash: SquashNode | undefined = f
1114
+ ? {
1115
+ pullRequest: f.pullRequest,
1116
+ forge: f.forge,
1117
+ ref: f.ref,
1118
+ head: f.head,
1119
+ fetched: f.fetched,
1120
+ followed: f.followed,
1121
+ commits: originalsOf(c.sha).map((o) => ({ sha: o.sha, subject: o.subject, author: o.author, date: o.date, trailers: o.trailers, signature: signatureOf(o.sha), joins: trailerJoins.get(o.sha)!.joins })),
1122
+ }
1123
+ : undefined;
1124
+ add<CommitNode>({ id: cid, kind: "commit", sha: c.sha, subject: c.subject, author: c.author, date: c.date, trailers: c.trailers, pullRequest: pr ? Number(pr[1]) : null, signature, lines: t.lines, state: null, joins: trailerJoins.get(c.sha)!.joins, ...(squash ? { squash } : {}) });
773
1125
  edges.push({ kind: "touched-by", from: rid, to: cid, lines: t.lines });
774
1126
 
775
- // 2. Each commit's origin, from the plugins.
776
- const entry: Joined = { contracts: [], authorship: [], decisions: [] };
1127
+ // 2. Each commit's origin, from the plugins. A followed squash also takes its original commits' (#3035).
1128
+ const entry: Joined = { contracts: [], authorship: [], decisions: [], carries: [] };
777
1129
  joined.set(c.sha, entry);
1130
+ const pushEdge = (e: IntentEdge) => {
1131
+ if (!edges.some((x) => x.kind === e.kind && x.from === e.from && x.to === e.to)) edges.push(e);
1132
+ };
1133
+ const subjects: IntentCommit[] = [c, ...originalsOf(c.sha)];
778
1134
  for (const k of kinds) {
779
1135
  if (!k.joins) continue;
780
- let result;
781
- try {
782
- result = await runCommitJoins(k.joins, c, { read: readAt, list: listAt, at: located.at }, k.name);
783
- } catch (err) {
784
- const message = `${k.display}: commitJoins failed for ${c.sha.slice(0, 8)}: ${err instanceof Error ? err.message : String(err)}`;
785
- if (!failedPlugins.has(message)) reasons.push({ code: "intent-plugin-failed", message });
786
- failedPlugins.add(message);
787
- continue;
788
- }
789
- const joinedNode = (kind: JoinedNode["kind"], e: JoinedEntity): string => {
790
- const { id, ...data } = e;
791
- return add<JoinedNode>({ id: `${kind}:${id}`, kind, ref: id, plugin: k.display, data }).id;
792
- };
793
- const unitId = result.unit ? joinedNode("unit", result.unit) : undefined;
794
- const contractId = result.contract ? joinedNode("contract", result.contract) : undefined;
795
- if (unitId) {
796
- entry.unit = unitId;
797
- edges.push({ kind: "produced-by", from: cid, to: unitId });
798
- if (contractId) edges.push({ kind: "serves", from: unitId, to: contractId });
1136
+ for (const subject of subjects) {
1137
+ const original = subject !== c;
1138
+ let result;
1139
+ try {
1140
+ result = await runCommitJoins(k.joins, subject, { read: readAt, list: listAt, at: located.at }, k.name);
1141
+ } catch (err) {
1142
+ const message = `${k.display}: commitJoins failed for ${subject.sha.slice(0, 8)}: ${err instanceof Error ? err.message : String(err)}`;
1143
+ if (!failedPlugins.has(message)) reasons.push({ code: "intent-plugin-failed", message });
1144
+ failedPlugins.add(message);
1145
+ continue;
1146
+ }
1147
+ const joinedNode = (kind: JoinedNode["kind"], e: JoinedEntity): string => {
1148
+ const { id, ...data } = e;
1149
+ return add<JoinedNode>({ id: `${kind}:${id}`, kind, ref: id, plugin: k.display, data }).id;
1150
+ };
1151
+ const unitId = result.unit ? joinedNode("unit", result.unit) : undefined;
1152
+ const contractId = result.contract ? joinedNode("contract", result.contract) : undefined;
1153
+ if (unitId) {
1154
+ if (original) entry.unit ??= unitId;
1155
+ else entry.unit = unitId;
1156
+ pushEdge({ kind: "produced-by", from: cid, to: unitId });
1157
+ if (contractId) pushEdge({ kind: "serves", from: unitId, to: contractId });
1158
+ }
1159
+ if (contractId) entry.contracts.push(result.contract!.id);
1160
+ if (unitId) entry.decisions.push(...entityDecisions(result.unit), ...entityDecisions(result.contract));
1161
+ for (const f of result.findings ?? []) pluginFindings.push({ commit: cid, plugin: k.display, finding: f });
1162
+ const evidence = result.evidence === undefined ? [] : Array.isArray(result.evidence) ? result.evidence : [result.evidence];
1163
+ for (const e of evidence) pushEdge({ kind: "cites-evidence", from: unitId ?? contractId ?? cid, to: joinedNode("evidence", e) });
1164
+ // Authorship is checked against the squash's own trailers, so only its own claims count.
1165
+ if (!original) entry.authorship.push(...(result.authorship ?? []));
799
1166
  }
800
- if (contractId) entry.contracts.push(result.contract!.id);
801
- if (unitId) entry.decisions.push(...entityDecisions(result.unit), ...entityDecisions(result.contract));
802
- for (const f of result.findings ?? []) pluginFindings.push({ commit: cid, plugin: k.display, finding: f });
803
- const evidence = result.evidence === undefined ? [] : Array.isArray(result.evidence) ? result.evidence : [result.evidence];
804
- for (const e of evidence) edges.push({ kind: "cites-evidence", from: unitId ?? contractId ?? cid, to: joinedNode("evidence", e) });
805
- entry.authorship.push(...(result.authorship ?? []));
806
1167
  }
807
1168
  }
808
1169
 
1170
+ // The agent runs that made each commit (#3033): the one its Chant-Run names, and any whose record lists it.
1171
+ const runJoins = await runsForCommits(
1172
+ top,
1173
+ located.rootOnDisk,
1174
+ [...new Set([...touched.map((t) => t.sha), ...originalDetails.keys()])].map((sha) => ({ sha, run: trailerJoins.get(sha)?.joins.run ?? null })),
1175
+ );
1176
+ // A followed squash joins its original commits' runs (#3035).
1177
+ addSquashRuns(runJoins.refs, follows);
1178
+ for (const t of touched) {
1179
+ if (!nodes.has(`commit:${t.sha}`)) continue;
1180
+ for (const ref of runJoins.refs.get(t.sha) ?? []) {
1181
+ const r = runJoins.runs.get(ref.id);
1182
+ const node = add<RunNode>({
1183
+ id: `run:${ref.id}`,
1184
+ kind: "run",
1185
+ run: ref.id,
1186
+ recorded: r !== undefined,
1187
+ state: r?.state ?? null,
1188
+ harness: r?.harness ?? null,
1189
+ model: r?.model ?? null,
1190
+ provider: r?.provider ?? null,
1191
+ by: r?.by ?? null,
1192
+ agent: r?.agent ?? null,
1193
+ unit: r?.unit?.id ?? null,
1194
+ startedAt: r?.startedAt ?? null,
1195
+ endedAt: r?.endedAt ?? null,
1196
+ outcome: r?.outcome ?? null,
1197
+ usage: r?.usage ?? null,
1198
+ cost: r?.cost ?? null,
1199
+ transcript: r?.transcript ?? null,
1200
+ instruction: r?.instruction ?? null,
1201
+ lease: r?.lease ?? null,
1202
+ });
1203
+ edges.push({ kind: "made-by", from: `commit:${t.sha}`, to: node.id, joinedBy: ref.joinedBy, ...(ref.recordedAs ? { recordedAs: ref.recordedAs } : {}), ...(ref.via ? { via: ref.via } : {}) });
1204
+ }
1205
+ }
1206
+ // A commit a trailer or a record joins to one run, with the content of a commit another run recorded (#3036).
1207
+ for (const x of runJoins.conflicts) {
1208
+ if (!nodes.has(`commit:${x.sha}`)) continue;
1209
+ const others = x.content.map((m) => `${m.run} (${m.sha.slice(0, 8)})`).join(", ");
1210
+ find("intent-commit-join-conflict", `${x.sha.slice(0, 8)} joins ${x.joined.join(", ")} by its trailer or the run's record, and has the patch-id of a commit ${others} recorded; it is not joined to ${x.content.length === 1 ? "that run" : "those runs"}`, [`commit:${x.sha}`, ...x.joined.map((id) => `run:${id}`)]);
1211
+ }
1212
+
809
1213
  // 3. The decisions whose constrains cover the region, and their chains.
810
1214
  const regionFull = region.path === "." ? workspacePrefix : gitPath;
811
1215
  const origin = originRepo(top);
@@ -1099,16 +1503,6 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
1099
1503
  }
1100
1504
  if (node.constrains.length > 0) workCovering.push(h);
1101
1505
  }
1102
- for (const h of workAll.filter((x) => x.node)) {
1103
- for (const id of implementedIds(h)) {
1104
- const hit = decisionById.get(id.slice("record:".length))!;
1105
- edges.push({ kind: "implements", from: h.node!.id, to: decisionNode(hit.kind, hit.view).id });
1106
- }
1107
- for (const n of idList(h.view.data, workSpec(h).needs)) {
1108
- const hit = workByKey.get(`${h.kind.records!.loaded.kind.name}/${n}`);
1109
- if (hit) edges.push({ kind: "needs", from: h.node!.id, to: workNode(hit).id });
1110
- }
1111
- }
1112
1506
  const workWindows = new Map<string, { from: Set<string>; until: Set<string> | null }>();
1113
1507
  if (rev) {
1114
1508
  for (const h of workCovering) {
@@ -1125,6 +1519,105 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
1125
1519
  return !!w && w.from.has(sha) && !w.until?.has(sha);
1126
1520
  };
1127
1521
 
1522
+ // The records chant's own trailers name (#3149): a Chant-Record of a kind
1523
+ // read, and the work item a Chant-Lease was taken on. The commit carries
1524
+ // each, and is the own work of a decision it names or that a work item it
1525
+ // names implements.
1526
+ const recordKinds = new Map(kinds.filter((k) => k.records).map((k) => [k.records!.loaded.kind.name, k]));
1527
+ /** The work item a run worked on and the records it names, among the kinds read, deduplicated. */
1528
+ const runTargets = (r: RunView): { node: string; work?: WorkHit; decision?: string }[] => {
1529
+ const out: { node: string; work?: WorkHit; decision?: string }[] = [];
1530
+ const push = (x: { node: string; work?: WorkHit; decision?: string }) => {
1531
+ if (!out.some((o) => o.node === x.node)) out.push(x);
1532
+ };
1533
+ if (r.unit) {
1534
+ const named = r.unit.kind ? workKinds.filter((k) => k.records!.loaded.kind.name === r.unit!.kind) : workKinds;
1535
+ const hits = named.map((k) => workByKey.get(`${k.records!.loaded.kind.name}/${r.unit!.id}`)).filter((h): h is WorkHit => h !== undefined);
1536
+ if (hits.length === 1) push({ node: workNode(hits[0]).id, work: hits[0] });
1537
+ }
1538
+ for (const ref of r.records) {
1539
+ const k = recordKinds.get(ref.kind);
1540
+ if (!k) continue;
1541
+ if (k.records!.loaded.kind.work) {
1542
+ const h = workByKey.get(`${ref.kind}/${ref.id}`);
1543
+ if (h) push({ node: workNode(h).id, work: h });
1544
+ continue;
1545
+ }
1546
+ const hit = decisionById.get(`${ref.kind}/${ref.id}`);
1547
+ if (hit) push({ node: decisionNode(hit.kind, hit.view).id, decision: `${ref.kind}/${ref.id}` });
1548
+ }
1549
+ return out;
1550
+ };
1551
+ for (const t of touched) {
1552
+ const c = nodes.get(`commit:${t.sha}`) as CommitNode | undefined;
1553
+ const j = joined.get(t.sha);
1554
+ if (!c || !j) continue;
1555
+ const carry = (to: string) => {
1556
+ if (j.carries.includes(to)) return;
1557
+ j.carries.push(to);
1558
+ edges.push({ kind: "carries", from: c.id, to });
1559
+ };
1560
+ const carryWork = (h: WorkHit): string => {
1561
+ const node = workNode(h);
1562
+ carry(node.id);
1563
+ j.decisions.push(...implementedIds(h).map((id) => id.slice("record:".length)));
1564
+ return node.id;
1565
+ };
1566
+ // The commit's own trailers, and a followed squash's original commits' (#3035).
1567
+ for (const joins of [c.joins, ...(c.squash?.commits ?? []).map((o) => o.joins)]) {
1568
+ for (const r of joins.records) {
1569
+ const k = recordKinds.get(r.kind);
1570
+ if (!k) continue;
1571
+ if (k.records!.loaded.kind.work) {
1572
+ const h = workByKey.get(`${r.kind}/${r.id}`);
1573
+ if (h) r.node = carryWork(h);
1574
+ continue;
1575
+ }
1576
+ const hit = decisionById.get(`${r.kind}/${r.id}`);
1577
+ if (!hit) continue;
1578
+ r.node = decisionNode(hit.kind, hit.view).id;
1579
+ carry(r.node);
1580
+ j.decisions.push(`${r.kind}/${r.id}`);
1581
+ }
1582
+ const item = joins.lease?.item;
1583
+ if (item) {
1584
+ const hits = workKinds.map((k) => workByKey.get(`${k.records!.loaded.kind.name}/${item}`)).filter((h): h is WorkHit => h !== undefined);
1585
+ if (hits.length === 1) carryWork(hits[0]);
1586
+ }
1587
+ }
1588
+ // The runs that made the commit (#3034): its run's work item and the
1589
+ // records the run names are carried as a Chant-Lease and a Chant-Record are.
1590
+ for (const ref of runJoins.refs.get(t.sha) ?? []) {
1591
+ const r = runJoins.runs.get(ref.id);
1592
+ if (!r) continue;
1593
+ for (const target of runTargets(r)) {
1594
+ if (target.work) carryWork(target.work);
1595
+ else if (target.decision) {
1596
+ carry(target.node);
1597
+ j.decisions.push(target.decision);
1598
+ }
1599
+ }
1600
+ }
1601
+ }
1602
+ // Each recorded run in the walk to the work item and records it worked on (#3034).
1603
+ for (const n of [...nodes.values()]) {
1604
+ if (n.kind !== "run") continue;
1605
+ const r = runJoins.runs.get(n.run);
1606
+ if (!r) continue;
1607
+ for (const target of runTargets(r)) edges.push({ kind: "worked-on", from: n.id, to: target.node });
1608
+ }
1609
+ // Each work item in the graph, those its commits and runs carry included, to the decisions it implements and the items it needs.
1610
+ for (const h of workAll.filter((x) => x.node)) {
1611
+ for (const id of implementedIds(h)) {
1612
+ const hit = decisionById.get(id.slice("record:".length))!;
1613
+ edges.push({ kind: "implements", from: h.node!.id, to: decisionNode(hit.kind, hit.view).id });
1614
+ }
1615
+ for (const n of idList(h.view.data, workSpec(h).needs)) {
1616
+ const hit = workByKey.get(`${h.kind.records!.loaded.kind.name}/${n}`);
1617
+ if (hit) edges.push({ kind: "needs", from: h.node!.id, to: workNode(hit).id });
1618
+ }
1619
+ }
1620
+
1128
1621
  const coveredAt = (sha: string, granularities: Granularity[]) =>
1129
1622
  covering.filter((c) => {
1130
1623
  if (!c.node.constrains.some((x) => granularities.includes(x.granularity))) return false;
@@ -1133,9 +1626,10 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
1133
1626
  });
1134
1627
  const readsRecords = kinds.some((k) => k.records);
1135
1628
  // A decision's own work: a commit whose unit, or that unit's contract, names
1136
- // the decision, or whose unit serves a contract the decision constrains.
1629
+ // the decision, or whose unit serves a contract the decision constrains; or
1630
+ // a commit whose own trailers name the decision, or a work item implementing it.
1137
1631
  const ownWork = (j: Joined, d: DecisionNode) =>
1138
- !!j.unit && (j.decisions.some((x) => x === d.record || x === `${d.recordKind}/${d.record}`) || d.constrains.some((x) => x.granularity === "contract" && j.contracts.includes(x.entry)));
1632
+ (!!j.unit || j.carries.length > 0) && (j.decisions.some((x) => x === d.record || x === `${d.recordKind}/${d.record}`) || d.constrains.some((x) => x.granularity === "contract" && j.contracts.includes(x.entry)));
1139
1633
  for (const t of touched) {
1140
1634
  const c = nodes.get(`commit:${t.sha}`) as CommitNode | undefined;
1141
1635
  if (!c) continue;
@@ -1152,8 +1646,8 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
1152
1646
  if (readsRecords && c.state === "undecided") {
1153
1647
  find("intent-commit-undecided", `${t.sha.slice(0, 8)} changed the region when no decision constrained ${region.path} by path`, [c.id, rid]);
1154
1648
  }
1155
- if (readsRecords && !j.unit && c.pullRequest === null && coveredAt(t.sha, ["path", "member", "contract", "issue"]).length === 0) {
1156
- find("intent-commit-bare", `${t.sha.slice(0, 8)} names no unit, no pull request and no decision`, [c.id]);
1649
+ if (readsRecords && !j.unit && j.carries.length === 0 && c.pullRequest === null && coveredAt(t.sha, ["path", "member", "contract", "issue"]).length === 0) {
1650
+ find("intent-commit-bare", `${t.sha.slice(0, 8)} names no unit, no record, no pull request and no decision`, [c.id]);
1157
1651
  }
1158
1652
  const claims = [...new Set(j.authorship)].filter((key) => hasTrailer(c.trailers, key));
1159
1653
  if (claims.length > 0 && c.signature.level !== "attested") {
@@ -1303,7 +1797,37 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
1303
1797
  for (const h of by) edges.push({ kind: "addressed-by", from: f.id, to: workNode(h).id });
1304
1798
  }
1305
1799
  }
1800
+ // Reviews that led to a work item in the graph (#3154, E21): a session's comments[].follow_ups name it.
1801
+ const workNodes = [...nodes.values()].filter((n): n is WorkNode => n.kind === "work");
1802
+ if (workNodes.length > 0) {
1803
+ const byRef = new Map(workNodes.map((n) => [`${n.recordKind}:${n.record}`, n]));
1804
+ for (const k of kinds) {
1805
+ const loaded = k.records?.loaded.kind;
1806
+ if (!loaded?.session) continue;
1807
+ for (const v of k.records!.views) {
1808
+ if (v.id === null || !v.data) continue;
1809
+ const hits = new Map<WorkNode, number[]>();
1810
+ const note = (refs: unknown, comment: number | null) => {
1811
+ if (!Array.isArray(refs)) return;
1812
+ for (const ref of refs) {
1813
+ const n = typeof ref === "string" ? byRef.get(ref) : undefined;
1814
+ if (!n) continue;
1815
+ const list = hits.get(n) ?? [];
1816
+ if (comment !== null && !list.includes(comment)) list.push(comment);
1817
+ hits.set(n, list);
1818
+ }
1819
+ };
1820
+ note(v.data.follow_ups, null);
1821
+ const comments = Array.isArray(v.data.comments) ? v.data.comments : [];
1822
+ comments.forEach((c: unknown, i: number) => note((c as { follow_ups?: unknown } | null)?.follow_ups, i + 1));
1823
+ for (const [n, list] of hits) {
1824
+ (n.reviews ??= []).push({ id: `record:${loaded.name}/${v.id}`, recordKind: loaded.name, record: v.id, path: v.path, state: v.state, comments: list.sort((a, b) => a - b) });
1825
+ }
1826
+ }
1827
+ }
1828
+ }
1306
1829
  for (const f of findings) nodes.set(f.id, f);
1830
+ const why = explain();
1307
1831
  const all = [...nodes.values()];
1308
1832
  return {
1309
1833
  doc: {
@@ -1322,7 +1846,140 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
1322
1846
  artifacts: all.filter((n) => n.kind === "artifact").length,
1323
1847
  findings: findings.length,
1324
1848
  },
1849
+ why,
1325
1850
  },
1326
1851
  failed: reasons.some((r) => r.code === "intent-plugin-failed"),
1327
1852
  };
1853
+
1854
+ /** "Why is it like this" over the region (#3034): who made its current lines, and the decisions governing it, most relevant first. */
1855
+ function explain(): WhyAnswer {
1856
+ const hunksOf = (run: string, sha: string) => runJoins.runs.get(run)?.commits.find((c) => c.sha === sha)?.hunks ?? null;
1857
+ const joinOf = (run: string, sha: string): RunJoin[] => (runJoins.refs.get(sha) ?? []).find((r) => r.id === run)?.joinedBy ?? [];
1858
+ const joinsOf = (runs: string[], shas: string[]): RunJoin[] => {
1859
+ const all = new Set(runs.flatMap((id) => shas.flatMap((sha) => joinOf(id, sha))));
1860
+ return RUN_JOIN_ORDER.filter((j) => all.has(j));
1861
+ };
1862
+ // For a followed squash (#3035), the original commit behind each of its lines, by blame at the pull request's head.
1863
+ const origins = new Map<string, Map<number, string> | undefined>();
1864
+ const originOf = (sha: string, path: string, line: number): { squash: boolean; origin: string | undefined } => {
1865
+ const f = follows.get(sha);
1866
+ if (!f?.followed || !f.head) return { squash: false, origin: undefined };
1867
+ const key = `${sha}\0${path}`;
1868
+ if (!origins.has(key)) origins.set(key, squashLineOrigins(top!, sha, f.head, path, new Set(f.commits)));
1869
+ return { squash: true, origin: origins.get(key)?.get(line) };
1870
+ };
1871
+ // Each current line: its commit, and the runs that wrote it.
1872
+ const who = blamed.map((b) => {
1873
+ if (b.sha === null) return { line: b.line, sha: null, runs: [] as string[], narrowedBy: null as "hunks" | null, via: undefined as string[] | undefined };
1874
+ const sha = b.sha;
1875
+ const { squash, origin } = originOf(sha, b.origPath, b.origLine);
1876
+ const via = squash ? (origin ? [origin] : []) : undefined;
1877
+ // On a squash line blame at the head traced to one original, a run joined only through the other originals didn't write it.
1878
+ const refs = (runJoins.refs.get(sha) ?? []).filter((r) => !origin || r.joinedBy.some((j) => j !== "squash") || (r.via ?? []).includes(origin)).map((r) => r.id);
1879
+ const withHunks = refs.filter((id) => hunksOf(id, sha) !== null);
1880
+ if (withHunks.length === 0) return { line: b.line, sha, runs: refs, narrowedBy: null, via };
1881
+ const wrote = withHunks.filter((id) => hunksOf(id, sha)!.some((h) => h.path === b.origPath && h.start <= b.origLine && b.origLine <= h.end));
1882
+ // A run that recorded its hunks and doesn't cover the line didn't write it; a run that recorded none may have.
1883
+ return { line: b.line, sha, runs: wrote.length > 0 ? wrote : refs.filter((id) => !withHunks.includes(id)), narrowedBy: "hunks" as const, via };
1884
+ });
1885
+ const spans: WhySpan[] = [];
1886
+ for (const w of who) {
1887
+ const last = spans[spans.length - 1];
1888
+ const via = w.via;
1889
+ if (last && last.end === w.line - 1 && last.sha === w.sha && last.narrowedBy === w.narrowedBy && last.runs.join("\0") === w.runs.map((r) => `run:${r}`).join("\0") && last.via?.join("\0") === via?.join("\0")) {
1890
+ last.end = w.line;
1891
+ continue;
1892
+ }
1893
+ spans.push({ start: w.line, end: w.line, commit: w.sha ? `commit:${w.sha}` : null, sha: w.sha, runs: w.runs.map((r) => `run:${r}`), narrowedBy: w.narrowedBy, joinedBy: w.sha ? joinsOf(w.runs, [w.sha]) : [], ...(via ? { via } : {}) });
1894
+ }
1895
+ const ranges = (lines: number[]): LineRange[] => {
1896
+ const out: LineRange[] = [];
1897
+ for (const l of lines) {
1898
+ const last = out[out.length - 1];
1899
+ if (last && last.end === l - 1) last.end = l;
1900
+ else out.push({ start: l, end: l });
1901
+ }
1902
+ return out;
1903
+ };
1904
+
1905
+ // The decisions, most relevant first.
1906
+ const decisionNodes = [...nodes.values()].filter((n): n is DecisionNode => n.kind === "decision");
1907
+ const ownLines = (d: DecisionNode) => who.filter((w) => w.sha !== null && ownWork(joined.get(w.sha) ?? { contracts: [], authorship: [], decisions: [], carries: [] }, d)).length;
1908
+ const ORDER: WhyRelevance[] = ["carried", "path", "contract", "issue", "member", "related"];
1909
+ const ranked = decisionNodes.map((d) => {
1910
+ const lines = ownLines(d);
1911
+ const carried = lines > 0 || (fileLines === null && touched.some((t) => ownWork(joined.get(t.sha) ?? { contracts: [], authorship: [], decisions: [], carries: [] }, d)));
1912
+ const granularities = new Set(d.constrains.map((c) => c.granularity));
1913
+ const relevance: WhyRelevance = carried ? "carried" : ((["path", "contract", "issue", "member"] as const).find((g) => granularities.has(g)) ?? "related");
1914
+ // A narrower path constraint is more specific to the region.
1915
+ const specificity = Math.max(0, ...d.constrains.filter((c) => c.granularity === "path").map((c) => c.entry.length));
1916
+ return { d, entry: { decision: d.id, relevance, current: d.supersededBy === null, closed: d.closed, lines } satisfies WhyDecision, specificity };
1917
+ });
1918
+ ranked.sort(
1919
+ (a, b) =>
1920
+ Number(b.entry.current) - Number(a.entry.current) ||
1921
+ ORDER.indexOf(a.entry.relevance) - ORDER.indexOf(b.entry.relevance) ||
1922
+ b.entry.lines - a.entry.lines ||
1923
+ Number(b.entry.closed) - Number(a.entry.closed) ||
1924
+ b.specificity - a.specificity ||
1925
+ (b.d.decidedIn?.date ?? "").localeCompare(a.d.decidedIn?.date ?? "") ||
1926
+ a.d.id.localeCompare(b.d.id),
1927
+ );
1928
+ const decisions = ranked.map((r) => r.entry);
1929
+
1930
+ // The runs that wrote the current lines, most lines first; for a directory, every run in the walk.
1931
+ const runNodes = [...nodes.values()].filter((n): n is RunNode => n.kind === "run");
1932
+ const runEntry = (n: RunNode, lines: number, commits: string[]): WhyRun => {
1933
+ const r = runJoins.runs.get(n.run);
1934
+ const targets = edges.filter((e) => e.kind === "worked-on" && e.from === n.id).map((e) => nodes.get(e.to));
1935
+ const work = r?.unit ? targets.find((t): t is WorkNode => t?.kind === "work" && t.record === r.unit!.id) : undefined;
1936
+ const carried = new Set<string>();
1937
+ for (const t of targets) {
1938
+ if (t?.kind === "decision") carried.add(t.id);
1939
+ if (t?.kind === "work") for (const e of edges) if (e.kind === "implements" && e.from === t.id) carried.add(e.to);
1940
+ }
1941
+ const shas = commits.map((c) => c.slice("commit:".length));
1942
+ return { run: n.id, lines, commits, unit: r?.unit ? { id: r.unit.id, kind: r.unit.kind, node: work?.id ?? null } : null, decisions: [...carried], joinedBy: joinsOf([n.run], shas) };
1943
+ };
1944
+ let runs: WhyRun[];
1945
+ if (fileLines !== null) {
1946
+ const tally = new Map<string, { lines: number; commits: Set<string> }>();
1947
+ for (const w of who) {
1948
+ for (const id of w.runs) {
1949
+ const t = tally.get(id) ?? tally.set(id, { lines: 0, commits: new Set() }).get(id)!;
1950
+ t.lines++;
1951
+ t.commits.add(`commit:${w.sha}`);
1952
+ }
1953
+ }
1954
+ runs = [...tally]
1955
+ .map(([id, t]) => ({ n: nodes.get(`run:${id}`) as RunNode, t }))
1956
+ .filter((x) => x.n !== undefined)
1957
+ .sort((a, b) => b.t.lines - a.t.lines || (b.n.startedAt ?? "").localeCompare(a.n.startedAt ?? "") || a.n.run.localeCompare(b.n.run))
1958
+ .map((x) => runEntry(x.n, x.t.lines, [...x.t.commits]));
1959
+ } else {
1960
+ runs = runNodes
1961
+ .sort((a, b) => (b.startedAt ?? "").localeCompare(a.startedAt ?? "") || a.run.localeCompare(b.run))
1962
+ .map((n) => runEntry(n, 0, edges.filter((e) => e.kind === "made-by" && e.to === n.id).map((e) => e.from)));
1963
+ }
1964
+
1965
+ const explained = decisions.some((d) => d.current && d.relevance !== "related");
1966
+ const gaps: WhyGap[] = [];
1967
+ if (!explained) {
1968
+ gaps.push({
1969
+ code: "intent-why-no-decision",
1970
+ 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}`,
1971
+ });
1972
+ }
1973
+ const committed = who.filter((w) => w.sha !== null);
1974
+ if (fileLines !== null ? committed.length > 0 && committed.every((w) => w.runs.length === 0) : touched.length > 0 && runNodes.length === 0) {
1975
+ 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}` });
1976
+ }
1977
+ const uncommitted = who.filter((w) => w.sha === null).map((w) => w.line);
1978
+ 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) });
1979
+ const ambiguous = who.filter((w) => w.runs.length > 1).map((w) => w.line);
1980
+ if (ambiguous.length > 0) {
1981
+ gaps.push({ code: "intent-why-run-ambiguous", message: `${ambiguous.length} ${ambiguous.length === 1 ? "line comes" : "lines come"} from a commit several runs made, and no run's recorded hunks say which wrote ${ambiguous.length === 1 ? "it" : "them"}`, lines: ranges(ambiguous) });
1982
+ }
1983
+ return { lines: fileLines, blame: spans, decisions, runs, explained, gaps };
1984
+ }
1328
1985
  }