@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
@@ -2,9 +2,10 @@
2
2
  * Answers to decision points as records (ws-058, #2739).
3
3
  *
4
4
  * {@link askPoint} asks a point's deciders for one set of inputs and records
5
- * the answer in the answer kind's records directory, and {@link answerPoint}
6
- * records people's answer to an open question. Both write one Markdown record
7
- * or none, never commit, and go through the checks every records write goes
5
+ * the answer in the answer kind's records directory, {@link answerPoint}
6
+ * records people's answer to an open question, with an optional note, and
7
+ * {@link retractAnswer} takes an answer back (#3351). Each writes one Markdown
8
+ * record or none, never commits, and goes through the checks every records write goes
8
9
  * through (`records-write.ts`): the record is read back with the kind's schema
9
10
  * before it is written, and no other record may become invalid.
10
11
  *
@@ -21,12 +22,28 @@
21
22
  * escalated one is asked again, since a backend may answer now, and rewritten
22
23
  * only when the chain no longer escalates.
23
24
  *
25
+ * An ad-hoc point (#3403) has no candidates of its own: each ask gives the
26
+ * question's text and candidates, the record keeps them as `asked`, and they
27
+ * are part of the inputs hash, so a question asked with other options is
28
+ * another question. People's answer is checked against them.
29
+ *
30
+ * An answer never changes in place. People may retract it (ws-084): the
31
+ * question is escalated to them again, and the answer, who gave it, when, and
32
+ * its note move into the record's `retractions` with who retracted it, when
33
+ * and why. Asking a retracted question again returns it as it stands, since
34
+ * people took it back from the deciders; people answer it again.
35
+ *
36
+ * Each write holds the working tree's write lock (#3173, ws-089) from the first read
37
+ * to the write, so two answers to one question are never both taken from the
38
+ * same reading of it.
39
+ *
24
40
  * chant never calls a model (ws-052): the model call is the caller's
25
41
  * {@link ModelAsk}, such as the decide Op activity (#2740), or a backend's
26
42
  * response the caller already has (`points ask --response`).
27
43
  */
28
44
 
29
45
  import { writeFileSync } from "node:fs";
46
+ import { noteWrites, withWriteLock, WriteLockError, WRITE_LOCK_CODES, type WriteLockWho } from "./write-lock";
30
47
  import type { ReasonCode } from "./reason-codes";
31
48
  import { gitRoot } from "./record-source";
32
49
  import { loadRecordKind, normalisePrincipal, RECORD_REASON_CODES, RecordReadError, type RecordEntry, type RecordWarning } from "./records";
@@ -47,15 +64,19 @@ import {
47
64
  } from "./records-write";
48
65
  import {
49
66
  answerId,
67
+ askedOf,
68
+ askedQuestion,
50
69
  candidates,
51
70
  DeciderFailed,
52
71
  inputsHash,
72
+ parseAsked,
53
73
  pointOf,
54
74
  pointsFileOf,
55
75
  pointVersion,
56
76
  quorumOf,
57
77
  readPointsThrough,
58
78
  runChain,
79
+ type Asked,
59
80
  type ChainResult,
60
81
  type Escalation,
61
82
  type ModelAsk,
@@ -68,14 +89,16 @@ import { AGENT_ROLE } from "./records-cli";
68
89
  import type { SourceVia } from "./source-block";
69
90
  import type { RecordSource } from "./record-source";
70
91
  import { currentStewardTurn } from "../op/steward-turn";
92
+ import { IdentityError, refuseUnidentified } from "./identity";
93
+ import { scopeSource } from "./write-scope";
71
94
  import { RefCASConflictError } from "../lifecycle/git";
72
95
  import { readLedgerAnswers, refreshLedger, withLedgerAnswers, writeLedgerAnswer, type LedgerAnswers } from "./answers-ledger";
73
96
 
74
- /** The version of the write documents `points ask` and `points answer` print. */
97
+ /** The version of the write documents `points ask`, `points answer` and `points retract` print. */
75
98
  export const POINTS_WRITE_CONTRACT_VERSION = 1;
76
99
  export const POINTS_WRITE_SCHEMA_ID = "https://intentius.io/chant/schemas/workspace/points-write/v1/points-write.schema.json";
77
100
 
78
- /** Why `points ask` or `points answer` wrote nothing. Closed: a reader may switch on it. */
101
+ /** Why `points ask`, `points answer` or `points retract` wrote nothing. Closed: a reader may switch on it. */
79
102
  export const POINTS_WRITE_ERROR_CODES = [
80
103
  ...LOAD_ERROR_CODES,
81
104
  "write-usage-invalid",
@@ -99,6 +122,15 @@ export const POINTS_WRITE_ERROR_CODES = [
99
122
  "record-id-taken",
100
123
  /** The answer was given during a steward's turn (#2749): a steward never answers a question, its own or another's. */
101
124
  "answer-in-steward-turn",
125
+ /** An answerer is named by a bare name, and identity.attribution at base is identified (#3163). */
126
+ "principal-unidentified",
127
+ /** `points retract` names a question that has no answer: escalated or proposed (#3351). */
128
+ "answer-not-answered",
129
+ /** The answer kind's schema copy has no `note`, `retractions`, `relayed_by` or `asked` field for what the write was given (#3351, #3402, #3403). */
130
+ "answer-field-unsupported",
131
+ /** An ad-hoc point was asked without its question and candidates, a declared point with them, or they don't fit the point's question type (#3403). */
132
+ "point-candidates-invalid",
133
+ ...WRITE_LOCK_CODES,
102
134
  ...RECORD_REASON_CODES,
103
135
  ] as const satisfies readonly ReasonCode[];
104
136
  export type PointsWriteErrorCode = (typeof POINTS_WRITE_ERROR_CODES)[number];
@@ -137,16 +169,34 @@ export interface QuestionView {
137
169
  probabilities: Record<string, number> | null;
138
170
  confidence: number | null;
139
171
  threshold: number | null;
172
+ /**
173
+ * The model's explanation of the recorded answer (#3345): a proposal's, or a
174
+ * confirmed proposal's. null when a model gave none, or did not decide.
175
+ */
176
+ reason: string | null;
140
177
  /**
141
178
  * Any model answer: the proposal for a proposed question, or for an
142
179
  * escalated one the last model answer below its threshold. null when no
143
- * model answered.
180
+ * model answered. `reason` is the model's explanation of that answer, or
181
+ * null when it gave none (#3345).
144
182
  */
145
- model: { answer: string | boolean | null; confidence: number | null; threshold: number | null; model: string | null; backend: string | null; observed: boolean } | null;
183
+ model: { answer: string | boolean | null; confidence: number | null; threshold: number | null; model: string | null; backend: string | null; observed: boolean; reason: string | null } | null;
146
184
  escalations: Escalation[];
147
185
  answeredBy: string[];
148
186
  askedOn: string | null;
149
187
  answeredOn: string | null;
188
+ /** What the people who answered wrote with their answer (`points answer --note`, #3351), or null. */
189
+ note: string | null;
190
+ /** Who relayed the answer to chant for the people who gave it (`points answer --relayed-by`, #3402), or null. */
191
+ relayedBy: string | null;
192
+ /** Answers people took back, oldest first (`points retract`, #3351). Empty when none was. */
193
+ retractions: RetractionView[];
194
+ /**
195
+ * An ad-hoc point's question as its ask gave it (#3403): the question's text
196
+ * and its criteria, what each candidate means. null for a declared point's
197
+ * question, whose text and criteria are the point's.
198
+ */
199
+ asked: Asked | null;
150
200
  /**
151
201
  * The steward whose turn asked the question (#2749), and the run it was in,
152
202
  * or null when no steward asked it. The steward waits on the question and
@@ -165,6 +215,43 @@ export interface QuestionView {
165
215
  warnings: RecordWarning[];
166
216
  }
167
217
 
218
+ /** One retracted answer, as a {@link QuestionView} lists it: the answer as it was, and who took it back, when and why. */
219
+ export interface RetractionView {
220
+ answer: string | boolean | null;
221
+ decider: Record<string, unknown>;
222
+ answeredBy: string[];
223
+ answeredOn: string | null;
224
+ /** The note the answer carried, or null. */
225
+ answerNote: string | null;
226
+ /** Who relayed the answer (#3402), or null. */
227
+ answerRelayedBy: string | null;
228
+ /** Who retracted it. */
229
+ by: string[];
230
+ on: string | null;
231
+ /** Why it was retracted (`points retract --note`), or null. */
232
+ note: string | null;
233
+ }
234
+
235
+ /** The record's `retractions`, as views. */
236
+ function retractionsOf(d: Record<string, unknown>): RetractionView[] {
237
+ if (!Array.isArray(d.retractions)) return [];
238
+ const str = (v: unknown): string | null => (typeof v === "string" ? v : null);
239
+ const strs = (v: unknown): string[] => (Array.isArray(v) ? v.filter((x): x is string => typeof x === "string") : []);
240
+ return d.retractions
241
+ .filter((r): r is Record<string, unknown> => r !== null && typeof r === "object" && !Array.isArray(r))
242
+ .map((r) => ({
243
+ answer: typeof r.answer === "string" || typeof r.answer === "boolean" ? r.answer : null,
244
+ decider: r.decider !== null && typeof r.decider === "object" && !Array.isArray(r.decider) ? (r.decider as Record<string, unknown>) : {},
245
+ answeredBy: strs(r.answered_by),
246
+ answeredOn: str(r.answered_on),
247
+ answerNote: str(r.answer_note),
248
+ answerRelayedBy: str(r.answer_relayed_by),
249
+ by: strs(r.by),
250
+ on: str(r.on),
251
+ note: str(r.note),
252
+ }));
253
+ }
254
+
168
255
  /** The harness a steward's question names in its source block (#2749). */
169
256
  export const STEWARD_HARNESS = "chant-steward";
170
257
 
@@ -198,10 +285,20 @@ export function questionView(entry: RecordEntry, point: Point | undefined, ledge
198
285
  const answer = typeof d.answer === "string" || typeof d.answer === "boolean" ? d.answer : null;
199
286
  let model: QuestionView["model"] = null;
200
287
  if (decider.kind === "model") {
201
- model = { answer, confidence: num(d.confidence), threshold: num(d.threshold), model: str(decider.model), backend: str(decider.backend), observed: true };
288
+ model = { answer, confidence: num(d.confidence), threshold: num(d.threshold), model: str(decider.model), backend: str(decider.backend), observed: true, reason: str(d.reason) };
202
289
  } else {
203
290
  const lean = [...escalations].reverse().find((e) => e.kind === "model" && e.answer !== undefined && e.answer !== null);
204
- if (lean) model = { answer: lean.answer ?? null, confidence: lean.confidence ?? null, threshold: lean.threshold ?? null, model: lean.model ?? null, backend: lean.backend ?? null, observed: false };
291
+ if (lean) {
292
+ model = {
293
+ answer: lean.answer ?? null,
294
+ confidence: lean.confidence ?? null,
295
+ threshold: lean.threshold ?? null,
296
+ model: lean.model ?? null,
297
+ backend: lean.backend ?? null,
298
+ observed: false,
299
+ reason: typeof lean.model_reason === "string" ? lean.model_reason : null,
300
+ };
301
+ }
205
302
  }
206
303
  const constrains = Array.isArray(d.constrains) ? d.constrains.filter((c): c is string => typeof c === "string") : [];
207
304
  return {
@@ -223,11 +320,16 @@ export function questionView(entry: RecordEntry, point: Point | undefined, ledge
223
320
  probabilities: d.probabilities !== null && typeof d.probabilities === "object" ? (d.probabilities as Record<string, number>) : null,
224
321
  confidence: num(d.confidence),
225
322
  threshold: num(d.threshold),
323
+ reason: decider.kind === "model" ? str(d.reason) : null,
226
324
  model,
227
325
  escalations,
228
326
  answeredBy: Array.isArray(d.answered_by) ? d.answered_by.filter((b): b is string => typeof b === "string") : [],
229
327
  askedOn: str(d.asked_on),
230
328
  answeredOn: str(d.answered_on),
329
+ note: str(d.note),
330
+ relayedBy: str(d.relayed_by),
331
+ retractions: retractionsOf(d),
332
+ asked: askedOf(d) ?? null,
231
333
  askedBy: askedByOf(d),
232
334
  ledger,
233
335
  valid: entry.valid,
@@ -235,12 +337,15 @@ export function questionView(entry: RecordEntry, point: Point | undefined, ledge
235
337
  };
236
338
  }
237
339
 
238
- /** What `points ask` and `points answer` print. */
340
+ /** The write verbs of `points`. */
341
+ export type PointsWriteVerb = "ask" | "answer" | "retract";
342
+
343
+ /** What `points ask`, `points answer` and `points retract` print. */
239
344
  export type PointsWriteDocument =
240
345
  | {
241
346
  $schema: string;
242
347
  contract: number;
243
- verb: "ask" | "answer";
348
+ verb: PointsWriteVerb;
244
349
  kind: KindView;
245
350
  id: string;
246
351
  path: string;
@@ -253,10 +358,10 @@ export type PointsWriteDocument =
253
358
  /** With --dry-run, the text the command would write. */
254
359
  text?: string;
255
360
  }
256
- | { $schema: string; contract: number; verb: "ask" | "answer"; error: { code: PointsWriteErrorCode; message: string } };
361
+ | { $schema: string; contract: number; verb: PointsWriteVerb; error: { code: PointsWriteErrorCode; message: string } };
257
362
 
258
- function failure(verb: "ask" | "answer", err: unknown): PointsWriteDocument {
259
- if (err instanceof PointsWriteError || err instanceof RecordWriteError || err instanceof RecordReadError) {
363
+ function failure(verb: PointsWriteVerb, err: unknown): PointsWriteDocument {
364
+ if (err instanceof PointsWriteError || err instanceof RecordWriteError || err instanceof RecordReadError || err instanceof IdentityError || err instanceof WriteLockError) {
260
365
  return { $schema: POINTS_WRITE_SCHEMA_ID, contract: POINTS_WRITE_CONTRACT_VERSION, verb, error: { code: err.code as PointsWriteErrorCode, message: err.message } };
261
366
  }
262
367
  if (err instanceof WorkspaceReadError) {
@@ -309,6 +414,12 @@ export interface AskPointOptions {
309
414
  point: string;
310
415
  /** The inputs, keyed by the point's declared input names. */
311
416
  inputs: unknown;
417
+ /**
418
+ * An ad-hoc point's question and candidates (#3403), as `{ question,
419
+ * criteria }` in the point's question type's shape. Required for an ad-hoc
420
+ * point and refused for a declared one.
421
+ */
422
+ candidates?: unknown;
312
423
  /** What the question is about, such as a work item's id: written to `constrains`. */
313
424
  subject?: string;
314
425
  /** The answer kind file, or a declared kind's name. Without it, the declared answer kind whose points file declares the point. */
@@ -342,14 +453,15 @@ export interface AskPointOptions {
342
453
  const today = (): string => new Date().toISOString().slice(0, 10);
343
454
  const show = (answer: string | boolean, type: string): string => (type === "noul" ? (answer ? "yes" : "no") : String(answer));
344
455
 
345
- function titleFor(point: Point, subject: string | undefined, state: string, answer: string | boolean | undefined): string {
456
+ /** A record's title: the point's title, or an ad-hoc question's own text (#3403), what it is about, and where it stands. */
457
+ function titleFor(point: Point, subject: string | undefined, state: string, answer: string | boolean | undefined, asked?: Asked): string {
346
458
  const outcome = state === "escalated" ? "open for people" : state === "proposed" ? `${show(answer!, point.question.type)}, proposed` : show(answer!, point.question.type);
347
- return `${point.title}${subject ? ` (${subject})` : ""}: ${outcome}`;
459
+ return `${asked ? asked.question : point.title}${subject ? ` (${subject})` : ""}: ${outcome}`;
348
460
  }
349
461
 
350
- function body(point: Point, title: string): string {
351
- const q = point.question;
352
- const describe = (c: string | boolean): string => (q.type === "score" ? "" : ((q.criteria as Record<string, string>)[String(c)] ?? ""));
462
+ function body(point: Point, title: string, asked?: Asked): string {
463
+ const q = askedQuestion(point, asked);
464
+ const describe = (c: string | boolean): string => (q.type === "score" ? "" : ((q.criteria as Record<string, string> | undefined)?.[String(c)] ?? ""));
353
465
  const options = candidates(q).map((c) => `- ${show(c, q.type)}${describe(c) ? `: ${describe(c)}` : ""}`);
354
466
  return ["", `# ${title}`, "", q.instructions.trim(), "", ...options, ""].join("\n");
355
467
  }
@@ -367,6 +479,26 @@ async function findPoint(kinds: string[], name: string, cwd: string): Promise<Op
367
479
  throw new PointsWriteError("point-unknown", `no points file declares ${JSON.stringify(name)}${declared.length ? ` (the points are ${declared.join(", ")})` : ""}`);
368
480
  }
369
481
 
482
+ /**
483
+ * An ad-hoc point's question and candidates from the ask (#3403), or
484
+ * undefined for a declared point. Refused when an ad-hoc point has none, a
485
+ * declared point is given them, they don't fit the question type, or the
486
+ * answer kind's schema copy has no `asked` field to keep them in.
487
+ */
488
+ function checkAsked(name: string, point: Point, given: unknown, loaded: { kind: { name: string }; schema: Record<string, unknown> }): Asked | undefined {
489
+ if (!point.adhoc) {
490
+ if (given !== undefined) throw new PointsWriteError("point-candidates-invalid", `${name} declares its candidates, so the ask gives none: --candidates is for an ad-hoc point`);
491
+ return undefined;
492
+ }
493
+ if (given === undefined) throw new PointsWriteError("point-candidates-invalid", `${name} is an ad-hoc point, so each ask gives the question and its candidates: --candidates <file|-|json>`);
494
+ const parsed = parseAsked(point, given);
495
+ if ("problems" in parsed) throw new PointsWriteError("point-candidates-invalid", `the candidates for ${name}, a ${point.question.type} question: ${parsed.problems.join("; ")}`);
496
+ if (!answerFields(loaded.schema).asked) {
497
+ throw new PointsWriteError("answer-field-unsupported", `the ${loaded.kind.name} kind's schema has no asked field, so an ad-hoc question's text and candidates can't be kept: copy point-answer.schema.json from @intentius/chant anew`);
498
+ }
499
+ return parsed.asked;
500
+ }
501
+
370
502
  function checkInputs(name: string, point: Point, inputs: unknown): Record<string, unknown> {
371
503
  if (inputs === null || typeof inputs !== "object" || Array.isArray(inputs)) throw new PointsWriteError("point-inputs-invalid", `the inputs to ${name} must be a JSON object`);
372
504
  const declared = Object.keys(point.inputs);
@@ -385,6 +517,8 @@ interface WriteTarget {
385
517
  /** Write to the ledger rather than the tree. */
386
518
  onLedger: boolean;
387
519
  message: string;
520
+ /** Who writes, for the write journal (#3173). */
521
+ who: WriteLockWho;
388
522
  }
389
523
 
390
524
  /**
@@ -408,6 +542,7 @@ async function write(o: Opened, before: RecordEntry[], path: string, text: strin
408
542
  if (!dryRun) {
409
543
  try {
410
544
  writeFileSync(abs(o, path), text, create ? { flag: "wx" } : undefined);
545
+ noteWrites(o.root, [{ path, text }], target.who);
411
546
  } catch (err) {
412
547
  if ((err as NodeJS.ErrnoException).code === "EEXIST") throw new PointsWriteError("record-id-taken", `${path} was written by another ask at the same time: ask again to read it`);
413
548
  throw err;
@@ -429,10 +564,28 @@ function recordData(o: Opened, fields: Record<string, unknown>): Record<string,
429
564
  return pick(clean, schemaOrder(clean, { properties: o.loaded.schema.properties }));
430
565
  }
431
566
 
432
- function resultFields(result: ChainResult): Record<string, unknown> {
433
- const escalations = result.escalations.length > 0 ? result.escalations : undefined;
567
+ /**
568
+ * Whether an answer kind's schema copy takes the model's reason (#3345): at
569
+ * the top level, and in an escalation entry as `model_reason`. A workspace
570
+ * whose copy of point-answer.schema.json predates them keeps no reason rather
571
+ * than failing the write; copying the schema anew turns them on.
572
+ */
573
+ export function reasonFields(schema: Record<string, unknown>): { top: boolean; escalation: boolean } {
574
+ const has = (o: unknown, key: string): boolean => o !== null && typeof o === "object" && Object.prototype.hasOwnProperty.call(o, key);
575
+ const defs = (schema.definitions ?? schema.$defs) as Record<string, { properties?: unknown }> | undefined;
576
+ return { top: has(schema.properties, "reason"), escalation: has(defs?.escalation?.properties, "model_reason") };
577
+ }
578
+
579
+ /** Escalations without `model_reason` when the kind's schema does not take it. */
580
+ function keepReasons(escalations: Escalation[], takes: { escalation: boolean }): Escalation[] {
581
+ return takes.escalation ? escalations : escalations.map(({ model_reason: _, ...e }) => e);
582
+ }
583
+
584
+ function resultFields(result: ChainResult, takes: { top: boolean; escalation: boolean }): Record<string, unknown> {
585
+ const escalations = result.escalations.length > 0 ? keepReasons(result.escalations, takes) : undefined;
434
586
  if (result.status === "proposed") {
435
- return { state: "proposed", answer: result.answer, decider: result.decider, probabilities: result.probabilities, confidence: result.confidence, threshold: result.threshold, escalations };
587
+ const reason = takes.top ? result.reason : undefined;
588
+ return { state: "proposed", answer: result.answer, decider: result.decider, probabilities: result.probabilities, confidence: result.confidence, threshold: result.threshold, reason, escalations };
436
589
  }
437
590
  if (result.status === "answered") return { state: "answered", answer: result.answer, decider: result.decider, escalations };
438
591
  return { state: "escalated", decider: result.decider, escalations };
@@ -444,6 +597,14 @@ function resultFields(result: ChainResult): Record<string, unknown> {
444
597
  * answered or proposed before.
445
598
  */
446
599
  export async function askPoint(opts: AskPointOptions): Promise<PointsWriteDocument> {
600
+ try {
601
+ return await withWriteLock(opts.cwd, { verb: "points ask", by: opts.steward?.name ?? null }, opts.dryRun, () => askPointLocked(opts));
602
+ } catch (err) {
603
+ return failure("ask", err);
604
+ }
605
+ }
606
+
607
+ async function askPointLocked(opts: AskPointOptions): Promise<PointsWriteDocument> {
447
608
  try {
448
609
  // A steward's turn (#2749): named by the caller, or this process's, as when an Op shells out to `points ask`.
449
610
  const turn = opts.steward ? undefined : currentStewardTurn();
@@ -452,8 +613,9 @@ export async function askPoint(opts: AskPointOptions): Promise<PointsWriteDocume
452
613
  const kinds = await answerKindFiles(opts.cwd, opts.kind);
453
614
  const { o, point } = await findPoint(kinds, opts.point, opts.cwd);
454
615
  const inputs = checkInputs(opts.point, point, opts.inputs);
616
+ const asked = checkAsked(opts.point, point, opts.candidates, o.loaded);
455
617
  const version = pointVersion(point);
456
- const hash = inputsHash(opts.point, version, inputs);
618
+ const hash = inputsHash(opts.point, version, inputs, asked);
457
619
  const id = answerId(opts.point, hash);
458
620
  if (toLedger && !opts.dryRun) await refreshLedger(o.loaded.file);
459
621
  const { ledger, source: read, before } = await readWithLedger(o);
@@ -473,10 +635,12 @@ export async function askPoint(opts: AskPointOptions): Promise<PointsWriteDocume
473
635
  ...(extra.text !== undefined ? { text: extra.text } : {}),
474
636
  });
475
637
  if (existing && existing.data !== null && (existing.state === "answered" || existing.state === "proposed")) return done(existing, { reused: true, written: false });
638
+ // People took a retracted question back from the deciders (ws-084): they answer it again, and asking returns it as it stands.
639
+ if (existing && existing.data !== null && Array.isArray(existing.data.retractions) && existing.data.retractions.length > 0) return done(existing, { reused: true, written: false });
476
640
 
477
641
  let result: ChainResult;
478
642
  try {
479
- result = await runChain(opts.point, point, inputs, opts.ask);
643
+ result = await runChain(opts.point, { ...point, question: askedQuestion(point, asked) }, inputs, opts.ask);
480
644
  } catch (err) {
481
645
  if (err instanceof DeciderFailed) throw new PointsWriteError("point-decider-failed", err.message);
482
646
  throw err;
@@ -487,7 +651,7 @@ export async function askPoint(opts: AskPointOptions): Promise<PointsWriteDocume
487
651
  const on = opts.on ?? today();
488
652
  const state = result.status;
489
653
  const answer = result.status === "escalated" ? undefined : result.answer;
490
- const title = titleFor(point, opts.subject, state, answer);
654
+ const title = titleFor(point, opts.subject, state, answer, asked);
491
655
  const modelId = result.status === "proposed" ? result.decider.model : undefined;
492
656
  const client = steward ? { name: steward.name } : opts.client;
493
657
  const source = {
@@ -503,21 +667,22 @@ export async function askPoint(opts: AskPointOptions): Promise<PointsWriteDocume
503
667
  point: opts.point,
504
668
  point_version: version,
505
669
  question_type: point.question.type,
506
- candidates: candidates(point.question),
670
+ candidates: candidates(askedQuestion(point, asked)),
671
+ asked,
507
672
  inputs,
508
673
  inputs_hash: hash,
509
674
  constrains: opts.subject !== undefined ? [opts.subject] : existing?.data?.constrains,
510
- ...resultFields(result),
675
+ ...resultFields(result, reasonFields(o.loaded.schema)),
511
676
  asked_on: on,
512
677
  answered_on: state === "answered" ? on : undefined,
513
678
  source,
514
679
  });
515
680
  const path = existing ? existing.path : o.dirRel === "." ? `${id}.md` : `${o.dirRel}/${id}.md`;
516
- const text = renderRecord(data, body(point, title));
681
+ const text = renderRecord(data, body(point, title, asked));
517
682
  // A steward's question goes on the ledger, and one already held there stays there (#2786).
518
683
  const onLedger = toLedger || ledger.byPath.has(path);
519
684
  const message = `Decision point ${opts.point}: ${state}${opts.subject ? ` (${opts.subject})` : ""}${steward ? `, asked by ${steward.name}` : ""}`;
520
- const wrote = await write(o, before, path, text, !existing, !!opts.dryRun, { source: read, ledger, onLedger, message });
685
+ const wrote = await write(o, before, path, text, !existing, !!opts.dryRun, { source: read, ledger, onLedger, message, who: { verb: "points ask", by: steward?.name ?? null } });
521
686
  const entry: RecordEntry = { ...(existing ?? emptyEntry(path)), id, path, state, data, valid: true, reasons: [], warnings: wrote.warnings };
522
687
  return done(entry, { reused: false, written: !opts.dryRun, ledger: wrote.ledger, ...(opts.dryRun ? { text } : {}) });
523
688
  } catch (err) {
@@ -539,11 +704,38 @@ export interface AnswerPointOptions {
539
704
  answer: string | boolean;
540
705
  /** Who answered, as the caller names them. chant does not check who they are; the trust policy says who counts. */
541
706
  by: string[];
707
+ /** What the people who answered say with it, recorded as the record's `note` (#3351). */
708
+ note?: string;
709
+ /**
710
+ * Who carried the answer to chant for the people in `by` (#3402), such as a
711
+ * follower relaying a person's answer through hud, recorded as the record's
712
+ * `relayed_by`. The relay does not count toward the quorum.
713
+ */
714
+ relayedBy?: string;
542
715
  kind?: string;
543
716
  on?: string;
544
717
  dryRun?: boolean;
545
718
  }
546
719
 
720
+ /**
721
+ * Whether an answer kind's schema copy takes the fields of #3351 (`note` on an
722
+ * answer, and `retractions`), of #3402 (`relayed_by`) and of #3403 (`asked`).
723
+ * A copy of point-answer.schema.json from before them refuses a note, a
724
+ * retraction, a relay or an ad-hoc ask with `answer-field-unsupported`, rather
725
+ * than dropping what a person wrote or the question an agent asked.
726
+ */
727
+ export function answerFields(schema: Record<string, unknown>): { note: boolean; retractions: boolean; relayedBy: boolean; asked: boolean } {
728
+ const props = schema.properties as Record<string, unknown> | undefined;
729
+ const has = (key: string): boolean => props !== undefined && props !== null && typeof props === "object" && Object.prototype.hasOwnProperty.call(props, key);
730
+ return { note: has("note"), retractions: has("retractions"), relayedBy: has("relayed_by"), asked: has("asked") };
731
+ }
732
+
733
+ /** A note as given, trimmed, or undefined when it is empty. */
734
+ const noteOf = (note: string | undefined): string | undefined => {
735
+ const t = note?.trim();
736
+ return t ? t : undefined;
737
+ };
738
+
547
739
  /** The trust policy at base, for the quorum: who holds the agent role and each role a quorum names. */
548
740
  function policyFor(root: string): TrustPolicy {
549
741
  if (!gitRoot(root)) return emptyPolicy(null);
@@ -593,71 +785,114 @@ export function tallyQuorum(
593
785
  return { counted, left, met: counted.length >= quorum.count };
594
786
  }
595
787
 
788
+ /**
789
+ * A steward waits on a question and never answers or retracts one (#2749), as
790
+ * it never clears a gate: the write has to come from a person, through hud or
791
+ * a shell, not from the steward's own turn or a process it started.
792
+ */
793
+ function refuseStewardTurn(id: string, does: "answers" | "retracts"): void {
794
+ const turn = currentStewardTurn();
795
+ if (turn) {
796
+ throw new PointsWriteError(
797
+ "answer-in-steward-turn",
798
+ `this is the steward ${turn.steward}'s turn, and a steward never ${does} a decision point: a person ${does === "answers" ? "answers" : "retracts the answer to"} ${id} through hud or \`chant workspace points ${does === "answers" ? "answer" : "retract"}\` at a shell`,
799
+ );
800
+ }
801
+ }
802
+
803
+ interface FoundQuestion {
804
+ opened: OpenedPoints;
805
+ before: RecordEntry[];
806
+ read: Awaited<ReturnType<typeof readWithLedger>>;
807
+ target: RecordEntry & { data: Record<string, unknown> };
808
+ }
809
+
810
+ /** The answer record with this id, in the answer kind `kind` names or the first declared one that has it, read with the ledger's answers laid over the tree. */
811
+ async function findQuestion(cwd: string, kind: string | undefined, id: string): Promise<FoundQuestion> {
812
+ const kinds = await answerKindFiles(cwd, kind);
813
+ if (kinds.length === 0) throw new PointsWriteError("points-undeclared", "no record kind with an answers block is declared: name one with --kind, or declare one in chant.workspace.json");
814
+ for (const k of kinds) {
815
+ const opened = await openAnswers(k, cwd);
816
+ // A question a steward asked is on the ledger (#2786), and people's answer goes back there.
817
+ const read = await readWithLedger(opened.o);
818
+ const target = read.before.find((e) => e.id === id);
819
+ if (target && target.data !== null) return { opened, before: read.before, read, target: target as FoundQuestion["target"] };
820
+ }
821
+ throw new PointsWriteError("record-not-found", `no answer record has id ${id}`);
822
+ }
823
+
824
+ /** Count `by` toward the point's quorum, or refuse with `quorum-not-met`. */
825
+ function meetQuorum(name: string, by: string[], quorum: { count: number; roles?: string[] }, root: string, steward: string | undefined, verb: "answer" | "retract"): { counted: string[] } {
826
+ const tally = tallyQuorum(by, quorum, policyFor(root), steward);
827
+ if (!tally.met) {
828
+ const got = tally.counted.length;
829
+ const left = tally.left.length ? `; not counted: ${tally.left.map((l) => `${l.name}, who ${l.why}`).join("; ")}` : "";
830
+ throw new PointsWriteError(
831
+ "quorum-not-met",
832
+ `${name} needs ${quorum.count} ${quorum.count === 1 ? "person" : "people"}${quorum.roles ? ` holding ${quorum.roles.join(" or ")}` : ""} to ${verb === "answer" ? "answer" : "retract an answer"}, and ${got} ${got === 1 ? "counts" : "count"}${got ? ` (${tally.counted.join(", ")})` : ""}${left}`,
833
+ );
834
+ }
835
+ return tally;
836
+ }
837
+
596
838
  /**
597
839
  * People answer an open question: `points answer`. A proposed question the
598
840
  * people answer as the model did is confirmed, and keeps the model as its
599
841
  * decider; any other answer is the quorum's, and a model's proposal moves into
600
- * the escalations. The question becomes `answered`.
842
+ * the escalations. The question becomes `answered`, with the people's note
843
+ * when they give one (#3351).
601
844
  */
602
845
  export async function answerPoint(opts: AnswerPointOptions): Promise<PointsWriteDocument> {
603
846
  try {
604
- // A steward waits on a question and never answers one (#2749), as it never
605
- // clears a gate: the answer has to come from a person, through hud or a
606
- // shell, not from the steward's own turn or a process it started.
607
- const turn = currentStewardTurn();
608
- if (turn) {
609
- throw new PointsWriteError(
610
- "answer-in-steward-turn",
611
- `this is the steward ${turn.steward}'s turn, and a steward never answers a decision point: a person answers ${opts.id} through hud or \`chant workspace points answer\` at a shell`,
612
- );
613
- }
614
- const kinds = await answerKindFiles(opts.cwd, opts.kind);
615
- if (kinds.length === 0) throw new PointsWriteError("points-undeclared", "no record kind with an answers block is declared: name one with --kind, or declare one in chant.workspace.json");
616
- let found:
617
- | { opened: OpenedPoints; before: RecordEntry[]; read: Awaited<ReturnType<typeof readWithLedger>>; target: RecordEntry & { data: Record<string, unknown> } }
618
- | undefined;
619
- for (const k of kinds) {
620
- const opened = await openAnswers(k, opts.cwd);
621
- // A question a steward asked is on the ledger (#2786), and people's answer goes back there.
622
- const read = await readWithLedger(opened.o);
623
- const target = read.before.find((e) => e.id === opts.id);
624
- if (target && target.data !== null) {
625
- found = { opened, before: read.before, read, target: target as RecordEntry & { data: Record<string, unknown> } };
626
- break;
627
- }
628
- }
629
- if (!found) throw new PointsWriteError("record-not-found", `no answer record has id ${opts.id}`);
847
+ return await withWriteLock(opts.cwd, { verb: "points answer", by: (opts.by ?? []).join(", ") || null }, opts.dryRun, () => answerPointLocked(opts));
848
+ } catch (err) {
849
+ return failure("answer", err);
850
+ }
851
+ }
852
+
853
+ async function answerPointLocked(opts: AnswerPointOptions): Promise<PointsWriteDocument> {
854
+ try {
855
+ refuseStewardTurn(opts.id, "answers");
856
+ // #3163: under identity.attribution "identified" at base, each answerer is a forge identity or a signer.
857
+ refuseUnidentified(scopeSource(opts.cwd), opts.by, "--by");
858
+ // #3402: and so is whoever relayed the answer.
859
+ const relayedBy = noteOf(opts.relayedBy);
860
+ if (relayedBy !== undefined) refuseUnidentified(scopeSource(opts.cwd), [relayedBy], "--relayed-by");
861
+ const found = await findQuestion(opts.cwd, opts.kind, opts.id);
630
862
  const { opened, before, target } = found;
631
863
  const ledger = found.read.ledger;
632
864
  const { o } = opened;
633
865
  const d = target.data;
634
- if (target.state === "answered") throw new PointsWriteError("record-closed", `${opts.id} is answered, and an answer never changes: ask again with other inputs, or change the point, which asks the question anew`);
866
+ if (target.state === "answered") {
867
+ throw new PointsWriteError("record-closed", `${opts.id} is answered, and an answer never changes in place: retract it with \`points retract ${opts.id}\` and answer again, ask again with other inputs, or change the point, which asks the question anew`);
868
+ }
869
+ const note = noteOf(opts.note);
870
+ if (note !== undefined && !answerFields(o.loaded.schema).note) {
871
+ throw new PointsWriteError("answer-field-unsupported", `the ${o.loaded.kind.name} kind's schema has no note field, so ${opts.id} can't keep the note: copy point-answer.schema.json from @intentius/chant anew`);
872
+ }
873
+ if (relayedBy !== undefined && !answerFields(o.loaded.schema).relayedBy) {
874
+ throw new PointsWriteError("answer-field-unsupported", `the ${o.loaded.kind.name} kind's schema has no relayed_by field, so ${opts.id} can't record who relayed the answer: copy point-answer.schema.json from @intentius/chant anew`);
875
+ }
635
876
  const name = String(d.point);
636
877
  const point = pointOf(opened.points, name);
637
878
  if (!point) throw new PointsWriteError("point-unknown", `${opts.id} answers ${name}, which ${opened.pointsFile} no longer declares`);
638
- const allowed = Array.isArray(d.candidates) ? (d.candidates as (string | boolean)[]) : candidates(point.question);
879
+ // An ad-hoc question is answered from the candidates it was asked with (#3403).
880
+ const asked = askedOf(d);
881
+ const allowed = asked ? candidates(askedQuestion(point, asked)) : Array.isArray(d.candidates) ? (d.candidates as (string | boolean)[]) : candidates(point.question);
639
882
  const type = String(d.question_type);
640
883
  const value = type === "noul" && typeof opts.answer === "string" ? ({ true: true, yes: true, false: false, no: false } as Record<string, boolean>)[opts.answer.toLowerCase()] : opts.answer;
641
884
  if (value === undefined || !allowed.includes(value)) {
642
885
  throw new PointsWriteError("answer-not-candidate", `${opts.id} takes one of ${allowed.map((c) => JSON.stringify(c)).join(", ")}, not ${JSON.stringify(opts.answer)}`);
643
886
  }
644
887
  const quorum = quorumOf(point);
645
- const tally = tallyQuorum(opts.by, quorum, policyFor(o.root), askedByOf(d)?.steward);
646
- if (!tally.met) {
647
- const got = tally.counted.length;
648
- const left = tally.left.length ? `; not counted: ${tally.left.map((l) => `${l.name}, who ${l.why}`).join("; ")}` : "";
649
- throw new PointsWriteError(
650
- "quorum-not-met",
651
- `${name} needs ${quorum.count} ${quorum.count === 1 ? "person" : "people"}${quorum.roles ? ` holding ${quorum.roles.join(" or ")}` : ""} to answer, and ${got} ${got === 1 ? "counts" : "count"}${got ? ` (${tally.counted.join(", ")})` : ""}${left}`,
652
- );
653
- }
888
+ const tally = meetQuorum(name, opts.by, quorum, o.root, askedByOf(d)?.steward, "answer");
654
889
  const on = opts.on ?? today();
655
890
  const decider = d.decider as Record<string, unknown>;
656
891
  const confirmed = target.state === "proposed" && decider.kind === "model" && d.answer === value;
657
892
  const escalations = Array.isArray(d.escalations) ? [...(d.escalations as Escalation[])] : [];
658
893
  let fields: Record<string, unknown>;
659
894
  if (confirmed) {
660
- fields = { ...d, state: "answered", answered_by: tally.counted, answered_on: on };
895
+ fields = { ...d, state: "answered", answered_by: tally.counted, answered_on: on, note, relayed_by: relayedBy };
661
896
  } else {
662
897
  if (target.state === "proposed" && decider.kind === "model") {
663
898
  escalations.push({
@@ -669,9 +904,11 @@ export async function answerPoint(opts: AnswerPointOptions): Promise<PointsWrite
669
904
  ...(typeof d.confidence === "number" ? { confidence: d.confidence } : {}),
670
905
  ...(typeof d.threshold === "number" ? { threshold: d.threshold } : {}),
671
906
  reason: `proposed ${show(d.answer as string | boolean, type)}, and people answered ${show(value, type)}`,
907
+ // The model's own reason moves with its proposal (#3345).
908
+ ...(typeof d.reason === "string" && reasonFields(o.loaded.schema).escalation ? { model_reason: d.reason } : {}),
672
909
  });
673
910
  }
674
- const rest = Object.fromEntries(Object.entries(d).filter(([k]) => k !== "probabilities" && k !== "confidence" && k !== "threshold"));
911
+ const rest = Object.fromEntries(Object.entries(d).filter(([k]) => k !== "probabilities" && k !== "confidence" && k !== "threshold" && k !== "reason"));
675
912
  fields = {
676
913
  ...rest,
677
914
  state: "answered",
@@ -680,15 +917,17 @@ export async function answerPoint(opts: AnswerPointOptions): Promise<PointsWrite
680
917
  escalations: escalations.length > 0 ? escalations : undefined,
681
918
  answered_by: tally.counted,
682
919
  answered_on: on,
920
+ note,
921
+ relayed_by: relayedBy,
683
922
  };
684
923
  }
685
924
  const subject = Array.isArray(d.constrains) && typeof d.constrains[0] === "string" ? (d.constrains[0] as string) : undefined;
686
- fields.title = titleFor(point, subject, "answered", value);
925
+ fields.title = titleFor(point, subject, "answered", value, asked);
687
926
  const data = recordData(o, fields);
688
- const text = renderRecord(data, body(point, String(fields.title)));
927
+ const text = renderRecord(data, body(point, String(fields.title), asked));
689
928
  const onLedger = ledger.byPath.has(target.path);
690
- const message = `Decision point ${name}: answered ${show(value, type)} by ${tally.counted.join(", ")}`;
691
- const wrote = await write(o, before, target.path, text, false, !!opts.dryRun, { source: found.read.source, ledger, onLedger, message });
929
+ const message = `Decision point ${name}: answered ${show(value, type)} by ${tally.counted.join(", ")}${relayedBy ? `, relayed by ${relayedBy}` : ""}`;
930
+ const wrote = await write(o, before, target.path, text, false, !!opts.dryRun, { source: found.read.source, ledger, onLedger, message, who: { verb: "points answer", by: tally.counted.join(", ") } });
692
931
  const entry: RecordEntry = { ...target, state: "answered", data, valid: true, reasons: [], warnings: wrote.warnings };
693
932
  return {
694
933
  $schema: POINTS_WRITE_SCHEMA_ID,
@@ -707,3 +946,124 @@ export async function answerPoint(opts: AnswerPointOptions): Promise<PointsWrite
707
946
  return failure("answer", err);
708
947
  }
709
948
  }
949
+
950
+ // ── points retract ───────────────────────────────────────────────────────────
951
+
952
+ export interface RetractAnswerOptions {
953
+ cwd: string;
954
+ /** The answer record's id. */
955
+ id: string;
956
+ /** Who takes the answer back. They count toward the point's quorum as answerers do. */
957
+ by: string[];
958
+ /** Why, recorded on the retraction. */
959
+ note?: string;
960
+ kind?: string;
961
+ on?: string;
962
+ dryRun?: boolean;
963
+ }
964
+
965
+ /**
966
+ * People take an answer back: `points retract` (#3351, ws-084). The answer
967
+ * never changes in place. The question is escalated to the point's quorum
968
+ * again, open for people, and the answer as it was (its value, decider, who
969
+ * gave it, when, and its note) moves into the record's `retractions`, with who
970
+ * retracted it, when and why. A model's confirmed proposal also moves into the
971
+ * escalations, as when people answer otherwise. Retracting needs the point's
972
+ * quorum, counted as an answer is, and is refused in a steward's turn. People
973
+ * answer the question again with `points answer`.
974
+ */
975
+ export async function retractAnswer(opts: RetractAnswerOptions): Promise<PointsWriteDocument> {
976
+ try {
977
+ return await withWriteLock(opts.cwd, { verb: "points retract", by: (opts.by ?? []).join(", ") || null }, opts.dryRun, () => retractAnswerLocked(opts));
978
+ } catch (err) {
979
+ return failure("retract", err);
980
+ }
981
+ }
982
+
983
+ async function retractAnswerLocked(opts: RetractAnswerOptions): Promise<PointsWriteDocument> {
984
+ try {
985
+ refuseStewardTurn(opts.id, "retracts");
986
+ refuseUnidentified(scopeSource(opts.cwd), opts.by, "--by");
987
+ const found = await findQuestion(opts.cwd, opts.kind, opts.id);
988
+ const { opened, before, target } = found;
989
+ const ledger = found.read.ledger;
990
+ const { o } = opened;
991
+ const d = target.data;
992
+ if (target.state !== "answered") {
993
+ throw new PointsWriteError("answer-not-answered", `${opts.id} is ${target.state ?? "not answered"}, so there is no answer to retract: answer it with \`points answer ${opts.id}\``);
994
+ }
995
+ if (!answerFields(o.loaded.schema).retractions) {
996
+ throw new PointsWriteError("answer-field-unsupported", `the ${o.loaded.kind.name} kind's schema has no retractions field, so ${opts.id} can't keep the answer it would retract: copy point-answer.schema.json from @intentius/chant anew`);
997
+ }
998
+ const name = String(d.point);
999
+ const point = pointOf(opened.points, name);
1000
+ if (!point) throw new PointsWriteError("point-unknown", `${opts.id} answers ${name}, which ${opened.pointsFile} no longer declares, so no quorum can take the answer back: ask the point as it is declared now`);
1001
+ const quorum = quorumOf(point);
1002
+ const tally = meetQuorum(name, opts.by, quorum, o.root, askedByOf(d)?.steward, "retract");
1003
+ const on = opts.on ?? today();
1004
+ const type = String(d.question_type);
1005
+ const decider = d.decider !== null && typeof d.decider === "object" && !Array.isArray(d.decider) ? (d.decider as Record<string, unknown>) : { kind: "quorum" };
1006
+ const answer = d.answer as string | boolean;
1007
+ const note = noteOf(opts.note);
1008
+ const retraction = {
1009
+ answer,
1010
+ decider,
1011
+ answered_by: Array.isArray(d.answered_by) && d.answered_by.length > 0 ? d.answered_by : undefined,
1012
+ answered_on: typeof d.answered_on === "string" ? d.answered_on : undefined,
1013
+ answer_note: typeof d.note === "string" ? d.note : undefined,
1014
+ answer_relayed_by: typeof d.relayed_by === "string" ? d.relayed_by : undefined,
1015
+ by: tally.counted,
1016
+ on,
1017
+ note,
1018
+ };
1019
+ const escalations = Array.isArray(d.escalations) ? [...(d.escalations as Escalation[])] : [];
1020
+ if (decider.kind === "model") {
1021
+ // The confirmed proposal goes back among the deciders that did not settle the question.
1022
+ escalations.push({
1023
+ kind: "model",
1024
+ backend: String(decider.backend),
1025
+ model: String(decider.model),
1026
+ answer,
1027
+ ...(d.probabilities ? { probabilities: d.probabilities as Record<string, number> } : {}),
1028
+ ...(typeof d.confidence === "number" ? { confidence: d.confidence } : {}),
1029
+ ...(typeof d.threshold === "number" ? { threshold: d.threshold } : {}),
1030
+ reason: `proposed ${show(answer, type)}, which people confirmed and then retracted`,
1031
+ ...(typeof d.reason === "string" && reasonFields(o.loaded.schema).escalation ? { model_reason: d.reason } : {}),
1032
+ });
1033
+ }
1034
+ const gone = new Set(["answer", "answered_by", "answered_on", "note", "relayed_by", "probabilities", "confidence", "threshold", "reason"]);
1035
+ const rest = Object.fromEntries(Object.entries(d).filter(([k]) => !gone.has(k)));
1036
+ const prior = Array.isArray(d.retractions) ? (d.retractions as unknown[]) : [];
1037
+ const subject = Array.isArray(d.constrains) && typeof d.constrains[0] === "string" ? (d.constrains[0] as string) : undefined;
1038
+ const asked = askedOf(d);
1039
+ const title = titleFor(point, subject, "escalated", undefined, asked);
1040
+ const data = recordData(o, {
1041
+ ...rest,
1042
+ title,
1043
+ state: "escalated",
1044
+ decider: { kind: "quorum", count: quorum.count, ...(quorum.roles ? { roles: quorum.roles } : {}) },
1045
+ escalations: escalations.length > 0 ? escalations : undefined,
1046
+ retractions: [...prior, Object.fromEntries(Object.entries(retraction).filter(([, v]) => v !== undefined))],
1047
+ });
1048
+ const text = renderRecord(data, body(point, title, asked));
1049
+ const onLedger = ledger.byPath.has(target.path);
1050
+ const message = `Decision point ${name}: ${show(answer, type)} retracted by ${tally.counted.join(", ")}`;
1051
+ const wrote = await write(o, before, target.path, text, false, !!opts.dryRun, { source: found.read.source, ledger, onLedger, message, who: { verb: "points retract", by: tally.counted.join(", ") } });
1052
+ const entry: RecordEntry = { ...target, state: "escalated", data, valid: true, reasons: [], warnings: wrote.warnings };
1053
+ return {
1054
+ $schema: POINTS_WRITE_SCHEMA_ID,
1055
+ contract: POINTS_WRITE_CONTRACT_VERSION,
1056
+ verb: "retract",
1057
+ kind: o.view,
1058
+ id: opts.id,
1059
+ path: target.path,
1060
+ reused: false,
1061
+ written: !opts.dryRun,
1062
+ dryRun: !!opts.dryRun,
1063
+ question: questionView(entry, point, onLedger ? (wrote.ledger ?? ledger.byPath.get(target.path)?.ledger ?? null) : null)!,
1064
+ ...(opts.dryRun ? { text } : {}),
1065
+ };
1066
+ } catch (err) {
1067
+ return failure("retract", err);
1068
+ }
1069
+ }