@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
@@ -19,12 +19,27 @@
19
19
  * as well, after checking both files, so the two lists never differ.
20
20
  * `records close` is in `records-close.ts`.
21
21
  *
22
+ * Each write holds the working tree's write lock (`write-lock.ts`, #3173,
23
+ * ws-089) from its first read to its last write, so writes to one working
24
+ * tree never interleave. `amend`, `review` and `close` take `--expect
25
+ * <digest>`: the record's digest the caller last read. When the record has
26
+ * moved on since, the write is refused with record-conflict, naming the
27
+ * digest it has now and the last chant write of it, and the caller re-reads
28
+ * and tries again.
29
+ *
22
30
  * `new --sign` and `amend --sign` seal the record's author (#2688), and
23
31
  * `review --sign` seals a verdict (#2687): see `trust/seal.ts`. A record of
24
32
  * a kind that declares `seal` gets its whole-file seal when `new` or `amend`
25
33
  * writes it into a closed state (#2546, ws-063).
34
+ *
35
+ * `new` also writes a record of a content-addressed JSON kind (`format:
36
+ * "json"`, `idFrom: "sha256"`, ws-053), such as the reference workspace's
37
+ * evidence (ws-082): the fields as JSON, in a file named for the SHA-256 of
38
+ * its bytes. Such a record never changes, so amend, review and close refuse
39
+ * the kind.
26
40
  */
27
41
 
42
+ import { createHash } from "node:crypto";
28
43
  import { readFileSync, statSync, writeFileSync } from "node:fs";
29
44
  import { dirname, join, posix, relative, resolve } from "node:path";
30
45
  import type { CommandContext } from "../cli/registry";
@@ -35,6 +50,7 @@ import {
35
50
  loadRecordKind,
36
51
  parseFrontMatter,
37
52
  readRecords,
53
+ readSessionSubjects,
38
54
  RECORD_REASON_CODES,
39
55
  RecordReadError,
40
56
  RECORD_SEAL_FIELD,
@@ -50,8 +66,10 @@ import { policyAtBase, resolveBase } from "./trust/provenance";
50
66
  import { WorkspaceReadError } from "./declaration";
51
67
  import { findSessionKinds, headCommit, sessionKindsFor } from "./session-kinds";
52
68
  import { workingTree } from "./tree";
53
- import { AGENT_ENV, refuseRecordWrite, WRITE_SCOPE_CODES, WriteScopeError } from "./write-scope";
69
+ import { AGENT_ENV, refuseRecordWrite, WRITE_SCOPE_CODES, WriteScopeError, type ScopeSource } from "./write-scope";
70
+ import { IDENTITY_CODES, IdentityError, refuseUnidentified } from "./identity";
54
71
  import type { WriteVerb } from "./declaration";
72
+ import { lastWriteReader, noteWrites, withWriteLock, writeFileAtomic, WriteLockError, WRITE_LOCK_CODES, type LastWrite, type WriteLockWho } from "./write-lock";
55
73
 
56
74
  // ── Contract ─────────────────────────────────────────────────────────────────
57
75
 
@@ -77,7 +95,9 @@ export const NEW_ERROR_CODES = [
77
95
  "source-harvest-not-proposed",
78
96
  "record-state-not-initial",
79
97
  "ratify-quorum-not-met",
98
+ ...WRITE_LOCK_CODES,
80
99
  ...WRITE_SCOPE_CODES,
100
+ ...IDENTITY_CODES,
81
101
  ...RECORD_REASON_CODES,
82
102
  ] as const satisfies readonly ReasonCode[];
83
103
 
@@ -92,7 +112,10 @@ export const AMEND_ERROR_CODES = [
92
112
  "amend-supersede-instead",
93
113
  "record-sign-failed",
94
114
  "ratify-quorum-not-met",
115
+ "record-conflict",
116
+ ...WRITE_LOCK_CODES,
95
117
  ...WRITE_SCOPE_CODES,
118
+ ...IDENTITY_CODES,
96
119
  ...RECORD_REASON_CODES,
97
120
  ] as const satisfies readonly ReasonCode[];
98
121
 
@@ -107,7 +130,10 @@ export const REVIEW_ERROR_CODES = [
107
130
  "review-sign-failed",
108
131
  "session-unknown",
109
132
  "session-not-open",
133
+ "record-conflict",
134
+ ...WRITE_LOCK_CODES,
110
135
  ...WRITE_SCOPE_CODES,
136
+ ...IDENTITY_CODES,
111
137
  ...RECORD_REASON_CODES,
112
138
  ] as const satisfies readonly ReasonCode[];
113
139
 
@@ -123,12 +149,28 @@ export class RecordWriteError extends Error {
123
149
  constructor(
124
150
  readonly code: WriteErrorCode,
125
151
  message: string,
152
+ /** With record-conflict: the record as it is now. */
153
+ readonly conflict?: RecordConflict,
126
154
  ) {
127
155
  super(message);
128
156
  this.name = "RecordWriteError";
129
157
  }
130
158
  }
131
159
 
160
+ /**
161
+ * What a record-conflict refusal says about the record (#3173): the digest
162
+ * the caller expected, the one it has now, and the last chant write of it
163
+ * when the journal knows it. The caller re-reads the record and writes again
164
+ * with `--expect` set to `digest`.
165
+ */
166
+ export interface RecordConflict {
167
+ id: string;
168
+ path: string;
169
+ expected: string;
170
+ digest: string;
171
+ lastWrite: LastWrite | null;
172
+ }
173
+
132
174
  export interface KindView {
133
175
  name: string;
134
176
  schema: string;
@@ -147,6 +189,12 @@ export interface WriteResult {
147
189
  dryRun: boolean;
148
190
  /** The written record's warnings, as `records` would report them. */
149
191
  warnings: RecordWarning[];
192
+ /**
193
+ * The record's digest after the write, as `records --json` reports it
194
+ * (#3173): what the caller passes as `--expect` to its next write of the
195
+ * record.
196
+ */
197
+ digest: string;
150
198
  /** With --dry-run, the whole text the command would write. */
151
199
  text?: string;
152
200
  }
@@ -155,6 +203,8 @@ export interface WriteFailure<C> {
155
203
  $schema: string;
156
204
  contract: number;
157
205
  error: { code: C; message: string };
206
+ /** With record-conflict (#3173): the record as it is now. */
207
+ conflict?: RecordConflict;
158
208
  }
159
209
 
160
210
  /** A record's author seal as `new` and `amend` write it with `--sign` (#2688). */
@@ -335,15 +385,26 @@ export interface Opened {
335
385
  dirRel: string;
336
386
  }
337
387
 
338
- export async function open(kind: string, cwd: string): Promise<Opened> {
388
+ /** A kind `records new` writes as one JSON file named for the SHA-256 of its bytes (ws-053, ws-082). */
389
+ export function isContentAddressed(kind: LoadedRecordKind["kind"]): boolean {
390
+ return kind.format === "json" && kind.idFrom === "sha256";
391
+ }
392
+
393
+ export async function open(kind: string, cwd: string, opts: { contentAddressed?: boolean } = {}): Promise<Opened> {
339
394
  const real = realpathOr(cwd);
340
395
  const root = gitRoot(real) ?? real;
341
396
  const loaded = await loadRecordKind(kind, real);
342
- // The writers write Markdown front matter with an id field. A JSON or
343
- // content-addressed kind (ws-053) is read by records, and written by its own tool.
344
- if (loaded.kind.format !== "markdown-front-matter" || loaded.kind.idField === undefined) {
397
+ // The writers write Markdown front matter with an id field. `records new`
398
+ // also writes a content-addressed JSON kind (ws-082), whose record never
399
+ // changes once written, so amend, review and close never open one. A JSON
400
+ // kind with an id field is read by records, and written by its own tool.
401
+ const writable = (loaded.kind.format === "markdown-front-matter" && loaded.kind.idField !== undefined) || (!!opts.contentAddressed && isContentAddressed(loaded.kind));
402
+ if (!writable) {
345
403
  const what = loaded.kind.format !== "markdown-front-matter" ? `format ${loaded.kind.format}` : `ids from ${loaded.kind.idFrom}`;
346
- throw new RecordWriteError("write-usage-invalid", `the ${loaded.kind.name} kind has ${what}, and records new, amend and review write only Markdown front matter records with an idField`);
404
+ const verbs = isContentAddressed(loaded.kind)
405
+ ? "a content-addressed record never changes once records new writes it, so amend, review and close never write one"
406
+ : "records new, amend and review write only Markdown front matter records with an idField, and records new a JSON record named for the sha256 of its bytes";
407
+ throw new RecordWriteError("write-usage-invalid", `the ${loaded.kind.name} kind has ${what}: ${verbs}`);
347
408
  }
348
409
  const dirRel = relative(root, loaded.dir).split("\\").join("/") || ".";
349
410
  return {
@@ -361,8 +422,24 @@ export async function open(kind: string, cwd: string): Promise<Opened> {
361
422
  * written with `verb`, by the session `agent` names (`CHANT_AGENT`) or the
362
423
  * principal `by` names. Throws a {@link WriteScopeError}.
363
424
  */
364
- export function refuseOutOfScope(o: Opened, verb: WriteVerb, cwd: string, writer: { agent?: string; by?: string }): void {
365
- refuseRecordWrite(cwd, { kindName: o.loaded.kind.name, kindFile: o.loaded.file, recordsDir: o.loaded.dir, verb, agent: writer.agent, principal: writer.by });
425
+ export function refuseOutOfScope(o: Opened, verb: WriteVerb, cwd: string, writer: { agent?: string; by?: string }): ScopeSource {
426
+ const source = refuseRecordWrite(cwd, { kindName: o.loaded.kind.name, kindFile: o.loaded.file, recordsDir: o.loaded.dir, verb, agent: writer.agent, principal: writer.by });
427
+ // #3163: under identity.attribution "identified" at base, --by names a forge identity or a signer.
428
+ if (writer.by !== undefined) refuseUnidentified(source, [writer.by], "--by", { agent: writer.agent });
429
+ return source;
430
+ }
431
+
432
+ /**
433
+ * Refuse author fields (the kind's reviews.decider and proposedBy fields)
434
+ * that name a person by a bare name, under identity.attribution
435
+ * "identified" at base (#3163). `flag` is where the fields came from.
436
+ */
437
+ function refuseUnidentifiedAuthors(source: ScopeSource, kind: Opened["loaded"]["kind"], fields: Record<string, unknown>, flag: string, agent?: string): void {
438
+ const names = [kind.reviews?.decider, kind.proposedBy?.field]
439
+ .filter((f): f is string => f !== undefined)
440
+ .map((f) => fields[f])
441
+ .filter((v): v is string => typeof v === "string" && v.trim() !== "");
442
+ refuseUnidentified(source, names, `${flag} author`, { agent });
366
443
  }
367
444
 
368
445
  /**
@@ -372,11 +449,7 @@ export function refuseOutOfScope(o: Opened, verb: WriteVerb, cwd: string, writer
372
449
  */
373
450
  export async function readAll(o: Opened, source: RecordSource): Promise<RecordEntry[]> {
374
451
  const assets = workingTree(o.workspaceRoot === "." ? o.root : join(o.root, ...o.workspaceRoot.split("/")));
375
- let subjects: { records: RecordEntry[]; reviews: string } | undefined;
376
- if (o.loaded.kind.session) {
377
- const subjectKind = await loadRecordKind(resolve(dirname(o.loaded.file), o.loaded.kind.session.subjects.kind), o.root);
378
- subjects = { records: (await readRecords(subjectKind, { root: o.root, source })).records, reviews: subjectKind.kind.reviews?.field ?? "reviews" };
379
- }
452
+ const subjects = await readSessionSubjects(o.loaded, o.root, source);
380
453
  return (await readRecords(o.loaded, { root: o.root, source, assets, workspaceRoot: o.workspaceRoot, ...(subjects ? { subjects } : {}) })).records;
381
454
  }
382
455
 
@@ -450,6 +523,35 @@ async function refuseRatifyBelowQuorum(o: Opened, written: RecordEntry, id: stri
450
523
  );
451
524
  }
452
525
 
526
+ /**
527
+ * Refuse a write to `target` when the caller expected another digest
528
+ * (#3173): someone wrote the record since the caller read it. Called holding
529
+ * the write lock, after the records are read, so the digest compared is the
530
+ * one the write would build on.
531
+ */
532
+ export function refuseStale(o: Opened, target: RecordEntry, id: string, expect: string | undefined): void {
533
+ if (expect === undefined || expect === target.digest) return;
534
+ const lastWrite = lastWriteReader(o.root)(target.path, o.source.read(target.path));
535
+ const who = lastWrite ? ` It was last written by chant workspace ${lastWrite.verb}${lastWrite.by ? ` for ${lastWrite.by}` : ""}${lastWrite.agent ? ` in agent session ${lastWrite.agent}` : ""} at ${lastWrite.at}.` : "";
536
+ throw new RecordWriteError(
537
+ "record-conflict",
538
+ `${id} has changed since the digest given with --expect (${expect}): its digest is now ${target.digest}.${who} Read it again with chant workspace records --json, and write again with --expect ${target.digest} if the change still stands`,
539
+ { id, path: target.path, expected: expect, digest: target.digest, lastWrite },
540
+ );
541
+ }
542
+
543
+ /** Refuse a --expect that is not a digest, before anything is read. */
544
+ function refuseExpectShape(expect: string | undefined): void {
545
+ if (expect !== undefined && !/^[0-9a-f]{64}$/.test(expect)) {
546
+ throw new RecordWriteError("write-usage-invalid", `--expect takes a record's digest, 64 lowercase hex digits as records --json prints it, not ${JSON.stringify(expect)}`);
547
+ }
548
+ }
549
+
550
+ /** Who a write names, for the lock's owner file and the journal. */
551
+ export function writeWho(verb: string, opts: { by?: string; agent?: string }): WriteLockWho {
552
+ return { verb, by: opts.by ?? null, agent: opts.agent ?? null };
553
+ }
554
+
453
555
  export function findRecord(entries: RecordEntry[], id: string, kind: string): RecordEntry & { data: Record<string, unknown> } {
454
556
  const hits = entries.filter((e) => e.id === id);
455
557
  if (hits.length === 0) throw new RecordWriteError("record-not-found", `no ${kind} record has id ${id}`);
@@ -628,8 +730,9 @@ function today(): string {
628
730
  }
629
731
 
630
732
  export function failure<C>(schema: string, err: unknown): WriteFailure<C> {
631
- if (err instanceof RecordWriteError || err instanceof RecordReadError || err instanceof WriteScopeError) {
632
- return { $schema: schema, contract: RECORDS_WRITE_CONTRACT_VERSION, error: { code: err.code as C, message: err.message } };
733
+ if (err instanceof RecordWriteError || err instanceof RecordReadError || err instanceof WriteScopeError || err instanceof IdentityError || err instanceof WriteLockError) {
734
+ const conflict = err instanceof RecordWriteError ? err.conflict : undefined;
735
+ return { $schema: schema, contract: RECORDS_WRITE_CONTRACT_VERSION, error: { code: err.code as C, message: err.message }, ...(conflict ? { conflict } : {}) };
633
736
  }
634
737
  throw err;
635
738
  }
@@ -766,18 +869,28 @@ function applyChannel(o: Opened, fields: Record<string, unknown>, opts: ChannelO
766
869
  return out;
767
870
  }
768
871
 
769
- /** `records new`: write one new record from validated fields, sealed by its author with `sign`. */
872
+ /** `records new`: write one new record from validated fields, sealed by its author with `sign`. Holds the write lock (#3173). */
770
873
  export async function newRecord(opts: NewRecordOptions & ChannelOptions): Promise<NewDocument> {
874
+ try {
875
+ return await withWriteLock(opts.cwd, writeWho("records new", opts), opts.dryRun, () => newRecordLocked(opts));
876
+ } catch (err) {
877
+ return failure<NewErrorCode>(RECORDS_NEW_SCHEMA_ID, err);
878
+ }
879
+ }
880
+
881
+ async function newRecordLocked(opts: NewRecordOptions & ChannelOptions): Promise<NewDocument> {
771
882
  try {
772
883
  if (opts.prefix !== undefined && !/^[A-Za-z][A-Za-z0-9]*$/.test(opts.prefix)) {
773
884
  throw new RecordWriteError("write-usage-invalid", `--prefix takes letters and digits, starting with a letter, not ${JSON.stringify(opts.prefix)}`);
774
885
  }
775
- const o = await open(opts.kind, opts.cwd);
776
- refuseOutOfScope(o, "new", opts.cwd, opts);
886
+ const o = await open(opts.kind, opts.cwd, { contentAddressed: true });
887
+ const scope = refuseOutOfScope(o, "new", opts.cwd, opts);
777
888
  const fields = applyChannel(o, parseFields(opts.fields, "--from"), opts, true, "--from");
778
889
  const { kind, schema } = o.loaded;
890
+ refuseUnidentifiedAuthors(scope, kind, fields, "--from", opts.agent);
779
891
  refuseSealField(o, fields, "--from");
780
892
  refuseRevisionFields(kind, fields, {}, "--from");
893
+ if (isContentAddressed(kind)) return await newContentAddressed(o, fields, opts);
781
894
  const idField = kind.idField!;
782
895
  const before = await readAll(o, o.source);
783
896
  const given = fields[idField];
@@ -809,7 +922,10 @@ export async function newRecord(opts: NewRecordOptions & ChannelOptions): Promis
809
922
  const written = await validatedEntry(o, before, path, text);
810
923
  await refuseRatifyBelowQuorum(o, written, id);
811
924
  const warnings = written.warnings;
812
- if (!opts.dryRun) writeFileSync(abs(o, path), text, { flag: "wx" });
925
+ if (!opts.dryRun) {
926
+ writeFileSync(abs(o, path), text, { flag: "wx" });
927
+ noteWrites(o.root, [{ path, text }], writeWho("records new", opts));
928
+ }
813
929
  return {
814
930
  $schema: RECORDS_NEW_SCHEMA_ID,
815
931
  contract: RECORDS_WRITE_CONTRACT_VERSION,
@@ -819,6 +935,7 @@ export async function newRecord(opts: NewRecordOptions & ChannelOptions): Promis
819
935
  ...(seal ? { seal } : {}),
820
936
  dryRun: !!opts.dryRun,
821
937
  warnings,
938
+ digest: written.digest,
822
939
  ...(opts.dryRun ? { text } : {}),
823
940
  };
824
941
  } catch (err) {
@@ -826,6 +943,43 @@ export async function newRecord(opts: NewRecordOptions & ChannelOptions): Promis
826
943
  }
827
944
  }
828
945
 
946
+ /**
947
+ * `records new` on a content-addressed JSON kind (ws-053, ws-082), such as
948
+ * the reference workspace's evidence: the fields, in the schema's order, as
949
+ * JSON with two-space indents and a final newline, in a file named for the
950
+ * SHA-256 of those bytes. The id is that hash, so the fields give no id, and
951
+ * the record never changes afterwards.
952
+ */
953
+ async function newContentAddressed(o: Opened, fields: Record<string, unknown>, opts: NewRecordOptions): Promise<NewDocument> {
954
+ const { kind, schema } = o.loaded;
955
+ if (opts.prefix !== undefined) throw new RecordWriteError("write-usage-invalid", `the ${kind.name} kind's ids are the sha256 of each record's bytes, so --prefix allocates nothing`);
956
+ if (opts.sign !== undefined) throw new RecordWriteError("write-usage-invalid", `the ${kind.name} kind names no author to seal: its records are named for the sha256 of their bytes`);
957
+ const data = pick(fields, schemaOrder(fields, schema));
958
+ const text = `${JSON.stringify(data, null, 2)}\n`;
959
+ const id = createHash("sha256").update(text, "utf-8").digest("hex");
960
+ const name = `${id}.json`;
961
+ if (!new RegExp(kind.location.match).test(name)) throw new RecordWriteError("record-path-unmatched", `the kind's location.match ${kind.location.match} does not match ${name}`);
962
+ const path = o.dirRel === "." ? name : `${o.dirRel}/${name}`;
963
+ if (o.source.list(o.dirRel)?.includes(name)) throw new RecordWriteError("record-id-taken", `${path} already exists, with the same bytes: the record is already written`);
964
+ const before = await readAll(o, o.source);
965
+ const written = await validatedEntry(o, before, path, text);
966
+ if (!opts.dryRun) {
967
+ writeFileSync(abs(o, path), text, { flag: "wx" });
968
+ noteWrites(o.root, [{ path, text }], writeWho("records new", opts));
969
+ }
970
+ return {
971
+ $schema: RECORDS_NEW_SCHEMA_ID,
972
+ contract: RECORDS_WRITE_CONTRACT_VERSION,
973
+ kind: o.view,
974
+ path,
975
+ id,
976
+ dryRun: !!opts.dryRun,
977
+ warnings: written.warnings,
978
+ digest: written.digest,
979
+ ...(opts.dryRun ? { text } : {}),
980
+ };
981
+ }
982
+
829
983
  // ── records amend ────────────────────────────────────────────────────────────
830
984
 
831
985
  export interface AmendRecordOptions {
@@ -839,6 +993,8 @@ export interface AmendRecordOptions {
839
993
  sign?: string | true;
840
994
  /** The agent session the write is made in (#2548). */
841
995
  agent?: string;
996
+ /** The record's digest the caller last read (#3173): the write is refused with record-conflict when the record has another. */
997
+ expect?: string;
842
998
  }
843
999
 
844
1000
  /**
@@ -856,14 +1012,25 @@ export interface AmendRecordOptions {
856
1012
  * `--sign` with nothing to change seals the record as it is.
857
1013
  */
858
1014
  export async function amendRecord(opts: AmendRecordOptions & ChannelOptions): Promise<AmendDocument> {
1015
+ try {
1016
+ refuseExpectShape(opts.expect);
1017
+ return await withWriteLock(opts.cwd, writeWho("records amend", opts), opts.dryRun, () => amendRecordLocked(opts));
1018
+ } catch (err) {
1019
+ return failure<AmendErrorCode>(RECORDS_AMEND_SCHEMA_ID, err);
1020
+ }
1021
+ }
1022
+
1023
+ async function amendRecordLocked(opts: AmendRecordOptions & ChannelOptions): Promise<AmendDocument> {
859
1024
  try {
860
1025
  const o = await open(opts.kind, opts.cwd);
861
- refuseOutOfScope(o, "amend", opts.cwd, opts);
1026
+ const scope = refuseOutOfScope(o, "amend", opts.cwd, opts);
862
1027
  const given = applyChannel(o, parseFields(opts.fields, "--set"), opts, false, "--set");
863
1028
  const { kind } = o.loaded;
1029
+ refuseUnidentifiedAuthors(scope, kind, given, "--set", opts.agent);
864
1030
  refuseSealField(o, given, "--set");
865
1031
  const before = await readAll(o, o.source);
866
1032
  const target = findRecord(before, opts.id, kind.name);
1033
+ refuseStale(o, target, opts.id, opts.expect);
867
1034
  const old = target.data;
868
1035
  refuseRevisionFields(kind, given, old, "--set");
869
1036
  const isClosed = target.state !== null && (kind.closedStates ?? []).includes(target.state);
@@ -929,12 +1096,17 @@ export async function amendRecord(opts: AmendRecordOptions & ChannelOptions): Pr
929
1096
  if (stableJson(old[kind.seal.field]) !== stableJson(closedSeal.data[kind.seal.field])) changed.push(kind.seal.field);
930
1097
  }
931
1098
  let warnings = target.warnings;
1099
+ let digest = target.digest;
932
1100
  if (changed.length > 0) {
933
1101
  const written = await validatedEntry(o, before, target.path, text);
934
1102
  if (kind.stateField !== undefined && changed.includes(kind.stateField)) await refuseRatifyBelowQuorum(o, written, opts.id);
935
1103
  warnings = written.warnings;
1104
+ digest = written.digest;
1105
+ }
1106
+ if (!opts.dryRun && changed.length > 0) {
1107
+ writeFileAtomic(abs(o, target.path), text);
1108
+ noteWrites(o.root, [{ path: target.path, text }], writeWho("records amend", opts));
936
1109
  }
937
- if (!opts.dryRun && changed.length > 0) writeFileSync(abs(o, target.path), text);
938
1110
  return {
939
1111
  $schema: RECORDS_AMEND_SCHEMA_ID,
940
1112
  contract: RECORDS_WRITE_CONTRACT_VERSION,
@@ -946,6 +1118,7 @@ export async function amendRecord(opts: AmendRecordOptions & ChannelOptions): Pr
946
1118
  ...(sealDropped ? { sealDropped } : {}),
947
1119
  dryRun: !!opts.dryRun,
948
1120
  warnings,
1121
+ digest,
949
1122
  ...(opts.dryRun ? { text } : {}),
950
1123
  };
951
1124
  } catch (err) {
@@ -975,6 +1148,8 @@ export interface ReviewRecordOptions {
975
1148
  sign?: string | true;
976
1149
  /** The agent session the write is made in (#2548). */
977
1150
  agent?: string;
1151
+ /** The record's digest the reviewer judged (#3173): the verdict is refused with record-conflict when the record has moved on. */
1152
+ expect?: string;
978
1153
  }
979
1154
 
980
1155
  /**
@@ -984,6 +1159,15 @@ export interface ReviewRecordOptions {
984
1159
  * id, the digest, the verdict, the reviewer and the date (`trust/seal.ts`).
985
1160
  */
986
1161
  export async function reviewRecord(opts: ReviewRecordOptions): Promise<ReviewDocument> {
1162
+ try {
1163
+ refuseExpectShape(opts.expect);
1164
+ return await withWriteLock(opts.cwd, writeWho("records review", opts), opts.dryRun, () => reviewRecordLocked(opts));
1165
+ } catch (err) {
1166
+ return failure<ReviewErrorCode>(RECORDS_REVIEW_SCHEMA_ID, err);
1167
+ }
1168
+ }
1169
+
1170
+ async function reviewRecordLocked(opts: ReviewRecordOptions): Promise<ReviewDocument> {
987
1171
  try {
988
1172
  if (!(VERDICTS as readonly string[]).includes(opts.verdict)) {
989
1173
  throw new RecordWriteError("write-usage-invalid", `--verdict takes ${VERDICTS.join(", ")}, not ${JSON.stringify(opts.verdict)}`);
@@ -999,6 +1183,7 @@ export async function reviewRecord(opts: ReviewRecordOptions): Promise<ReviewDoc
999
1183
  const field = kind.reviews.field;
1000
1184
  const before = await readAll(o, o.source);
1001
1185
  const target = findRecord(before, opts.id, kind.name);
1186
+ refuseStale(o, target, opts.id, opts.expect);
1002
1187
  if (target.state !== null && (kind.closedStates ?? []).includes(target.state)) {
1003
1188
  throw new RecordWriteError("record-closed", `${opts.id} is ${target.state}, a closed state, so it takes no more reviews`);
1004
1189
  }
@@ -1025,7 +1210,8 @@ export async function reviewRecord(opts: ReviewRecordOptions): Promise<ReviewDoc
1025
1210
  const list = [...reviews, review];
1026
1211
  const text = replaceFields(current, { [field]: list }, { ...target.data, [field]: list });
1027
1212
  if (text === undefined) throw new RecordWriteError("record-unparseable", `${target.path}: the ${field} block can't be rewritten in place without changing the rest of the file`);
1028
- const warnings = await validateWrite(o, before, target.path, text);
1213
+ const writtenEntry = await validatedEntry(o, before, target.path, text);
1214
+ const warnings = writtenEntry.warnings;
1029
1215
  // The same verdict on the session's own list (#2693), validated with the review in place, so the two lists never differ.
1030
1216
  let session: { path: string; text: string; out: ReviewSession } | undefined;
1031
1217
  if (inSession) {
@@ -1041,8 +1227,9 @@ export async function reviewRecord(opts: ReviewRecordOptions): Promise<ReviewDoc
1041
1227
  session = { path: st.path, text: sText, out: { id: opts.session!, path: st.path, verdict, ...(opts.dryRun ? { text: sText } : {}) } };
1042
1228
  }
1043
1229
  if (!opts.dryRun) {
1044
- writeFileSync(abs(o, target.path), text);
1045
- if (session) writeFileSync(abs(o, session.path), session.text);
1230
+ writeFileAtomic(abs(o, target.path), text);
1231
+ if (session) writeFileAtomic(abs(o, session.path), session.text);
1232
+ noteWrites(o.root, [{ path: target.path, text }, ...(session ? [{ path: session.path, text: session.text }] : [])], writeWho("records review", opts));
1046
1233
  }
1047
1234
  return {
1048
1235
  $schema: RECORDS_REVIEW_SCHEMA_ID,
@@ -1054,6 +1241,7 @@ export async function reviewRecord(opts: ReviewRecordOptions): Promise<ReviewDoc
1054
1241
  ...(session ? { session: session.out } : {}),
1055
1242
  dryRun: !!opts.dryRun,
1056
1243
  warnings,
1244
+ digest: writtenEntry.digest,
1057
1245
  ...(opts.dryRun ? { text } : {}),
1058
1246
  };
1059
1247
  } catch (err) {
@@ -1108,9 +1296,9 @@ async function seal(sign: string | true, cwd: string, v: { record: string; diges
1108
1296
 
1109
1297
  export const WRITE_USAGE = [
1110
1298
  "chant workspace records new [<kind file or declared kind>] --from <file|-> [--prefix <prefix>] [--by <name>] [--sign [<key file>]] [--dry-run]",
1111
- "chant workspace records amend <id> [--kind <kind file>] --set <file|-> [--sign [<key file>]] [--dry-run]",
1112
- "chant workspace records review <id> [--kind <kind file>] --verdict agree|dissent|abstain --by <principal> [--note <text>] [--session <id>] [--sign [<key file>]] [--dry-run]",
1113
- "chant workspace records close <session id> [--kind <session kind file>] [--dry-run]",
1299
+ "chant workspace records amend <id> [--kind <kind file>] --set <file|-> [--expect <digest>] [--sign [<key file>]] [--dry-run]",
1300
+ "chant workspace records review <id> [--kind <kind file>] --verdict agree|dissent|abstain --by <principal> [--note <text>] [--session <id>] [--expect <digest>] [--sign [<key file>]] [--dry-run]",
1301
+ "chant workspace records close <session id> [--kind <session kind file>] [--expect <digest>] [--dry-run]",
1114
1302
  ].join("\n");
1115
1303
 
1116
1304
  function usage(schema: string, message: string): WriteFailure<"write-usage-invalid"> {
@@ -1204,12 +1392,13 @@ export async function runRecordsWrite(ctx: CommandContext): Promise<number> {
1204
1392
  if (!id) return print(usage(RECORDS_CLOSE_SCHEMA_ID, "close needs the session's id"));
1205
1393
  const kind = args.kind !== undefined ? resolveWriteKind(args.kind, cwd) : await declaredSessionKind(RECORDS_CLOSE_SCHEMA_ID, cwd);
1206
1394
  if (typeof kind !== "string") return print(kind);
1207
- return print(await closeRecord({ kind, id, dryRun: args.dryRun, cwd, agent }));
1395
+ return print(await closeRecord({ kind, id, dryRun: args.dryRun, cwd, agent, expect: args.expect }));
1208
1396
  }
1209
1397
  if (verb === "new") {
1210
1398
  const named = args.extraPositional2 ?? args.kind;
1211
1399
  const kind = named !== undefined ? resolveWriteKind(named, cwd) : declaredWriteKind(RECORDS_NEW_SCHEMA_ID, cwd, "new needs the kind file");
1212
1400
  if (typeof kind !== "string") return print(kind);
1401
+ if (args.expect !== undefined) return print(usage(RECORDS_NEW_SCHEMA_ID, "--expect is not taken by new: a new record has no digest yet, and its id is allocated under the write lock"));
1213
1402
  const input = readInput(RECORDS_NEW_SCHEMA_ID, "--from", args.migrateFrom, cwd);
1214
1403
  if (typeof input !== "string") return print(input);
1215
1404
  return print(await newRecord({ kind, fields: input, prefix: args.prefix, by: args.by, sign: args.sign, dryRun: args.dryRun, cwd, agent }));
@@ -1223,11 +1412,11 @@ export async function runRecordsWrite(ctx: CommandContext): Promise<number> {
1223
1412
  if (args.by !== undefined) return print(usage(schema, "--by is not taken by amend: set decided_by, or a kind's proposedBy field, with --set"));
1224
1413
  const input = readInput(schema, "--set", args.set, cwd);
1225
1414
  if (typeof input !== "string") return print(input);
1226
- return print(await amendRecord({ kind, id, fields: input, sign: args.sign, dryRun: args.dryRun, cwd, agent }));
1415
+ return print(await amendRecord({ kind, id, fields: input, sign: args.sign, dryRun: args.dryRun, cwd, agent, expect: args.expect }));
1227
1416
  }
1228
1417
  if (args.verdict === undefined) return print(usage(schema, "--verdict agree|dissent|abstain is required"));
1229
1418
  if (args.by === undefined) return print(usage(schema, "--by <principal> is required"));
1230
1419
  return print(
1231
- await reviewRecord({ kind, id, verdict: args.verdict, by: args.by, note: args.note, session: args.session, sign: args.sign, dryRun: args.dryRun, cwd, agent }),
1420
+ await reviewRecord({ kind, id, verdict: args.verdict, by: args.by, note: args.note, session: args.session, sign: args.sign, dryRun: args.dryRun, cwd, agent, expect: args.expect }),
1232
1421
  );
1233
1422
  }
@@ -124,6 +124,14 @@
124
124
  },
125
125
  "error": false,
126
126
  "current": { "type": "boolean", "description": "Whether superseded records were left out (--current)." },
127
+ "uncommitted": {
128
+ "type": "boolean",
129
+ "description": "Added in contract 1 by #3160. Whether only the records HEAD doesn't hold were listed (--uncommitted): each record's worktree is then modified or new, checkout is present, and summary's total, valid and invalid count the records listed. Absent from documents written before #3160, which read as false."
130
+ },
131
+ "checkout": {
132
+ "description": "Added in contract 1 by #3160, on a read of the working tree in a git repository; absent under --at and outside git. The branch, head and base the working tree is on, and the records of the kind it has deleted.",
133
+ "$ref": "#/$defs/checkout"
134
+ },
127
135
  "trust": { "$ref": "#/$defs/trust" },
128
136
  "records": {
129
137
  "description": "One entry per file the kind locates, sorted by path.",
@@ -145,6 +153,42 @@
145
153
  "type": "array",
146
154
  "items": { "$ref": "#/$defs/decisionWork" }
147
155
  }
156
+ },
157
+ "allOf": [
158
+ {
159
+ "if": { "properties": { "uncommitted": { "const": true } }, "required": ["uncommitted"] },
160
+ "then": {
161
+ "required": ["checkout"],
162
+ "properties": {
163
+ "checkout": true,
164
+ "at": { "type": "null" },
165
+ "records": { "type": "array", "items": { "type": "object", "required": ["worktree"], "properties": { "worktree": { "enum": ["modified", "new"] } } } }
166
+ }
167
+ }
168
+ }
169
+ ]
170
+ },
171
+ "checkout": {
172
+ "type": "object",
173
+ "required": ["branch", "head", "base", "baseFrom", "deleted"],
174
+ "properties": {
175
+ "branch": { "type": ["string", "null"], "description": "The checked-out branch's short name, such as chant/work/w-12, or null when HEAD is detached." },
176
+ "head": {
177
+ "type": ["string", "null"],
178
+ "pattern": "^[0-9a-f]{40,64}$",
179
+ "description": "The full commit id HEAD names, or null before the first commit (every record is then new). Each record's worktree is judged against it."
180
+ },
181
+ "base": {
182
+ "type": ["string", "null"],
183
+ "pattern": "^[0-9a-f]{40,64}$",
184
+ "description": "The merge base of head and the target branch tip, the commit the branch forked from: what applying the branch would be measured against. The target is --base when given, else origin/HEAD, main or master, as for trust. Null without a head, a target, or common history."
185
+ },
186
+ "baseFrom": { "enum": ["flag", "origin/HEAD", "main", "master", null], "description": "Where the target branch was found; null when there is none." },
187
+ "deleted": {
188
+ "type": "array",
189
+ "items": { "type": "string" },
190
+ "description": "Record files of the kind that head holds and the working tree no longer has, staged or not, from the repository root with / separators, sorted. A renamed record is here under its old name and in records as new."
191
+ }
148
192
  }
149
193
  },
150
194
  "workLink": {
@@ -155,6 +199,17 @@
155
199
  "state": { "type": ["string", "null"], "description": "The linked record's state as it writes it, or null when no record has the id." }
156
200
  }
157
201
  },
202
+ "workAnswer": {
203
+ "type": "object",
204
+ "required": ["id", "point", "state", "answer", "answeredBy"],
205
+ "properties": {
206
+ "id": { "type": "string", "description": "The answer record's id: the point's name and the first 12 hex digits of its inputs hash." },
207
+ "point": { "type": ["string", "null"], "description": "The decision point it answers." },
208
+ "state": { "type": ["string", "null"], "description": "escalated, proposed or answered." },
209
+ "answer": { "oneOf": [{ "type": "string" }, { "type": "boolean" }, { "type": "null" }], "description": "The answer, or null while there is none." },
210
+ "answeredBy": { "type": "array", "items": { "type": "string" }, "description": "The people who answered or confirmed it; empty when a table or a model answered." }
211
+ }
212
+ },
158
213
  "decisionWork": {
159
214
  "type": "object",
160
215
  "required": ["id", "path", "state", "supersededBy", "implementedBy"],
@@ -199,7 +254,7 @@
199
254
  "items": { "$ref": "#/$defs/asset" }
200
255
  },
201
256
  "warnings": {
202
- "description": "Added in contract 1 by #2549. Findings that leave the record valid: a pinned file that changed, went missing or did not follow a superseding decision, a supersedes link that has no effect yet, (added by #2654) an evidence list that is empty, (added by #2672) a verdict that names no digest, or (added by #2683) a work record's links and dates, or (added by #2688) a record whose author seal is not attested under a signers file at base, or (added by #2772) a work record's acceptance criteria unmet when it is done, or met by a manual verdict from its implementer. A work record never carries record-no-evidence; a done one with no evidence carries work-done-unpinned. valid and --current do not look at them.",
257
+ "description": "Added in contract 1 by #2549. Findings that leave the record valid: a pinned file that changed, went missing or did not follow a superseding decision, a supersedes link that has no effect yet, (added by #2654) an evidence list that is empty, (added by #2672) a verdict that names no digest, or (added by #2683) a work record's links and dates, or (added by #2688) a record whose author seal is not attested under a signers file at base, or (added by #2772) a work record's acceptance criteria unmet when it is done, or met by a manual verdict from its implementer, or (added by #3147) a work record's contract that no record has or nobody approved, or a builder tier its kind does not list. A work record never carries record-no-evidence; a done one with no evidence carries work-done-unpinned. valid and --current do not look at them.",
203
258
  "type": "array",
204
259
  "items": { "$ref": "#/$defs/warning" }
205
260
  },
@@ -243,6 +298,35 @@
243
298
  "description": "Added in contract 1 by #2772, on every parsed record of a work kind whose work block names acceptance criteria. How many of the record's criteria passing evidence meets: evidence naming the criterion, with result pass and the criterion's verification, and for a manual criterion a by other than the implementer. Null when the record lists no criteria, so a record without them reads as before.",
244
299
  "oneOf": [{ "type": "null" }, { "$ref": "#/$defs/workAcceptance" }]
245
300
  },
301
+ "worktree": {
302
+ "enum": ["committed", "modified", "new"],
303
+ "description": "Added in contract 1 by #3160, on every record of a read of the working tree in a git repository; absent under --at and outside git. committed when the file is as HEAD holds it, modified when it differs from HEAD, new when HEAD doesn't hold it, staged or not. modified and new are the records a person has kept and not yet committed or applied."
304
+ },
305
+ "lastWrite": {
306
+ "description": "Added in contract 1 by #3173, beside worktree. The last chant write of the record's file in this working tree (records new, amend, review or close, points ask or answer), from the write journal in the git directory, while the file still holds the text that write left; null when the journal doesn't know it, such as after a commit was checked out or the file was edited by hand. A UI shows it with digest, so a person sees who changed a record they are looking at. It is a cache, never a fact: the journal can be lost.",
307
+ "oneOf": [
308
+ { "type": "null" },
309
+ {
310
+ "type": "object",
311
+ "required": ["verb", "by", "agent", "at"],
312
+ "properties": {
313
+ "verb": { "type": "string", "description": "The write command, such as records amend or points answer." },
314
+ "by": { "type": ["string", "null"], "description": "The principal the write named (--by, or the people who answered), or null when it named none." },
315
+ "agent": { "type": ["string", "null"], "description": "The agent session the write was made in (CHANT_AGENT), or null." },
316
+ "at": { "type": "string", "description": "When, as an ISO 8601 time." }
317
+ }
318
+ }
319
+ ]
320
+ },
321
+ "contract": {
322
+ "description": "Added in contract 1 by #3147, on every parsed record of a work kind whose work block names a contract kind. The contract record the item builds, by the id in the work block's contract field, with that record's state now; state is null when no record of the contract kind has the id. Null when the item names no contract.",
323
+ "oneOf": [{ "type": "null" }, { "$ref": "#/$defs/workLink" }]
324
+ },
325
+ "answers": {
326
+ "description": "Added in contract 1 by #3147, on every record with an id of a work kind whose work block names an answer kind. The decision-point answers about the item: each answer record whose constrains names the item's id, in path order, joined by id and never copied onto the item. Read at the same revision; in the working tree, with the answers a steward keeps on the lifecycle ledger laid over it. Absent when the answer kind can't be read.",
327
+ "type": "array",
328
+ "items": { "$ref": "#/$defs/workAnswer" }
329
+ },
246
330
  "lease": {
247
331
  "description": "Added in contract 1 by #2732, on every record of a work kind with an id, read in the working tree (absent under --at). The item's active work lease (chant workspace work claim), or null when nobody holds one live. Read from the local lease refs, refs/chant/lease/[_members/<member>/]work/<id> and the remote's as last fetched, of the ledger owning the work kind file; records never fetches.",
248
332
  "oneOf": [{ "type": "null" }, { "$ref": "#/$defs/workLease" }]
@@ -319,6 +403,9 @@
319
403
  "work-done-gap-open",
320
404
  "work-acceptance-unmet",
321
405
  "work-acceptance-self-verified",
406
+ "work-contract-unknown",
407
+ "work-contract-undecided",
408
+ "work-tier-unknown",
322
409
  "answer-points-unreadable",
323
410
  "answer-point-unknown",
324
411
  "answer-point-changed",