@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
@@ -15,10 +15,15 @@
15
15
  *
16
16
  * With `--since <rev>` it prints what changed between two revisions instead,
17
17
  * through `records-since.ts` (#2673).
18
+ *
19
+ * A working-tree read in git says, for each record, whether it is committed,
20
+ * modified or new against `HEAD`, and names the branch, head and base of the
21
+ * checkout; `--uncommitted` lists only the records `HEAD` doesn't hold
22
+ * (#3160, `records-checkout.ts`).
18
23
  */
19
24
 
20
25
  import { execFileSync } from "node:child_process";
21
- import { realpathSync } from "node:fs";
26
+ import { readFileSync, realpathSync } from "node:fs";
22
27
  import { dirname, join, relative, resolve, sep } from "node:path";
23
28
  import { formatError } from "../cli/format";
24
29
  import type { CommandContext } from "../cli/registry";
@@ -35,6 +40,7 @@ import {
35
40
  loadRecordKind,
36
41
  normalisePrincipal,
37
42
  readRecords,
43
+ readSessionSubjects,
38
44
  RECORD_SEAL_FIELD,
39
45
  RecordReadError,
40
46
  type LoadedRecordKind,
@@ -48,8 +54,18 @@ import {
48
54
  type SealInput,
49
55
  type VerdictAttestation,
50
56
  } from "./records";
57
+ import { readCheckoutHead, worktreeStates, type CheckoutView, type WorktreeState } from "./records-checkout";
58
+ import { lastWriteReader, type LastWrite } from "./write-lock";
59
+
60
+ function readTextOrNull(file: string): string | null {
61
+ try {
62
+ return readFileSync(file, "utf-8");
63
+ } catch {
64
+ return null;
65
+ }
66
+ }
51
67
  import { gitTree, workingTree, type WorkspaceTree } from "./tree";
52
- import type { DecisionWork } from "./work";
68
+ import type { DecisionWork, WorkAnswer } from "./work";
53
69
  import { activeAttestors, type CommitAttestor, type ProvenanceLevel } from "./trust/attestor";
54
70
  import { policyAtBase, recordProvenance, resolveBase, type BaseSource, type RecordProvenance } from "./trust/provenance";
55
71
  import { returnedPolicy, type TrustPolicy } from "./trust/policy";
@@ -62,7 +78,7 @@ export const RECORDS_CONTRACT_VERSION = 1;
62
78
  export const RECORDS_OUTPUT_SCHEMA_ID = "https://intentius.io/chant/schemas/workspace/records/v1/records.schema.json";
63
79
 
64
80
  const USAGE =
65
- "chant workspace records [--kind <kind file>] [--current] [--at <rev>] [--base <rev>] [--require attested] [--json] | chant workspace records [--kind <kind file>] --since <rev|session id> [--at <rev>] [--json] | chant workspace records pin <path> | chant workspace records new|amend|review|close (#2670, #2693)";
81
+ "chant workspace records [--kind <kind file>] [--current] [--uncommitted | --at <rev>] [--base <rev>] [--require attested] [--json] | chant workspace records [--kind <kind file>] --since <rev|session id> [--at <rev>] [--json] | chant workspace records pin <path> | chant workspace records new|amend|review|close (#2670, #2693)";
66
82
 
67
83
  /** Exit code when the read worked and a record falls below `--require`. */
68
84
  export const EXIT_BELOW_REQUIRED = 2;
@@ -81,6 +97,8 @@ export interface RecordsQuery {
81
97
  * false; the intent graph passes false, since it raises the warning itself.
82
98
  */
83
99
  workGaps?: boolean;
100
+ /** List only the records that are modified or new against `HEAD` (#3160). Needs git and a working-tree read. */
101
+ uncommitted?: boolean;
84
102
  }
85
103
 
86
104
  /**
@@ -108,6 +126,26 @@ export type RecordView = RecordEntry & {
108
126
  * (`record-decided.ts`). The intent graph's windows open at this commit.
109
127
  */
110
128
  decidedIn?: DecidedIn | null;
129
+ /**
130
+ * For a read in the working tree of a git repository (#3160): whether the
131
+ * record's file is as `HEAD` holds it (`committed`), changed from it
132
+ * (`modified`), or not in it (`new`), staged or not. Absent under `--at`.
133
+ */
134
+ worktree?: WorktreeState;
135
+ /**
136
+ * Beside `worktree` (#3173): the last chant write of the record's file, from
137
+ * the write journal (`write-lock.ts`), while the file holds the text it
138
+ * left, or null.
139
+ */
140
+ lastWrite?: LastWrite | null;
141
+ /**
142
+ * For a work kind whose work block names an answer kind (#3147): the
143
+ * decision-point answers about the item, the answer records whose
144
+ * `constrains` names its id, joined by id and never copied onto the item.
145
+ * Read at the same revision; in the working tree, with the questions a
146
+ * steward keeps on the lifecycle ledger (#2786) laid over it.
147
+ */
148
+ answers?: WorkAnswer[];
111
149
  };
112
150
 
113
151
  /** The role in the trust policy whose holders' verdicts the quorum does not count (#2671). */
@@ -134,6 +172,10 @@ export type RecordsDocument =
134
172
  /** The directory pinned paths resolve in, from the repository root: the workspace holding the kind file, or the repository root (#2549). */
135
173
  workspaceRoot: string;
136
174
  current: boolean;
175
+ /** Whether only the records `HEAD` doesn't hold were listed (`--uncommitted`, #3160). */
176
+ uncommitted: boolean;
177
+ /** For a read in the working tree of a git repository (#3160): the branch, head and base it is on, and the records deleted from it. Absent under `--at`. */
178
+ checkout?: CheckoutView;
137
179
  trust: TrustView;
138
180
  records: RecordView[];
139
181
  summary: { total: number; valid: number; invalid: number; superseded: number };
@@ -227,7 +269,8 @@ export async function queryDeclaredRecords(kinds: { declared: RecordKindDeclarat
227
269
  const doc = await queryRecords({ ...query, kind: k.file });
228
270
  out.push({ ...doc, declared: { member: k.declared.member, path: k.declared.path, name: k.declared.name } });
229
271
  }
230
- return { $schema: RECORDS_OUTPUT_SCHEMA_ID, contract: RECORDS_CONTRACT_VERSION, kinds: out, ...(query.current ? { spec: specOf(out) } : {}) };
272
+ // The spec is every current spec record; an --uncommitted read lists only some, so it has none.
273
+ return { $schema: RECORDS_OUTPUT_SCHEMA_ID, contract: RECORDS_CONTRACT_VERSION, kinds: out, ...(query.current && !query.uncommitted ? { spec: specOf(out) } : {}) };
231
274
  }
232
275
 
233
276
  /** A records read, before provenance. */
@@ -345,17 +388,15 @@ export async function readRecordsFor(
345
388
  if (at === null && query.overlay) source = await query.overlay(source, { loaded, root });
346
389
  const history = top ? gitHistory(top, at ?? "HEAD", workspaceRoot) : undefined;
347
390
  // A session kind's verdicts name records of another kind, read from the same tree (#2673).
348
- let subjects: { records: RecordEntry[]; reviews: string } | undefined;
349
- if (loaded.kind.session) {
350
- const subjectKind = await loadRecordKind(resolve(dirname(loaded.file), loaded.kind.session.subjects.kind), cwd);
351
- subjects = { records: (await readRecords(subjectKind, { root, source })).records, reviews: subjectKind.kind.reviews?.field ?? "reviews" };
352
- }
391
+ const subjects = await readSessionSubjects(loaded, root, source);
353
392
  const result = await readRecords(loaded, { root, source, current: !!query.current, assets, workspaceRoot, ...(history ? { history } : {}), ...(subjects ? { subjects } : {}) });
354
393
  return { loaded, root, top, at, workspaceRoot, tree: assets, result };
355
394
  }
356
395
 
357
396
  /** Run the query and build the document `--json` prints. Never throws a {@link RecordReadError}. */
358
397
  export async function queryRecords(query: RecordsQuery): Promise<RecordsDocument> {
398
+ // A revision has no working tree to hold anything uncommitted; the CLI and the MCP tool refuse the pair first.
399
+ if (query.uncommitted && query.at !== undefined) throw new Error("--uncommitted reads the working tree and takes no --at");
359
400
  try {
360
401
  const { loaded, root, top, at, workspaceRoot, tree, result } = await readRecordsFor(query);
361
402
  // Provenance, judged by the policy at base and never by the tree read (#2547).
@@ -395,6 +436,43 @@ export async function queryRecords(query: RecordsQuery): Promise<RecordsDocument
395
436
  const leases = await activeWorkLeases(loaded.file);
396
437
  for (const r of records) if (r.id !== null) r.lease = leases.get(r.id) ?? null;
397
438
  }
439
+ if (loaded.kind.work?.answers) {
440
+ // The answers about each item, joined by id (#3147). An answer kind that can't be read leaves answers out rather than failing the work read.
441
+ const [{ workAnswers }, { readLedgerAnswers, withLedgerAnswers }] = await Promise.all([import("./work"), import("./answers-ledger")]);
442
+ const answersRead = await readRecordsFor({
443
+ kind: resolve(dirname(loaded.file), loaded.kind.work.answers),
444
+ cwd: query.cwd,
445
+ ...(query.at !== undefined ? { at: query.at } : {}),
446
+ overlay: async (base, { loaded: l, root: r }) =>
447
+ withLedgerAnswers(base, await readLedgerAnswers({ file: l.file, name: l.kind.name, dirRel: relative(r, l.dir).split(sep).join("/") || "." })),
448
+ }).catch((err: unknown) => {
449
+ if (err instanceof RecordReadError) return undefined;
450
+ throw err;
451
+ });
452
+ if (answersRead) {
453
+ const byItem = workAnswers(answersRead.result.records, records.flatMap((r) => (r.id !== null ? [r.id] : [])));
454
+ for (const r of records) if (r.id !== null) r.answers = byItem.get(r.id) ?? [];
455
+ }
456
+ }
457
+ // Which records the checkout holds uncommitted, judged after every record is read, so links still resolve across the whole set (#3160).
458
+ let checkout: CheckoutView | undefined;
459
+ let listed = records;
460
+ let summary = result.summary;
461
+ if (at === null && top) {
462
+ const { head, ...rest } = readCheckoutHead(top, query.base);
463
+ const dir = relative(root, loaded.dir).split(sep).join("/") || ".";
464
+ const { states, deleted } = worktreeStates(top, head, dir, records.map((r) => r.path), new RegExp(loaded.kind.location.match));
465
+ for (const r of records) r.worktree = states.get(r.path) ?? "new";
466
+ const lastWrite = lastWriteReader(top);
467
+ for (const r of records) r.lastWrite = lastWrite(r.path, readTextOrNull(join(top, ...r.path.split("/"))));
468
+ checkout = { branch: rest.branch, head, base: rest.base, baseFrom: rest.baseFrom, deleted };
469
+ if (query.uncommitted) {
470
+ listed = records.filter((r) => r.worktree !== "committed");
471
+ summary = { ...summary, total: listed.length, valid: listed.filter((r) => r.valid).length, invalid: listed.filter((r) => !r.valid).length };
472
+ }
473
+ } else if (query.uncommitted && !top) {
474
+ throw new RecordReadError("not-a-git-repository", "--uncommitted compares the working tree with HEAD, and this directory is not in a git repository");
475
+ }
398
476
  return {
399
477
  $schema: RECORDS_OUTPUT_SCHEMA_ID,
400
478
  contract: RECORDS_CONTRACT_VERSION,
@@ -408,9 +486,11 @@ export async function queryRecords(query: RecordsQuery): Promise<RecordsDocument
408
486
  at,
409
487
  workspaceRoot,
410
488
  current: !!query.current,
489
+ uncommitted: !!query.uncommitted,
490
+ ...(checkout ? { checkout } : {}),
411
491
  trust: { base: base.commit, baseFrom: base.from, active: policy.active, signersPath: policy.signersPath, problems: policy.problems },
412
- records,
413
- summary: result.summary,
492
+ records: listed,
493
+ summary,
414
494
  ...(result.decisions ? { decisions: result.decisions } : {}),
415
495
  };
416
496
  } catch (err) {
@@ -561,6 +641,11 @@ export async function runWorkspaceRecords(ctx: CommandContext): Promise<number>
561
641
  console.error(formatError({ message: `chant workspace records takes no argument but pin, new, amend, review or close (got ${args.extraPositional})`, hint: USAGE }));
562
642
  return 1;
563
643
  }
644
+ const pairing = uncommittedPairing(args);
645
+ if (pairing) {
646
+ console.error(formatError({ message: pairing, hint: USAGE }));
647
+ return 1;
648
+ }
564
649
  if (!args.kind) return runDeclaredRecords(args);
565
650
  if (args.require !== undefined && args.require !== "attested") {
566
651
  console.error(formatError({ message: `--require takes one level, attested, not ${JSON.stringify(args.require)}`, hint: USAGE }));
@@ -579,13 +664,13 @@ export async function runWorkspaceRecords(ctx: CommandContext): Promise<number>
579
664
  else console.log(formatSince(since));
580
665
  return "error" in since ? 1 : 0;
581
666
  }
582
- const doc = await queryRecords({ kind: args.kind, current: args.current, at: args.at, base: args.base, cwd: process.cwd() });
667
+ const doc = await queryRecords({ kind: args.kind, current: args.current, uncommitted: args.uncommitted, at: args.at, base: args.base, cwd: process.cwd() });
583
668
  if (args.json) {
584
669
  console.log(JSON.stringify(doc, null, 2));
585
670
  } else if ("error" in doc) {
586
671
  console.error(formatError({ message: `${doc.error.code}: ${doc.error.message}`, hint: USAGE }));
587
672
  } else {
588
- console.log(formatRecords(doc.records, doc.summary, doc.at));
673
+ console.log(formatRecords(doc.records, doc.summary, doc.at, doc.checkout));
589
674
  }
590
675
  if ("error" in doc) return 1;
591
676
  if (args.require) {
@@ -643,7 +728,7 @@ async function runDeclaredRecords(args: CommandContext["args"]): Promise<number>
643
728
  return 1;
644
729
  }
645
730
  if (args.since !== undefined) return runDeclaredSince(declared, args);
646
- const set = await queryDeclaredRecords(declared, { current: args.current, at: args.at, base: args.base, cwd: process.cwd() });
731
+ const set = await queryDeclaredRecords(declared, { current: args.current, uncommitted: args.uncommitted, at: args.at, base: args.base, cwd: process.cwd() });
647
732
  if (args.json) console.log(JSON.stringify(set, null, 2));
648
733
  for (const doc of args.json ? [] : set.kinds) {
649
734
  if ("error" in doc) {
@@ -651,7 +736,7 @@ async function runDeclaredRecords(args: CommandContext["args"]): Promise<number>
651
736
  continue;
652
737
  }
653
738
  console.log(`${doc.declared.name ?? doc.kind.name} (${doc.declared.path})`);
654
- console.log(formatRecords(doc.records, doc.summary, doc.at));
739
+ console.log(formatRecords(doc.records, doc.summary, doc.at, doc.checkout));
655
740
  }
656
741
  if (set.kinds.some((d) => "error" in d)) return 1;
657
742
  if (args.require) {
@@ -717,7 +802,20 @@ export function belowRequired(records: RecordView[], required: ProvenanceLevel):
717
802
  return records.filter((r) => r.provenance.level !== required);
718
803
  }
719
804
 
720
- function formatRecords(records: RecordView[], summary: { total: number; valid: number; invalid: number; superseded: number }, at: string | null): string {
805
+ /** Why `--uncommitted` can't be given with the other flags given, or undefined when it can (#3160). */
806
+ function uncommittedPairing(args: CommandContext["args"]): string | undefined {
807
+ if (!args.uncommitted) return undefined;
808
+ if (args.at !== undefined) return "--uncommitted reads the working tree against HEAD, and takes no --at";
809
+ if (args.since !== undefined) return "--uncommitted takes no --since: --since compares two revisions";
810
+ return undefined;
811
+ }
812
+
813
+ function formatRecords(
814
+ records: RecordView[],
815
+ summary: { total: number; valid: number; invalid: number; superseded: number },
816
+ at: string | null,
817
+ checkout?: CheckoutView,
818
+ ): string {
721
819
  const lines: string[] = [];
722
820
  const idWidth = Math.max(2, ...records.map((r) => (r.id ?? "-").length));
723
821
  const stateWidth = Math.max(5, ...records.map((r) => (r.state ?? "-").length));
@@ -728,7 +826,8 @@ function formatRecords(records: RecordView[], summary: { total: number; valid: n
728
826
  const remediated = r.remediatedBy.length > 0 ? ` remediated by ${r.remediatedBy.join(", ")}` : "";
729
827
  const attested = r.provenance.level === "attested" ? ` attested by ${r.provenance.principal}` : "";
730
828
  const sealed = r.attested === true ? ` sealed by ${(r.data?.[RECORD_SEAL_FIELD] as { signer: string }).signer}` : "";
731
- lines.push(`${(r.id ?? "-").padEnd(idWidth)} ${(r.state ?? "-").padEnd(stateWidth)} ${title}${superseded}${remediated}${attested}${sealed}${flag}`);
829
+ const uncommitted = r.worktree === "new" || r.worktree === "modified" ? ` (${r.worktree}, uncommitted)` : "";
830
+ lines.push(`${(r.id ?? "-").padEnd(idWidth)} ${(r.state ?? "-").padEnd(stateWidth)} ${title}${superseded}${remediated}${attested}${sealed}${uncommitted}${flag}`);
732
831
  if (r.ready !== undefined) {
733
832
  const blocked = (r.blockedBy ?? []).map((b) => `${b.id} (${b.state ?? "unknown"})`).join(", ");
734
833
  const implemented = (r.implements ?? []).map((d) => `${d.id} (${d.state ?? "unknown"})`).join(", ");
@@ -745,9 +844,16 @@ function formatRecords(records: RecordView[], summary: { total: number; valid: n
745
844
  lines.push(`${" ".repeat(idWidth + 2)}quorum ${q.agreed} of ${q.need} agreed, ${verdict}; ${q.notCounted.length} not counted${concerns}`);
746
845
  }
747
846
  }
847
+ for (const path of checkout?.deleted ?? []) lines.push(`${"-".padEnd(idWidth)} ${"-".padEnd(stateWidth)} ${path} (deleted, uncommitted)`);
748
848
  lines.push(
749
849
  `${summary.total} records${at ? ` at ${at.slice(0, 8)}` : ""}: ${summary.valid} valid, ${summary.invalid} invalid, ${summary.superseded} superseded`,
750
850
  );
851
+ if (checkout) {
852
+ const branch = checkout.branch ?? "a detached HEAD";
853
+ const head = checkout.head ? ` at ${checkout.head.slice(0, 8)}` : " with no commits";
854
+ const base = checkout.base ? `, forked from ${checkout.base.slice(0, 8)} of ${checkout.baseFrom}` : "";
855
+ lines.push(`working tree on ${branch}${head}${base}`);
856
+ }
751
857
  return lines.join("\n");
752
858
  }
753
859
 
@@ -108,6 +108,11 @@
108
108
  "$ref": "#/$defs/warning"
109
109
  }
110
110
  },
111
+ "digest": {
112
+ "type": "string",
113
+ "pattern": "^[0-9a-f]{64}$",
114
+ "description": "Added in contract 1 by #3173. The record's digest after the write, as chant workspace records --json reports it (with --dry-run, the digest it would have). Pass it as --expect to the next amend, review or close of the record, so that write is refused with record-conflict if someone else wrote the record in between."
115
+ },
111
116
  "text": {
112
117
  "type": "string",
113
118
  "description": "With --dry-run only: the whole text of the file the command would write."
@@ -171,8 +176,12 @@
171
176
  "write-usage-invalid",
172
177
  "record-not-found",
173
178
  "record-closed",
179
+ "record-conflict",
180
+ "write-lock-timeout",
181
+ "write-lock-not-held",
174
182
  "write-scope-member",
175
183
  "write-scope-kind",
184
+ "write-scope-class-unknown",
176
185
  "agent-unknown",
177
186
  "record-unparseable",
178
187
  "record-schema-invalid",
@@ -192,6 +201,76 @@
192
201
  }
193
202
  }
194
203
  },
204
+ "conflict": {
205
+ "type": "object",
206
+ "description": "Added in contract 1 by #3173. With record-conflict only: the record as it is now. Re-read it, and write again with --expect set to digest if the change still stands.",
207
+ "required": [
208
+ "id",
209
+ "path",
210
+ "expected",
211
+ "digest",
212
+ "lastWrite"
213
+ ],
214
+ "properties": {
215
+ "id": {
216
+ "type": "string"
217
+ },
218
+ "path": {
219
+ "type": "string",
220
+ "description": "The record file, from the repository root."
221
+ },
222
+ "expected": {
223
+ "type": "string",
224
+ "description": "The digest given with --expect."
225
+ },
226
+ "digest": {
227
+ "type": "string",
228
+ "pattern": "^[0-9a-f]{64}$",
229
+ "description": "The record's digest now."
230
+ },
231
+ "lastWrite": {
232
+ "description": "The last chant write of the record file, from the write journal in the git directory, while the file still holds the text it left; null when the journal does not know it.",
233
+ "oneOf": [
234
+ {
235
+ "type": "null"
236
+ },
237
+ {
238
+ "type": "object",
239
+ "required": [
240
+ "verb",
241
+ "by",
242
+ "agent",
243
+ "at"
244
+ ],
245
+ "properties": {
246
+ "verb": {
247
+ "type": "string",
248
+ "description": "The write command, such as records amend or points answer."
249
+ },
250
+ "by": {
251
+ "type": [
252
+ "string",
253
+ "null"
254
+ ],
255
+ "description": "The principal the write named, or null when it named none."
256
+ },
257
+ "agent": {
258
+ "type": [
259
+ "string",
260
+ "null"
261
+ ],
262
+ "description": "The agent session it was made in (CHANT_AGENT), or null."
263
+ },
264
+ "at": {
265
+ "type": "string",
266
+ "description": "When, as an ISO 8601 time."
267
+ }
268
+ }
269
+ }
270
+ ]
271
+ }
272
+ }
273
+ },
195
274
  "path": false
196
275
  }
197
276
  }
