@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
@@ -158,12 +158,39 @@ describe("chant workspace work (#2732)", () => {
158
158
  expect(json(closed).error.code).toBe("work-item-closed");
159
159
  const verb = await chant(root, ["workspace", "work", "grab", "W-001", "--holder", "a"]);
160
160
  expect(verb.code).toBe(1);
161
- expect(verb.stderr).toContain("claim, renew, release or history");
161
+ expect(verb.stderr).toContain("claim, renew, release, history or evidence");
162
162
  const noHolder = await chant(root, ["workspace", "work", "claim", "W-001"]);
163
163
  expect(noHolder.code).toBe(1);
164
164
  expect(noHolder.stderr).toContain("--holder");
165
165
  }, 120_000);
166
166
 
167
+ test("evidence (#3159): needs the holder, the token and the entry, runs only under the live lease, and names only a criterion the item lists", async () => {
168
+ const entry = JSON.stringify({ criterion: "AC-1", result: "pass", title: "The test", url: "https://example.com/ci/1" });
169
+ const usage = await chant(root, ["workspace", "work", "evidence", "W-002", "--holder", "a", "--from", "-"]);
170
+ expect(usage.code).toBe(1);
171
+ expect(JSON.parse(usage.stdout).error).toMatchObject({ code: "write-usage-invalid", message: expect.stringContaining("--token") });
172
+ const evidence = (holder: string, token: string) =>
173
+ new Promise<Run>((resolve) => {
174
+ const child = spawn(CHANT[0], [...CHANT.slice(1), "workspace", "work", "evidence", "W-002", "--holder", holder, "--token", token, "--from", "-"], { cwd: root, env: { ...process.env, NO_COLOR: "1" } });
175
+ let stdout = "";
176
+ let stderr = "";
177
+ child.stdout.on("data", (d) => (stdout += d));
178
+ child.stderr.on("data", (d) => (stderr += d));
179
+ child.on("close", (code) => resolve({ code, stdout, stderr }));
180
+ child.stdin.end(entry);
181
+ });
182
+ // worker-d holds W-002 since the records test above: anyone else is refused.
183
+ const held = json(await chant(root, ["workspace", "work", "claim", "W-002", "--holder", "ev", "--json"])).refused.heldBy as { holder: string; token: string };
184
+ const other = await evidence("ev", "none");
185
+ expect(other.code).toBe(1);
186
+ expect(JSON.parse(other.stdout).error.code).toBe("lease-held");
187
+ const stale = await evidence(held.holder, "not-the-token");
188
+ expect(JSON.parse(stale.stdout).error.code).toBe("lease-token-mismatch");
189
+ const unlisted = await evidence(held.holder, held.token);
190
+ expect(unlisted.code).toBe(1);
191
+ expect(JSON.parse(unlisted.stdout).error.code).toBe("work-criterion-unknown");
192
+ }, 120_000);
193
+
167
194
  test("the lease fields reach an MCP client through workspace-records and workspace-status", async () => {
168
195
  const claim = await chant(root, ["workspace", "work", "claim", "W-001", "--holder", "mcp-worker", "--json"]);
169
196
  expect(claim.code, claim.stderr).toBe(0);
@@ -4,6 +4,10 @@
4
4
  * workspace work history <id>` (#2785): the item's lease history, each claim
5
5
  * and how it ended, a read-contract output (`work-history.schema.json`).
6
6
  *
7
+ * And `chant workspace work evidence <id>` (#3159): the `workEvidence`
8
+ * activity as a command, so a writer outside an Op attaches evidence under
9
+ * the lease it holds.
10
+ *
7
11
  * The lease itself is `../lifecycle/work-lease.ts`, the operator lease under
8
12
  * the key `work/<id>`. This file finds the work item first: in the work kind
9
13
  * `--kind` names, or in the one work kind the declaration names that has a
@@ -26,13 +30,18 @@ import type { CommandContext } from "../cli/registry";
26
30
  import { parseDuration } from "../op/duration";
27
31
  import {
28
32
  claimWorkLease,
33
+ isWorkLeaseOutcome,
29
34
  leaseHistoryPath,
35
+ listKeptAttempts,
30
36
  listWorkLeases,
37
+ outcomeCountsAsAttempt,
31
38
  readLeaseHistory,
32
39
  releaseWorkLease,
33
40
  renewWorkLease,
34
41
  workLeaseRef,
35
42
  WORK_ITEM_ID_PATTERN,
43
+ WORK_LEASE_OUTCOMES,
44
+ type KeptAttempt,
36
45
  type WorkLease,
37
46
  type WorkLeaseRefusal,
38
47
  type WorkLeaseResult,
@@ -46,6 +55,7 @@ import { declaredKindFiles, readRecordsFor } from "./records-cli";
46
55
  import { loadRecordKind, RecordReadError, type LoadedRecordKind, type RecordEntry } from "./records";
47
56
  import { gitRoot } from "./record-source";
48
57
  import type { ReasonCode } from "./reason-codes";
58
+ import { replicateAfter } from "./wip";
49
59
 
50
60
  /** The version of the `work` output this chant writes. */
51
61
  export const WORK_LEASE_CONTRACT_VERSION = 1;
@@ -53,11 +63,12 @@ export const WORK_LEASE_CONTRACT_VERSION = 1;
53
63
  /** `$id` of the JSON Schema for the `--json` output, shipped beside this file. */
54
64
  export const WORK_LEASE_OUTPUT_SCHEMA_ID = "https://intentius.io/chant/schemas/workspace/work-lease/v1/work-lease.schema.json";
55
65
 
56
- export { WORK_LEASE_REFUSALS } from "../lifecycle/work-lease";
66
+ export { WORK_LEASE_REFUSALS, WORK_LEASE_OUTCOMES } from "../lifecycle/work-lease";
57
67
 
58
68
  const USAGE = [
59
- "chant workspace work claim|renew|release <id> --holder <name> [--kind <kind file>] [--ttl <seconds|duration>] [--token <token>] [--outcome <text>] [--note <text>] [--json]",
69
+ "chant workspace work claim|renew|release <id> --holder <name> [--kind <kind file>] [--ttl <seconds|duration>] [--token <token>] [--outcome <outcome>] [--note <text>] [--json]",
60
70
  "chant workspace work history <id> [--kind <kind file>] [--json]",
71
+ "chant workspace work evidence <id> --holder <name> --token <token> --from <file|-> [--kind <kind file>]",
61
72
  ].join("\n");
62
73
 
63
74
  /** Exit code when the lease was refused rather than the command failing. */
@@ -193,6 +204,9 @@ export async function workLease(req: WorkLeaseRequest): Promise<WorkLeaseDocumen
193
204
  const doc = { $schema: WORK_LEASE_OUTPUT_SCHEMA_ID, contract: WORK_LEASE_CONTRACT_VERSION };
194
205
  try {
195
206
  if (!WORK_ITEM_ID_PATTERN.test(req.id) || req.id.includes("..")) throw new WorkError("write-usage-invalid", `${JSON.stringify(req.id)} is not a work item id`);
207
+ if (req.outcome !== undefined && !isWorkLeaseOutcome(req.outcome)) {
208
+ throw new WorkError("write-usage-invalid", `${JSON.stringify(req.outcome)} is not a lease outcome: use one of ${WORK_LEASE_OUTCOMES.join(", ")}`);
209
+ }
196
210
  if (!gitRoot(req.cwd)) throw new WorkError("not-a-git-repository", "a work lease is a git ref, and this directory is not in a git repository");
197
211
  const { loaded, state, root } = await findWorkItem(req.id, req.cwd, req.kind);
198
212
  if (req.verb === "claim" && state !== null && (loaded.kind.closedStates ?? []).includes(state)) {
@@ -210,6 +224,8 @@ export async function workLease(req: WorkLeaseRequest): Promise<WorkLeaseDocumen
210
224
  const heldBy = result.heldBy ? strip(result.heldBy) : null;
211
225
  return { ...head, refused: { code: result.reason, message: result.message, heldBy } };
212
226
  }
227
+ // #3172: a box's replicate policy may push its work in progress once a lease is released. Best effort; status shows what is behind.
228
+ if (req.verb === "release") replicateAfter("release", req.cwd);
213
229
  return { ...head, lease: strip(result.lease), history: result.history };
214
230
  } catch (err) {
215
231
  if (err instanceof WorkError) return { ...doc, error: { code: err.code, message: err.message } };
@@ -259,6 +275,27 @@ export interface LeaseClaim {
259
275
  ended: ClaimEnd;
260
276
  /** The release, when it was released. */
261
277
  release: { by: string; at: string; outcome: string | null; note: string | null } | null;
278
+ /**
279
+ * Whether the claim counts toward the item's attempt limit (#3147): one
280
+ * released `not_done` or `abandoned`, with no outcome or an outcome from
281
+ * before the list was closed, or one that expired or was lost unreleased.
282
+ * Not one that is held, and not one released with any other outcome.
283
+ */
284
+ attempt: boolean;
285
+ /** The ref keeping this claim's unfinished work, `refs/chant/kept/.../<item>/<token>`, or null. */
286
+ kept: string | null;
287
+ }
288
+
289
+ /** An item's attempts against its limit (#3147). */
290
+ export interface WorkAttempts {
291
+ /** The claims that count as attempts. */
292
+ failed: number;
293
+ /** The item's limit: its own field, else its kind's default; null when neither declares one. */
294
+ limit: number | null;
295
+ /** How many attempts are left, never below 0; null without a limit. */
296
+ remaining: number | null;
297
+ /** True once failed reaches the limit: a runner leaves the item to people. */
298
+ exhausted: boolean;
262
299
  }
263
300
 
264
301
  /** What `work history --json` prints. */
@@ -283,17 +320,21 @@ export type WorkHistoryDocument =
283
320
  /** Lines of the history that are not lease events, left out. */
284
321
  malformed: number;
285
322
  summary: { claims: number; released: number; held: number; expired: number; lost: number; outcomes: Record<string, number> };
323
+ /** The item's attempts against its limit (#3147). */
324
+ attempts: WorkAttempts;
325
+ /** Every kept attempt of the item, by ref (#3147). */
326
+ kept: KeptAttempt[];
286
327
  }
287
328
  | { $schema: string; contract: number; chant: string; error: { code: WorkHistoryErrorCode; message: string } };
288
329
 
289
330
  /** Fold a history into its claims: one per token, in the order they were first seen. */
290
- export function claimsOf(events: LeaseHistoryRecord[], live: Pick<WorkLeaseState, "token" | "state"> | null): LeaseClaim[] {
331
+ export function claimsOf(events: LeaseHistoryRecord[], live: Pick<WorkLeaseState, "token" | "state"> | null, kept: readonly KeptAttempt[] = []): LeaseClaim[] {
291
332
  const byToken = new Map<string, LeaseClaim>();
292
333
  const order: string[] = [];
293
334
  for (const e of events) {
294
335
  let c = byToken.get(e.token);
295
336
  if (!c) {
296
- c = { token: e.token, holder: e.holder, acquiredAt: e.acquiredAt, expiresAt: e.expiresAt, renewals: 0, ended: "expired", release: null };
337
+ c = { token: e.token, holder: e.holder, acquiredAt: e.acquiredAt, expiresAt: e.expiresAt, renewals: 0, ended: "expired", release: null, attempt: false, kept: null };
297
338
  byToken.set(e.token, c);
298
339
  order.push(e.token);
299
340
  }
@@ -307,10 +348,30 @@ export function claimsOf(events: LeaseHistoryRecord[], live: Pick<WorkLeaseState
307
348
  else if (live && live.token === token) c.ended = live.state === "active" ? "held" : "expired";
308
349
  else if (i < order.length - 1) c.ended = "lost";
309
350
  else c.ended = "expired";
351
+ c.attempt = c.ended === "released" ? outcomeCountsAsAttempt(c.release?.outcome) : c.ended !== "held";
352
+ c.kept = kept.find((k) => k.token === token)?.ref ?? null;
310
353
  return c;
311
354
  });
312
355
  }
313
356
 
357
+ /**
358
+ * The attempt limit of a work record (#3147): the integer in the field the
359
+ * kind's `work.attempts.field` names, else the kind's `work.attempts.max`;
360
+ * null when the kind declares no attempts.
361
+ */
362
+ export function attemptLimit(kind: LoadedRecordKind["kind"], data: Record<string, unknown> | null): number | null {
363
+ const spec = kind.work?.attempts;
364
+ if (!spec) return null;
365
+ const own = data?.[spec.field];
366
+ return typeof own === "number" && Number.isInteger(own) && own >= 1 ? own : spec.max;
367
+ }
368
+
369
+ /** Count `claims`' attempts against `limit`. */
370
+ export function attemptsOf(claims: readonly LeaseClaim[], limit: number | null): WorkAttempts {
371
+ const failed = claims.filter((c) => c.attempt).length;
372
+ return { failed, limit, remaining: limit === null ? null : Math.max(0, limit - failed), exhausted: limit !== null && failed >= limit };
373
+ }
374
+
314
375
  /**
315
376
  * Read work item `id`'s lease history (#2785): every claim, renew and release
316
377
  * on `_leases/<id>.jsonl` in the ledger of the member owning the work kind,
@@ -322,12 +383,13 @@ export async function workHistory(req: { id: string; cwd: string; kind?: string
322
383
  try {
323
384
  if (!WORK_ITEM_ID_PATTERN.test(req.id) || req.id.includes("..")) throw new WorkError("work-item-unknown", `${JSON.stringify(req.id)} is not a work item id`);
324
385
  if (!gitRoot(req.cwd)) throw new WorkError("not-a-git-repository", "a work lease's history is on the chant/lifecycle branch, and this directory is not in a git repository");
325
- const { loaded, root } = await findWorkItem(req.id, req.cwd, req.kind);
386
+ const { loaded, root, record } = await findWorkItem(req.id, req.cwd, req.kind);
326
387
  const leaseCwd = dirname(loaded.file);
327
- const { prefix } = await resolveMemberLedger(leaseCwd);
388
+ const { prefix, members } = await resolveMemberLedger(leaseCwd);
328
389
  const { records: events, malformed } = await readLeaseHistory(req.id, { cwd: leaseCwd });
329
390
  const live = (await listWorkLeases({ cwd: leaseCwd, memberPrefix: prefix })).find((l) => l.item === req.id) ?? null;
330
- const claims = claimsOf(events, live);
391
+ const kept = await listKeptAttempts(req.id, { cwd: leaseCwd, members });
392
+ const claims = claimsOf(events, live, kept);
331
393
  const count = (end: ClaimEnd) => claims.filter((c) => c.ended === end).length;
332
394
  const outcomes: Record<string, number> = {};
333
395
  for (const c of claims) if (c.release?.outcome) outcomes[c.release.outcome] = (outcomes[c.release.outcome] ?? 0) + 1;
@@ -342,6 +404,8 @@ export async function workHistory(req: { id: string; cwd: string; kind?: string
342
404
  events,
343
405
  malformed,
344
406
  summary: { claims: claims.length, released: count("released"), held: count("held"), expired: count("expired"), lost: count("lost"), outcomes },
407
+ attempts: attemptsOf(claims, attemptLimit(loaded.kind, record.data)),
408
+ kept,
345
409
  };
346
410
  } catch (err) {
347
411
  if (err instanceof WorkError) return { ...head, error: { code: err.code as WorkHistoryErrorCode, message: err.message } };
@@ -357,10 +421,73 @@ export function formatWorkHistory(doc: Extract<WorkHistoryDocument, { claims: un
357
421
  lines.push(` ${c.acquiredAt} ${c.holder} token ${c.token} ${c.renewals} renewal${c.renewals === 1 ? "" : "s"} ${how}`);
358
422
  }
359
423
  if (doc.claims.length === 0) lines.push(" never claimed");
424
+ const a = doc.attempts;
425
+ if (a.limit !== null) lines.push(` ${a.failed} of ${a.limit} attempt${a.limit === 1 ? "" : "s"} used${a.exhausted ? "; left to people" : ""}`);
426
+ else if (a.failed > 0) lines.push(` ${a.failed} attempt${a.failed === 1 ? "" : "s"} counted; the kind declares no limit`);
427
+ for (const k of doc.kept) lines.push(` kept: ${k.ref} (${k.commit.slice(0, 12)})`);
360
428
  if (doc.malformed > 0) lines.push(` ${doc.malformed} malformed line${doc.malformed === 1 ? "" : "s"} left out`);
361
429
  return lines.join("\n");
362
430
  }
363
431
 
432
+ /**
433
+ * `chant workspace work evidence <id> --holder <name> --token <token> --from
434
+ * <file|->` (#3159): the `workEvidence` activity (#2772) as a command, so a
435
+ * writer that is not an Op, such as hud, attaches evidence through chant under
436
+ * the lease it holds. `--from` gives the entry as JSON: criterion, result,
437
+ * title, a url or a path, and optionally as_of. It always prints the
438
+ * work-evidence document; exit 0 when the entry was appended, 1 otherwise.
439
+ */
440
+ async function runWorkEvidence(ctx: CommandContext): Promise<number> {
441
+ const { args } = ctx;
442
+ const { attachWorkEvidence, WORK_EVIDENCE_CONTRACT_VERSION, WORK_EVIDENCE_OUTPUT_SCHEMA_ID } = await import("./work-evidence");
443
+ const print = (doc: unknown, code: number): number => {
444
+ console.log(JSON.stringify(doc, null, 2));
445
+ return code;
446
+ };
447
+ const fail = (message: string): number =>
448
+ print({ $schema: WORK_EVIDENCE_OUTPUT_SCHEMA_ID, contract: WORK_EVIDENCE_CONTRACT_VERSION, error: { code: "write-usage-invalid", message: `${message}\n${USAGE}` } }, 1);
449
+ const id = args.extraPositional2;
450
+ if (!id) return fail("evidence needs a work item id");
451
+ if (!args.holder) return fail("evidence needs --holder <name>: the lease's holder");
452
+ if (!args.token) return fail("evidence needs --token <token>: the lease's fencing token, as its claim printed it");
453
+ if (args.migrateFrom === undefined) return fail("evidence needs --from <file|->: the entry as JSON, with criterion, result, title and a url or a path");
454
+ for (const [flag, v] of [["--ttl", args.ttl], ["--outcome", args.outcome], ["--note", args.note]] as const) {
455
+ if (v !== undefined) return fail(`evidence takes no ${flag}`);
456
+ }
457
+ let fields: Record<string, unknown>;
458
+ try {
459
+ const { readFileSync } = await import("node:fs");
460
+ const { resolve } = await import("node:path");
461
+ const text = readFileSync(args.migrateFrom === "-" ? 0 : resolve(process.cwd(), args.migrateFrom), "utf-8");
462
+ const parsed: unknown = JSON.parse(text);
463
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("the entry is not a JSON object");
464
+ fields = parsed as Record<string, unknown>;
465
+ } catch (err) {
466
+ return print(
467
+ { $schema: WORK_EVIDENCE_OUTPUT_SCHEMA_ID, contract: WORK_EVIDENCE_CONTRACT_VERSION, error: { code: "write-input-invalid", message: `--from ${args.migrateFrom} could not be read: ${err instanceof Error ? err.message : String(err)}` } },
468
+ 1,
469
+ );
470
+ }
471
+ const known = ["criterion", "result", "title", "url", "path", "as_of"];
472
+ const unknown = Object.keys(fields).filter((k) => !known.includes(k));
473
+ if (unknown.length > 0) return fail(`the entry takes ${known.join(", ")}, not ${unknown.join(", ")}`);
474
+ const str = (k: string): string | undefined => (typeof fields[k] === "string" ? (fields[k] as string) : undefined);
475
+ const doc = await attachWorkEvidence({
476
+ cwd: process.cwd(),
477
+ item: id,
478
+ kind: args.kind,
479
+ holder: args.holder,
480
+ token: args.token,
481
+ criterion: str("criterion") ?? "",
482
+ result: fields.result as never,
483
+ title: str("title") ?? "",
484
+ url: str("url"),
485
+ path: str("path"),
486
+ asOf: str("as_of"),
487
+ });
488
+ return print(doc, "error" in doc ? 1 : 0);
489
+ }
490
+
364
491
  async function runWorkHistory(ctx: CommandContext): Promise<number> {
365
492
  const { args } = ctx;
366
493
  const id = args.extraPositional2;
@@ -385,13 +512,14 @@ export async function runWorkspaceWork(ctx: CommandContext): Promise<number> {
385
512
  const { args } = ctx;
386
513
  const verb = args.extraPositional;
387
514
  if (verb === "history") return runWorkHistory(ctx);
515
+ if (verb === "evidence") return runWorkEvidence(ctx);
388
516
  const fail = (message: string): number => {
389
517
  if (args.json) console.log(JSON.stringify({ $schema: WORK_LEASE_OUTPUT_SCHEMA_ID, contract: WORK_LEASE_CONTRACT_VERSION, error: { code: "write-usage-invalid", message } }, null, 2));
390
518
  else console.error(formatError({ message, hint: USAGE }));
391
519
  return 1;
392
520
  };
393
521
  if (verb !== "claim" && verb !== "renew" && verb !== "release") {
394
- return fail(verb ? `chant workspace work takes claim, renew, release or history, not ${verb}` : "chant workspace work needs claim, renew, release or history");
522
+ return fail(verb ? `chant workspace work takes claim, renew, release, history or evidence, not ${verb}` : "chant workspace work needs claim, renew, release, history or evidence");
395
523
  }
396
524
  const id = args.extraPositional2;
397
525
  if (!id) return fail(`${verb} needs a work item id`);
@@ -182,9 +182,14 @@
182
182
  "amend-supersede-instead",
183
183
  "record-sign-failed",
184
184
  "ratify-quorum-not-met",
185
+ "record-conflict",
186
+ "write-lock-timeout",
187
+ "write-lock-not-held",
185
188
  "write-scope-member",
186
189
  "write-scope-kind",
190
+ "write-scope-class-unknown",
187
191
  "agent-unknown",
192
+ "principal-unidentified",
188
193
  "record-unparseable",
189
194
  "record-schema-invalid",
190
195
  "record-id-duplicate",
@@ -65,7 +65,7 @@
65
65
  },
66
66
  "outcome": {
67
67
  "type": "string",
68
- "description": "How the work ended, on a release. Free text such as done, not_done, gated or waiting."
68
+ "description": "How the work ended, on a release: one of the closed list in work-lease.schema.json's $defs/outcome (#3147), done, not_done, abandoned, gated, waiting, dropped, redraft or ask. A line written before #3147 may hold other free text."
69
69
  },
70
70
  "note": {
71
71
  "type": "string"
@@ -146,6 +146,17 @@
146
146
  }
147
147
  }
148
148
  ]
149
+ },
150
+ "attempt": {
151
+ "type": "boolean",
152
+ "description": "Added in contract 1 by #3147. Whether the claim counts toward the item's attempt limit: released not_done or abandoned, released with no outcome or with free text from before the outcomes were closed, or ended expired or lost. Never one that is held, and never one released done, gated, waiting, dropped, redraft or ask."
153
+ },
154
+ "kept": {
155
+ "type": [
156
+ "string",
157
+ "null"
158
+ ],
159
+ "description": "Added in contract 1 by #3147. The ref keeping the claim's unfinished work, refs/chant/kept/[<member>/]<item>/<token>, written when a run that changes the checkout ended not_done; null when there is none."
149
160
  }
150
161
  }
151
162
  },
@@ -300,6 +311,67 @@
300
311
  "description": "How many released claims gave each outcome."
301
312
  }
302
313
  }
314
+ },
315
+ "attempts": {
316
+ "type": "object",
317
+ "description": "Added in contract 1 by #3147. The item's attempts against its limit. limit is the item's own field named by the kind's work.attempts.field, else the kind's work.attempts.max, else null. A runner leaves an exhausted item to people.",
318
+ "required": [
319
+ "failed",
320
+ "limit",
321
+ "remaining",
322
+ "exhausted"
323
+ ],
324
+ "properties": {
325
+ "failed": {
326
+ "type": "integer",
327
+ "minimum": 0,
328
+ "description": "The claims whose attempt is true."
329
+ },
330
+ "limit": {
331
+ "type": [
332
+ "integer",
333
+ "null"
334
+ ],
335
+ "minimum": 1
336
+ },
337
+ "remaining": {
338
+ "type": [
339
+ "integer",
340
+ "null"
341
+ ],
342
+ "minimum": 0,
343
+ "description": "limit minus failed, never below 0; null without a limit."
344
+ },
345
+ "exhausted": {
346
+ "type": "boolean",
347
+ "description": "True once failed reaches the limit; false without one."
348
+ }
349
+ }
350
+ },
351
+ "kept": {
352
+ "type": "array",
353
+ "description": "Added in contract 1 by #3147. Every kept attempt of the item, from the local refs under refs/chant/kept/[<member>/]<item>/, sorted by ref.",
354
+ "items": {
355
+ "type": "object",
356
+ "required": [
357
+ "token",
358
+ "ref",
359
+ "commit"
360
+ ],
361
+ "properties": {
362
+ "token": {
363
+ "type": "string",
364
+ "description": "The lease token the attempt ran under."
365
+ },
366
+ "ref": {
367
+ "type": "string"
368
+ },
369
+ "commit": {
370
+ "type": "string",
371
+ "description": "The commit the ref points at."
372
+ }
373
+ }
374
+ }
303
375
  }
304
376
  }
305
377
  },
@@ -9,14 +9,14 @@ import { join } from "node:path";
9
9
  import { afterAll, beforeAll, describe, expect, test } from "vitest";
10
10
  import { cleanScratch, contract, git, REPO, repo } from "./__fixtures__/contract-repo";
11
11
  import { claimWorkLease, releaseWorkLease, renewWorkLease, type LeaseHistoryRecord } from "../lifecycle/work-lease";
12
- import { claimsOf, formatWorkHistory, workHistory, type WorkHistoryDocument } from "./work-cli";
12
+ import { claimsOf, formatWorkHistory, workHistory, workLease, type WorkHistoryDocument } from "./work-cli";
13
13
  import historySchema from "./work-history.schema.json";
14
14
 
15
15
  const REF = join(REPO, "reference-workspace");
16
16
  const { expectValid } = contract(historySchema);
17
17
 
18
- function work(id: string): string {
19
- const data = { schema: 1, id, title: `Work ${id}`, state: "open", implements: [], needs: [], constrains: ["path:app/server.mjs"], evidence: [], opened_on: "2026-09-25", source: { kind: "workspace", member: "app" }, supersedes: [] };
18
+ function work(id: string, extra: Record<string, unknown> = {}): string {
19
+ const data = { schema: 1, id, title: `Work ${id}`, state: "open", implements: [], needs: [], constrains: ["path:app/server.mjs"], evidence: [], opened_on: "2026-09-25", source: { kind: "workspace", member: "app" }, supersedes: [], ...extra };
20
20
  return `---\n${JSON.stringify(data, null, 2)}\n---\n\n# ${id}\n`;
21
21
  }
22
22
 
@@ -46,6 +46,7 @@ beforeAll(() => {
46
46
  "design/work/work.schema.json": readFileSync(join(REF, "work", "work.schema.json"), "utf-8"),
47
47
  "design/work/W-001-one.md": work("W-001"),
48
48
  "design/work/W-002-two.md": work("W-002"),
49
+ "design/work/W-004-four.md": work("W-004", { max_attempts: 2 }),
49
50
  });
50
51
  git(root, "config", "user.name", "History Test");
51
52
  git(root, "config", "user.email", "history@example.com");
@@ -88,6 +89,10 @@ describe("workspace work history (#2785)", () => {
88
89
  ]);
89
90
  expect(doc.events.map((e) => e.event)).toEqual(["claim", "renew", "release", "claim", "claim"]);
90
91
  expect(doc.summary).toEqual({ claims: 3, released: 1, held: 0, expired: 1, lost: 1, outcomes: { not_done: 1 } });
92
+ // Each of the three counts as an attempt (#3147): not_done, lost and expired, against the kind's default of 3.
93
+ expect(doc.claims.map((c) => c.attempt)).toEqual([true, true, true]);
94
+ expect(doc.attempts).toEqual({ failed: 3, limit: 3, remaining: 0, exhausted: true });
95
+ expect(formatWorkHistory(doc)).toContain("3 of 3 attempts used; left to people");
91
96
  // The history file is where git would find it, under the member's prefix.
92
97
  expect(git(root, "show", `chant/lifecycle:${doc.ledger.path}`).split("\n")).toHaveLength(5);
93
98
  expect(formatWorkHistory(doc)).toContain("released not_done by steward/dispatch@1");
@@ -107,6 +112,37 @@ describe("workspace work history (#2785)", () => {
107
112
  expect(doc.claims.map((c) => c.ended)).toEqual(["released", "held"]);
108
113
  expect(doc.lease).toMatchObject({ holder: "worker-b", token: live.lease.token, state: "active" });
109
114
  expect(doc.summary.outcomes).toEqual({ done: 1 });
115
+ expect(doc.attempts).toEqual({ failed: 0, limit: 3, remaining: 3, exhausted: false });
116
+ });
117
+
118
+ test("outcomes that built nothing don't count as attempts, the item's max_attempts overrides the kind's, and kept attempts are listed (#3147)", async () => {
119
+ const now = () => new Date();
120
+ const tokens: string[] = [];
121
+ for (const outcome of ["dropped", "redraft", "ask", "waiting", "gated", "not_done"]) {
122
+ const c = await claimWorkLease("W-004", "worker-a", { cwd: kindDir, now });
123
+ if (!c.ok) throw new Error(c.message);
124
+ tokens.push(c.lease.token);
125
+ await releaseWorkLease("W-004", "worker-a", { cwd: kindDir, now, token: c.lease.token, outcome });
126
+ }
127
+ // The not_done attempt's work, kept the way a run that changes the checkout keeps it.
128
+ const kept = `refs/chant/kept/design/W-004/${tokens[5]}`;
129
+ git(root, "update-ref", kept, "HEAD");
130
+
131
+ const doc = await workHistory({ id: "W-004", cwd: root });
132
+ expectValid(doc);
133
+ if ("error" in doc) throw new Error(doc.error.message);
134
+ expect(doc.claims.map((c) => c.attempt)).toEqual([false, false, false, false, false, true]);
135
+ expect(doc.attempts).toEqual({ failed: 1, limit: 2, remaining: 1, exhausted: false });
136
+ expect(doc.claims.map((c) => c.kept)).toEqual([null, null, null, null, null, kept]);
137
+ expect(doc.kept).toEqual([{ token: tokens[5], ref: kept, commit: git(root, "rev-parse", "HEAD").trim() }]);
138
+ expect(formatWorkHistory(doc)).toContain(`kept: ${kept}`);
139
+ });
140
+
141
+ test("a release outside the closed list of outcomes is refused (#3147)", async () => {
142
+ await expect(releaseWorkLease("W-002", "worker-b", { cwd: kindDir, outcome: "skipped" })).rejects.toThrow(/not a work lease outcome/);
143
+ const doc = await workLease({ verb: "release", id: "W-002", holder: "worker-b", cwd: root, outcome: "skipped" });
144
+ expect(doc).toMatchObject({ error: { code: "write-usage-invalid" } });
145
+ expect((doc as { error: { message: string } }).error.message).toMatch(/done, not_done, abandoned, gated, waiting, dropped, redraft, ask/);
110
146
  });
111
147
 
112
148
  test("an item never claimed has no claims; an unknown item is an error", async () => {
@@ -142,12 +178,19 @@ describe("claimsOf", () => {
142
178
  });
143
179
 
144
180
  test("a release by someone closing out an expired lease keeps the holder and records who released it", () => {
145
- const claims = claimsOf([e("claim", "a"), e("release", "a", { by: "janitor", outcome: "expired" })], null);
181
+ const claims = claimsOf([e("claim", "a"), e("release", "a", { by: "janitor", outcome: "abandoned" })], null);
146
182
  expect(claims).toEqual([
147
- { token: "a", holder: "h-a", acquiredAt: "2026-09-25T00:00:00.000Z", expiresAt: "2026-09-25T00:10:00.000Z", renewals: 0, ended: "released", release: { by: "janitor", at: "2026-09-25T00:00:00.000Z", outcome: "expired", note: null } },
183
+ { token: "a", holder: "h-a", acquiredAt: "2026-09-25T00:00:00.000Z", expiresAt: "2026-09-25T00:10:00.000Z", renewals: 0, ended: "released", release: { by: "janitor", at: "2026-09-25T00:00:00.000Z", outcome: "abandoned", note: null }, attempt: true, kept: null },
148
184
  ]);
149
185
  });
150
186
 
187
+ test("a release with free text from before the outcomes were closed counts as an attempt, and one with no outcome too", () => {
188
+ expect(claimsOf([e("claim", "a"), e("release", "a", { outcome: "skipped" })], null)[0].attempt).toBe(true);
189
+ expect(claimsOf([e("claim", "a"), e("release", "a")], null)[0].attempt).toBe(true);
190
+ expect(claimsOf([e("claim", "a"), e("release", "a", { outcome: "done" })], null)[0].attempt).toBe(false);
191
+ expect(claimsOf([e("claim", "a")], { token: "a", state: "active" })[0].attempt).toBe(false);
192
+ });
193
+
151
194
  test("the live ref decides between held and expired for the newest claim", () => {
152
195
  expect(claimsOf([e("claim", "a")], { token: "a", state: "active" })[0].ended).toBe("held");
153
196
  expect(claimsOf([e("claim", "a")], { token: "a", state: "expired" })[0].ended).toBe("expired");