@@ -10,10 +10,11 @@
10
10
  * in place and writes only when the session comes back valid: a verdict
11
11
  * naming a record the subjects lack is refused with
12
12
  * `session-verdict-unknown-record`. A session already closed is
13
- * `record-closed`. It writes one file or none and never commits.
13
+ * `record-closed`. It writes one file or none and never commits. Like them
14
+ * it holds the working tree's write lock, and takes `--expect <digest>`
15
+ * (#3173).
14
16
  */
15
17
 
16
- import { writeFileSync } from "node:fs";
17
18
  import type { ReasonCode } from "./reason-codes";
18
19
  import { sessionSeal } from "./record-sessions";
19
20
  import { RECORD_REASON_CODES } from "./records";
@@ -28,12 +29,15 @@ import {
28
29
  RECORDS_WRITE_CONTRACT_VERSION,
29
30
  RecordWriteError,
30
31
  refuseOutOfScope,
32
+ refuseStale,
31
33
  replaceFields,
32
34
  stableJson,
33
- validateWrite,
35
+ validatedEntry,
36
+ writeWho,
34
37
  type WriteFailure,
35
38
  type WriteResult,
36
39
  } from "./records-write";
40
+ import { noteWrites, withWriteLock, writeFileAtomic, WRITE_LOCK_CODES } from "./write-lock";
37
41
  import { headCommit } from "./session-kinds";
38
42
  import { WRITE_SCOPE_CODES } from "./write-scope";
39
43
 
@@ -45,6 +49,8 @@ export const CLOSE_ERROR_CODES = [
45
49
  "write-usage-invalid",
46
50
  "record-not-found",
47
51
  "record-closed",
52
+ "record-conflict",
53
+ ...WRITE_LOCK_CODES,
48
54
  ...WRITE_SCOPE_CODES,
49
55
  ...RECORD_REASON_CODES,
50
56
  ] as const satisfies readonly ReasonCode[];
@@ -71,6 +77,8 @@ export interface CloseRecordOptions {
71
77
  now?: Date;
72
78
  /** The agent session the write is made in (#2548). */
73
79
  agent?: string;
80
+ /** The session's digest the caller last read (#3173): the close is refused with record-conflict when it has another. */
81
+ expect?: string;
74
82
  }
75
83
 
76
84
  /** An ISO 8601 time in UTC to the second, as the reference session schema's dateTime takes it. */
@@ -78,8 +86,19 @@ function isoSeconds(d: Date): string {
78
86
  return d.toISOString().replace(/\.\d{3}Z$/, "Z");
79
87
  }
80
88
 
81
- /** `records close`: close one open session and seal it. */
89
+ /** `records close`: close one open session and seal it, holding the write lock (#3173). */
82
90
  export async function closeRecord(opts: CloseRecordOptions): Promise<CloseDocument> {
91
+ try {
92
+ if (opts.expect !== undefined && !/^[0-9a-f]{64}$/.test(opts.expect)) {
93
+ throw new RecordWriteError("write-usage-invalid", `--expect takes a record's digest, 64 lowercase hex digits as records --json prints it, not ${JSON.stringify(opts.expect)}`);
94
+ }
95
+ return await withWriteLock(opts.cwd, writeWho("records close", opts), opts.dryRun, () => closeRecordLocked(opts));
96
+ } catch (err) {
97
+ return failure<CloseErrorCode>(RECORDS_CLOSE_SCHEMA_ID, err);
98
+ }
99
+ }
100
+
101
+ async function closeRecordLocked(opts: CloseRecordOptions): Promise<CloseDocument> {
83
102
  try {
84
103
  const o = await open(opts.kind, opts.cwd);
85
104
  refuseOutOfScope(o, "close", opts.cwd, opts);
@@ -92,6 +111,7 @@ export async function closeRecord(opts: CloseRecordOptions): Promise<CloseDocume
92
111
  if (closedState === undefined) throw new RecordWriteError("write-usage-invalid", `the ${kind.name} kind lists no closed state, so a session of it can't close`);
93
112
  const before = await readAll(o, o.source);
94
113
  const target = findRecord(before, opts.id, kind.name);
114
+ refuseStale(o, target, opts.id, opts.expect);
95
115
  if (target.state !== null && (kind.closedStates ?? []).includes(target.state)) {
96
116
  throw new RecordWriteError("record-closed", `${opts.id} is already ${target.state}, and a closed session is sealed and stays as it is`);
97
117
  }
@@ -115,8 +135,11 @@ export async function closeRecord(opts: CloseRecordOptions): Promise<CloseDocume
115
135
  if (text === undefined || sessionSeal(text, decl.seal, kind.format) !== digest) {
116
136
  throw new RecordWriteError("record-unparseable", `${target.path}: the ${decl.seal} line can't be added without changing the text it seals`);
117
137
  }
118
- const warnings = await validateWrite(o, before, target.path, text);
119
- if (!opts.dryRun) writeFileSync(abs(o, target.path), text);
138
+ const entry = await validatedEntry(o, before, target.path, text);
139
+ if (!opts.dryRun) {
140
+ writeFileAtomic(abs(o, target.path), text);
141
+ noteWrites(o.root, [{ path: target.path, text }], writeWho("records close", opts));
142
+ }
120
143
  const written = { ...merged, [decl.seal]: digest };
121
144
  return {
122
145
  $schema: RECORDS_CLOSE_SCHEMA_ID,
@@ -128,7 +151,8 @@ export async function closeRecord(opts: CloseRecordOptions): Promise<CloseDocume
128
151
  seal: { field: decl.seal, digest },
129
152
  closedRev,
130
153
  dryRun: !!opts.dryRun,
131
- warnings,
154
+ warnings: entry.warnings,
155
+ digest: entry.digest,
132
156
  ...(opts.dryRun ? { text } : {}),
133
157
  };
134
158
  } catch (err) {
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Concurrent writers in one working tree through the command line (#3173,
3
+ * ws-089): three `records amend --expect` processes started at once against
4
+ * one record, two people's and an agent's (CHANT_AGENT). Exactly one wins;
5
+ * the others exit 1 with record-conflict and the digest to retry from, and the
6
+ * retry wins. Then a batch: `lock acquire` holds the lock, a write run with
7
+ * CHANT_WRITE_LOCK goes ahead, and one without it is refused with
8
+ * write-lock-timeout until `lock release`. The in-process cases are in
9
+ * records-concurrent.test.ts.
10
+ */
11
+
12
+ import { execFile } from "node:child_process";
13
+ import { readFileSync } from "node:fs";
14
+ import { join } from "node:path";
15
+ import { pathToFileURL } from "node:url";
16
+ import { afterAll, beforeAll, describe, expect, test } from "vitest";
17
+ import { cleanScratch, git, REPO, repo } from "./__fixtures__/contract-repo";
18
+ import { parseFrontMatter } from "./records";
19
+ import { renderRecord } from "./records-write";
20
+
21
+ const DECISIONS = join(REPO, "docs", "design", "decisions");
22
+ const KIND = "decisions/decision.kind.mjs";
23
+ type Data = Record<string, unknown>;
24
+
25
+ const SAMPLE = (() => {
26
+ const fm = parseFrontMatter(readFileSync(join(DECISIONS, "ws-003-seal-scope.md"), "utf-8"));
27
+ if (!fm.ok) throw new Error(fm.message);
28
+ return fm.value;
29
+ })();
30
+ const proposal = (over: Data): Data => ({ ...structuredClone(SAMPLE), state: "proposed", choice: null, decided_by: null, decided_on: null, ...over });
31
+
32
+ let root: string;
33
+ beforeAll(() => {
34
+ root = repo(
35
+ {
36
+ "chant.workspace.json": JSON.stringify({
37
+ name: "box",
38
+ schema: 1,
39
+ members: [{ name: "app", dir: "app", kind: "other", because: "a plain Node server" }],
40
+ records: [{ kind: KIND }],
41
+ writeScope: { agent: { records: { decision: ["new", "amend", "review"] } } },
42
+ agents: [{ name: "app-agent", member: "app" }],
43
+ }),
44
+ [KIND]: readFileSync(join(DECISIONS, "decision.kind.mjs"), "utf-8"),
45
+ "decisions/decision.schema.json": readFileSync(join(DECISIONS, "decision.schema.json"), "utf-8"),
46
+ "decisions/ws-001-one.md": renderRecord(proposal({ id: "ws-001", title: "One" }), "\n# One\n"),
47
+ "app/server.mjs": "export const port = 8080;\n",
48
+ },
49
+ true,
50
+ );
51
+ git(root, "branch", "-M", "main");
52
+ });
53
+ afterAll(cleanScratch);
54
+
55
+ /** Run chant in the repository, without waiting on the others. */
56
+ function chant(args: string[], opts: { input?: string; env?: Record<string, string> } = {}): Promise<{ status: number; doc: Record<string, any> }> {
57
+ const loader = pathToFileURL(join(REPO, "node_modules", "tsx", "dist", "loader.mjs")).href;
58
+ return new Promise((resolve, reject) => {
59
+ const child = execFile(
60
+ process.execPath,
61
+ ["--import", loader, join(REPO, "packages", "core", "src", "cli", "main.ts"), "workspace", ...args],
62
+ { cwd: root, encoding: "utf-8", env: { ...process.env, TSX_DISABLE_CACHE: "1", ...opts.env } },
63
+ (err, stdout, stderr) => {
64
+ try {
65
+ resolve({ status: err ? Number((err as { code?: number }).code ?? 1) : 0, doc: JSON.parse(stdout) });
66
+ } catch {
67
+ reject(new Error(`chant ${args.join(" ")} printed no JSON: ${stderr}`));
68
+ }
69
+ },
70
+ );
71
+ child.stdin!.end(opts.input ?? "");
72
+ });
73
+ }
74
+
75
+ const amend = (fields: Data, expect: string, env?: Record<string, string>) => chant(["records", "amend", "ws-001", "--kind", KIND, "--set", "-", "--expect", expect], { input: JSON.stringify(fields), env });
76
+
77
+ describe("concurrent writers through the command line (#3173)", () => {
78
+ test(
79
+ "three amends from one digest in three processes: one wins, two conflict, and their retries win",
80
+ async () => {
81
+ const read = await chant(["records", "--kind", KIND, "--json"]);
82
+ const d0 = read.doc.records.find((r: { id: string }) => r.id === "ws-001").digest as string;
83
+ const runs = await Promise.all([
84
+ amend({ title: "One, as alice has it" }, d0),
85
+ amend({ question: "What does bob ask?" }, d0),
86
+ amend({ constrains: ["member:app"] }, d0, { CHANT_AGENT: "app-agent" }),
87
+ ]);
88
+ const won = runs.filter((r) => r.status === 0);
89
+ const lost = runs.filter((r) => r.status !== 0);
90
+ expect(won).toHaveLength(1);
91
+ expect(lost.map((r) => r.doc.error.code)).toEqual(["record-conflict", "record-conflict"]);
92
+ const d1 = won[0].doc.digest as string;
93
+ for (const r of lost) expect(r.doc.conflict).toMatchObject({ expected: d0, digest: d1, lastWrite: { verb: "records amend" } });
94
+
95
+ // Each loser retries from what it was told, one after the other.
96
+ const fieldsOf = (i: number) => [{ title: "One, as alice has it" }, { question: "What does bob ask?" }, { constrains: ["member:app"] }][i];
97
+ let digest = d1;
98
+ for (const r of lost) {
99
+ const i = runs.indexOf(r);
100
+ const retry = await amend(fieldsOf(i), digest, i === 2 ? { CHANT_AGENT: "app-agent" } : undefined);
101
+ expect(retry.status).toBe(0);
102
+ digest = retry.doc.digest;
103
+ }
104
+ const end = await chant(["records", "--kind", KIND, "--json"]);
105
+ const rec = end.doc.records.find((r: { id: string }) => r.id === "ws-001");
106
+ expect(rec.digest).toBe(digest);
107
+ expect(rec.data).toMatchObject({ title: "One, as alice has it", question: "What does bob ask?", constrains: ["member:app"] });
108
+ expect(rec.lastWrite).toMatchObject({ verb: "records amend" });
109
+ },
110
+ 120_000,
111
+ );
112
+
113
+ test(
114
+ "a batch holds the lock across calls: its own writes go ahead, another writer's waits and is refused",
115
+ async () => {
116
+ const taken = await chant(["lock", "acquire", "--holder", "hud:alice", "--ttl", "60s"]);
117
+ expect(taken).toMatchObject({ status: 0, doc: { verb: "acquire", holder: { verb: "batch", by: "hud:alice" } } });
118
+ const token = taken.doc.token as string;
119
+ const status = await chant(["lock"]);
120
+ expect(status.doc.holder).toMatchObject({ by: "hud:alice" });
121
+ expect(status.doc.token).toBeUndefined();
122
+
123
+ const outsider = await chant(["records", "review", "ws-001", "--kind", KIND, "--verdict", "agree", "--by", "bob"], { env: { CHANT_WRITE_LOCK_WAIT_MS: "300" } });
124
+ expect(outsider).toMatchObject({ status: 1, doc: { error: { code: "write-lock-timeout" } } });
125
+ expect(outsider.doc.error.message).toContain("hud:alice");
126
+ const inside = await chant(["records", "review", "ws-001", "--kind", KIND, "--verdict", "agree", "--by", "alice"], { env: { CHANT_WRITE_LOCK: token } });
127
+ expect(inside.status).toBe(0);
128
+
129
+ expect((await chant(["lock", "release", "--token", token])).status).toBe(0);
130
+ const after = await chant(["records", "review", "ws-001", "--kind", KIND, "--verdict", "agree", "--by", "bob"]);
131
+ expect(after.status).toBe(0);
132
+ },
133
+ 120_000,
134
+ );
135
+ });