@intentius/chant 0.100.0 → 0.102.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 (608) 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/discover.d.ts.map +1 -1
  4. package/dist/audit/nginx.d.ts.map +1 -1
  5. package/dist/audit/prior-art.d.ts +48 -0
  6. package/dist/audit/prior-art.d.ts.map +1 -1
  7. package/dist/behaviour-kinds.d.ts.map +1 -1
  8. package/dist/build.d.ts +6 -0
  9. package/dist/build.d.ts.map +1 -1
  10. package/dist/builder.d.ts.map +1 -1
  11. package/dist/cli/build-options.d.ts +2 -0
  12. package/dist/cli/build-options.d.ts.map +1 -1
  13. package/dist/cli/commands/build.d.ts.map +1 -1
  14. package/dist/cli/commands/carve-emit.d.ts.map +1 -1
  15. package/dist/cli/commands/check-lexicon.d.ts +16 -0
  16. package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
  17. package/dist/cli/commands/import.d.ts +8 -0
  18. package/dist/cli/commands/import.d.ts.map +1 -1
  19. package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
  20. package/dist/cli/commands/init.d.ts +19 -0
  21. package/dist/cli/commands/init.d.ts.map +1 -1
  22. package/dist/cli/commands/onboard.d.ts +9 -0
  23. package/dist/cli/commands/onboard.d.ts.map +1 -1
  24. package/dist/cli/commands/pinned-upgrade.d.ts +19 -5
  25. package/dist/cli/commands/pinned-upgrade.d.ts.map +1 -1
  26. package/dist/cli/handlers/dev.d.ts.map +1 -1
  27. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  28. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  29. package/dist/cli/handlers/misc.d.ts.map +1 -1
  30. package/dist/cli/handlers/operator.d.ts +9 -0
  31. package/dist/cli/handlers/operator.d.ts.map +1 -1
  32. package/dist/cli/handlers/run.d.ts.map +1 -1
  33. package/dist/cli/lsp/server.d.ts.map +1 -1
  34. package/dist/cli/main.d.ts.map +1 -1
  35. package/dist/cli/mcp/server.d.ts.map +1 -1
  36. package/dist/cli/mcp/tools/build.d.ts.map +1 -1
  37. package/dist/cli/mcp/tools/composites.d.ts.map +1 -1
  38. package/dist/cli/mcp/tools/explain.d.ts.map +1 -1
  39. package/dist/cli/mcp/tools/import.d.ts.map +1 -1
  40. package/dist/cli/mcp/tools/lint.d.ts.map +1 -1
  41. package/dist/cli/mcp/tools/scaffold.d.ts.map +1 -1
  42. package/dist/cli/mcp/tools/search.d.ts.map +1 -1
  43. package/dist/cli/mcp/workspace-tools.d.ts +9 -1
  44. package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
  45. package/dist/cli/registry.d.ts +33 -2
  46. package/dist/cli/registry.d.ts.map +1 -1
  47. package/dist/codegen/fetch.d.ts.map +1 -1
  48. package/dist/codegen/naming.d.ts.map +1 -1
  49. package/dist/codegen/pinned-upgrade.d.ts +67 -3
  50. package/dist/codegen/pinned-upgrade.d.ts.map +1 -1
  51. package/dist/codegen/registry.d.ts.map +1 -1
  52. package/dist/components/capability-plugin-loader.d.ts.map +1 -1
  53. package/dist/components/capability.d.ts.map +1 -1
  54. package/dist/components/driver.d.ts.map +1 -1
  55. package/dist/components/fan-out.d.ts.map +1 -1
  56. package/dist/components/starter-plugin.d.ts.map +1 -1
  57. package/dist/components/verbs/process-runner.d.ts.map +1 -1
  58. package/dist/components/verbs/r2-sync.d.ts.map +1 -1
  59. package/dist/components/verbs/run-agent.d.ts.map +1 -1
  60. package/dist/components/verbs/sbom-generator.d.ts.map +1 -1
  61. package/dist/components/verbs/sign.d.ts.map +1 -1
  62. package/dist/components/verbs/verify.d.ts.map +1 -1
  63. package/dist/components/verbs/vuln-gate.d.ts.map +1 -1
  64. package/dist/components/verbs/vuln-scan.d.ts.map +1 -1
  65. package/dist/components/verbs/wrangler.d.ts.map +1 -1
  66. package/dist/composite.d.ts.map +1 -1
  67. package/dist/config.d.ts +30 -7
  68. package/dist/config.d.ts.map +1 -1
  69. package/dist/declarable.d.ts +16 -0
  70. package/dist/declarable.d.ts.map +1 -1
  71. package/dist/discovery/cache.d.ts.map +1 -1
  72. package/dist/discovery/fold-import.d.ts +15 -5
  73. package/dist/discovery/fold-import.d.ts.map +1 -1
  74. package/dist/discovery/param-deps.d.ts +16 -0
  75. package/dist/discovery/param-deps.d.ts.map +1 -1
  76. package/dist/discovery/sandbox/policy-wire.d.ts.map +1 -1
  77. package/dist/discovery/specifier-resolve.d.ts +37 -0
  78. package/dist/discovery/specifier-resolve.d.ts.map +1 -0
  79. package/dist/errors.d.ts.map +1 -1
  80. package/dist/fold/fold.d.ts +3 -1
  81. package/dist/fold/fold.d.ts.map +1 -1
  82. package/dist/graph-detail.d.ts.map +1 -1
  83. package/dist/graph-ir.d.ts.map +1 -1
  84. package/dist/graph-layout.d.ts.map +1 -1
  85. package/dist/import/base-parser.d.ts.map +1 -1
  86. package/dist/import/embedded.d.ts +7 -0
  87. package/dist/import/embedded.d.ts.map +1 -1
  88. package/dist/import/parser-options.d.ts +14 -0
  89. package/dist/import/parser-options.d.ts.map +1 -0
  90. package/dist/import/parser.d.ts +17 -0
  91. package/dist/import/parser.d.ts.map +1 -1
  92. package/dist/kubectl-context.d.ts.map +1 -1
  93. package/dist/lexicon-output.d.ts.map +1 -1
  94. package/dist/lexicon-schema.d.ts +3 -3
  95. package/dist/lexicon.d.ts +78 -3
  96. package/dist/lexicon.d.ts.map +1 -1
  97. package/dist/lifecycle/assert-live.d.ts.map +1 -1
  98. package/dist/lifecycle/gate-ledger.d.ts +15 -0
  99. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  100. package/dist/lifecycle/git.d.ts.map +1 -1
  101. package/dist/lifecycle/member-ledger.d.ts.map +1 -1
  102. package/dist/lifecycle/observe.d.ts.map +1 -1
  103. package/dist/lifecycle/plan-digest.d.ts +26 -5
  104. package/dist/lifecycle/plan-digest.d.ts.map +1 -1
  105. package/dist/lifecycle/plan-ledger.d.ts.map +1 -1
  106. package/dist/lifecycle/release-ledger.d.ts.map +1 -1
  107. package/dist/lifecycle/scenario.d.ts.map +1 -1
  108. package/dist/lifecycle/status.d.ts.map +1 -1
  109. package/dist/lifecycle/work-lease.d.ts +61 -2
  110. package/dist/lifecycle/work-lease.d.ts.map +1 -1
  111. package/dist/lint/config.d.ts +4 -4
  112. package/dist/lint/engine.d.ts +6 -0
  113. package/dist/lint/engine.d.ts.map +1 -1
  114. package/dist/lsp/lexicon-providers.d.ts.map +1 -1
  115. package/dist/op/activities/activity-contracts.d.ts +12 -11
  116. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  117. package/dist/op/activities/apply.d.ts +79 -9
  118. package/dist/op/activities/apply.d.ts.map +1 -1
  119. package/dist/op/activities/decide.d.ts.map +1 -1
  120. package/dist/op/activities/index.d.ts +1 -1
  121. package/dist/op/activities/index.d.ts.map +1 -1
  122. package/dist/op/activities/lexicon-upgrade.d.ts +44 -12
  123. package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -1
  124. package/dist/op/activities/propose-upgrade.d.ts +2 -0
  125. package/dist/op/activities/propose-upgrade.d.ts.map +1 -1
  126. package/dist/op/activity-contract.d.ts +1 -1
  127. package/dist/op/activity-failure.d.ts.map +1 -1
  128. package/dist/op/activity-profiles.d.ts.map +1 -1
  129. package/dist/op/builders.d.ts.map +1 -1
  130. package/dist/op/composites/apply-op.d.ts +12 -5
  131. package/dist/op/composites/apply-op.d.ts.map +1 -1
  132. package/dist/op/composites/lexicon-upgrade-op.d.ts +22 -6
  133. package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -1
  134. package/dist/op/decide-backend.d.ts.map +1 -1
  135. package/dist/op/decide-read-inputs.d.ts.map +1 -1
  136. package/dist/op/emulator-lifecycle.d.ts +21 -2
  137. package/dist/op/emulator-lifecycle.d.ts.map +1 -1
  138. package/dist/op/gate-approval.d.ts +5 -1
  139. package/dist/op/gate-approval.d.ts.map +1 -1
  140. package/dist/op/gate-wait.d.ts.map +1 -1
  141. package/dist/op/gate.d.ts +19 -3
  142. package/dist/op/gate.d.ts.map +1 -1
  143. package/dist/op/index.d.ts +3 -1
  144. package/dist/op/index.d.ts.map +1 -1
  145. package/dist/op/local-executor.d.ts +6 -0
  146. package/dist/op/local-executor.d.ts.map +1 -1
  147. package/dist/op/operator.d.ts.map +1 -1
  148. package/dist/op/run-context.d.ts +39 -0
  149. package/dist/op/run-context.d.ts.map +1 -0
  150. package/dist/op/run-live.d.ts.map +1 -1
  151. package/dist/op/runtimes/local.d.ts.map +1 -1
  152. package/dist/op/source-archive.d.ts.map +1 -1
  153. package/dist/op/steward-beside.d.ts.map +1 -1
  154. package/dist/op/steward-points.d.ts.map +1 -1
  155. package/dist/op/steward.d.ts.map +1 -1
  156. package/dist/op/types.d.ts +8 -3
  157. package/dist/op/types.d.ts.map +1 -1
  158. package/dist/op/work-lease-run.d.ts +24 -4
  159. package/dist/op/work-lease-run.d.ts.map +1 -1
  160. package/dist/project-root.d.ts.map +1 -1
  161. package/dist/provenance.d.ts +21 -0
  162. package/dist/provenance.d.ts.map +1 -1
  163. package/dist/pseudo-parameter.d.ts.map +1 -1
  164. package/dist/reconcile.d.ts.map +1 -1
  165. package/dist/secret-materialization.d.ts.map +1 -1
  166. package/dist/serializer.d.ts +8 -0
  167. package/dist/serializer.d.ts.map +1 -1
  168. package/dist/telemetry-attribution.d.ts +77 -0
  169. package/dist/telemetry-attribution.d.ts.map +1 -0
  170. package/dist/terraform/emit-source.d.ts.map +1 -1
  171. package/dist/terraform/parse.d.ts.map +1 -1
  172. package/dist/testing.d.ts.map +1 -1
  173. package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -1
  174. package/dist/workspace/__fixtures__/decide-workspace.d.ts +1 -1
  175. package/dist/workspace/__fixtures__/decide-workspace.d.ts.map +1 -1
  176. package/dist/workspace/agent-cli.d.ts +88 -0
  177. package/dist/workspace/agent-cli.d.ts.map +1 -0
  178. package/dist/workspace/box-factory.d.ts +73 -0
  179. package/dist/workspace/box-factory.d.ts.map +1 -0
  180. package/dist/workspace/changes-cli.d.ts.map +1 -1
  181. package/dist/workspace/changes.d.ts +8 -1
  182. package/dist/workspace/changes.d.ts.map +1 -1
  183. package/dist/workspace/chant-migrations/chud-lexicon-exit.d.ts.map +1 -1
  184. package/dist/workspace/checks/boxes.d.ts.map +1 -1
  185. package/dist/workspace/checks/links.d.ts +1 -0
  186. package/dist/workspace/checks/links.d.ts.map +1 -1
  187. package/dist/workspace/checks/live.d.ts +40 -0
  188. package/dist/workspace/checks/live.d.ts.map +1 -0
  189. package/dist/workspace/checks.d.ts +26 -2
  190. package/dist/workspace/checks.d.ts.map +1 -1
  191. package/dist/workspace/compose-graph.d.ts +73 -0
  192. package/dist/workspace/compose-graph.d.ts.map +1 -1
  193. package/dist/workspace/conformance/index.d.ts +67 -6
  194. package/dist/workspace/conformance/index.d.ts.map +1 -1
  195. package/dist/workspace/conformance/vitest.d.ts +12 -3
  196. package/dist/workspace/conformance/vitest.d.ts.map +1 -1
  197. package/dist/workspace/conformance/writer.d.ts +368 -0
  198. package/dist/workspace/conformance/writer.d.ts.map +1 -0
  199. package/dist/workspace/decide.d.ts +1 -1
  200. package/dist/workspace/decide.d.ts.map +1 -1
  201. package/dist/workspace/declaration.d.ts +119 -0
  202. package/dist/workspace/declaration.d.ts.map +1 -1
  203. package/dist/workspace/declaration.schema.json +368 -4
  204. package/dist/workspace/export-cli.d.ts +12 -0
  205. package/dist/workspace/export-cli.d.ts.map +1 -0
  206. package/dist/workspace/export.d.ts +145 -0
  207. package/dist/workspace/export.d.ts.map +1 -0
  208. package/dist/workspace/generated-files.d.ts.map +1 -1
  209. package/dist/workspace/graph-cache.d.ts.map +1 -1
  210. package/dist/workspace/graph-cli.d.ts +2 -0
  211. package/dist/workspace/graph-cli.d.ts.map +1 -1
  212. package/dist/workspace/identity.d.ts +111 -0
  213. package/dist/workspace/identity.d.ts.map +1 -0
  214. package/dist/workspace/import.d.ts +73 -0
  215. package/dist/workspace/import.d.ts.map +1 -0
  216. package/dist/workspace/intent-cli.d.ts +7 -4
  217. package/dist/workspace/intent-cli.d.ts.map +1 -1
  218. package/dist/workspace/intent-record.d.ts +8 -1
  219. package/dist/workspace/intent-record.d.ts.map +1 -1
  220. package/dist/workspace/intent.d.ts +149 -5
  221. package/dist/workspace/intent.d.ts.map +1 -1
  222. package/dist/workspace/kinds.d.ts +24 -6
  223. package/dist/workspace/kinds.d.ts.map +1 -1
  224. package/dist/workspace/lineage-adopt-cli.d.ts +15 -0
  225. package/dist/workspace/lineage-adopt-cli.d.ts.map +1 -0
  226. package/dist/workspace/lineage-adopt.d.ts +106 -0
  227. package/dist/workspace/lineage-adopt.d.ts.map +1 -0
  228. package/dist/workspace/lineage-check.d.ts +9 -2
  229. package/dist/workspace/lineage-check.d.ts.map +1 -1
  230. package/dist/workspace/lineage-cli.d.ts +6 -1
  231. package/dist/workspace/lineage-cli.d.ts.map +1 -1
  232. package/dist/workspace/lineage-hash-index.d.ts +110 -0
  233. package/dist/workspace/lineage-hash-index.d.ts.map +1 -0
  234. package/dist/workspace/lineage-init.d.ts +10 -0
  235. package/dist/workspace/lineage-init.d.ts.map +1 -1
  236. package/dist/workspace/lineage-lock.d.ts +149 -2
  237. package/dist/workspace/lineage-lock.d.ts.map +1 -1
  238. package/dist/workspace/lineage-migrations.d.ts +15 -3
  239. package/dist/workspace/lineage-migrations.d.ts.map +1 -1
  240. package/dist/workspace/lineage-provenance.d.ts +18 -0
  241. package/dist/workspace/lineage-provenance.d.ts.map +1 -0
  242. package/dist/workspace/lineage-upgrade-cli.d.ts +2 -0
  243. package/dist/workspace/lineage-upgrade-cli.d.ts.map +1 -1
  244. package/dist/workspace/lineage-upgrade.d.ts +30 -1
  245. package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
  246. package/dist/workspace/lineage-versions.d.ts +116 -0
  247. package/dist/workspace/lineage-versions.d.ts.map +1 -0
  248. package/dist/workspace/links.d.ts +31 -5
  249. package/dist/workspace/links.d.ts.map +1 -1
  250. package/dist/workspace/ls-generated.d.ts +37 -0
  251. package/dist/workspace/ls-generated.d.ts.map +1 -0
  252. package/dist/workspace/ls.d.ts +4 -0
  253. package/dist/workspace/ls.d.ts.map +1 -1
  254. package/dist/workspace/member-commands.d.ts.map +1 -1
  255. package/dist/workspace/nested-graph.d.ts +56 -0
  256. package/dist/workspace/nested-graph.d.ts.map +1 -0
  257. package/dist/workspace/nesting.d.ts +21 -0
  258. package/dist/workspace/nesting.d.ts.map +1 -0
  259. package/dist/workspace/pin-cli.d.ts +10 -0
  260. package/dist/workspace/pin-cli.d.ts.map +1 -0
  261. package/dist/workspace/pin-integrity.d.ts +51 -0
  262. package/dist/workspace/pin-integrity.d.ts.map +1 -0
  263. package/dist/workspace/points.d.ts.map +1 -1
  264. package/dist/workspace/principal-classes.d.ts +114 -0
  265. package/dist/workspace/principal-classes.d.ts.map +1 -0
  266. package/dist/workspace/reason-codes.d.ts +47 -3
  267. package/dist/workspace/reason-codes.d.ts.map +1 -1
  268. package/dist/workspace/record-assets.d.ts.map +1 -1
  269. package/dist/workspace/record-sessions.d.ts +7 -11
  270. package/dist/workspace/record-sessions.d.ts.map +1 -1
  271. package/dist/workspace/records-checkout.d.ts +54 -0
  272. package/dist/workspace/records-checkout.d.ts.map +1 -0
  273. package/dist/workspace/records-cli.d.ts +57 -2
  274. package/dist/workspace/records-cli.d.ts.map +1 -1
  275. package/dist/workspace/records-close.d.ts +5 -2
  276. package/dist/workspace/records-close.d.ts.map +1 -1
  277. package/dist/workspace/records-since.d.ts.map +1 -1
  278. package/dist/workspace/records-write.d.ts +23 -4
  279. package/dist/workspace/records-write.d.ts.map +1 -1
  280. package/dist/workspace/records.d.ts +58 -5
  281. package/dist/workspace/records.d.ts.map +1 -1
  282. package/dist/workspace/returns.d.ts +129 -0
  283. package/dist/workspace/returns.d.ts.map +1 -0
  284. package/dist/workspace/runs-cli.d.ts +164 -0
  285. package/dist/workspace/runs-cli.d.ts.map +1 -0
  286. package/dist/workspace/runs.d.ts +423 -0
  287. package/dist/workspace/runs.d.ts.map +1 -0
  288. package/dist/workspace/source-block.d.ts +1 -1
  289. package/dist/workspace/source-block.d.ts.map +1 -1
  290. package/dist/workspace/status-gates.d.ts +15 -3
  291. package/dist/workspace/status-gates.d.ts.map +1 -1
  292. package/dist/workspace/status.d.ts +10 -0
  293. package/dist/workspace/status.d.ts.map +1 -1
  294. package/dist/workspace/symbols.d.ts +52 -0
  295. package/dist/workspace/symbols.d.ts.map +1 -0
  296. package/dist/workspace/template-manifest.d.ts +11 -3
  297. package/dist/workspace/template-manifest.d.ts.map +1 -1
  298. package/dist/workspace/trailer-joins.d.ts +46 -0
  299. package/dist/workspace/trailer-joins.d.ts.map +1 -0
  300. package/dist/workspace/trailers.d.ts +89 -0
  301. package/dist/workspace/trailers.d.ts.map +1 -0
  302. package/dist/workspace/trust/attestor.d.ts +8 -0
  303. package/dist/workspace/trust/attestor.d.ts.map +1 -1
  304. package/dist/workspace/trust/dsse.d.ts +58 -0
  305. package/dist/workspace/trust/dsse.d.ts.map +1 -0
  306. package/dist/workspace/trust/evidence-cli.d.ts +66 -0
  307. package/dist/workspace/trust/evidence-cli.d.ts.map +1 -0
  308. package/dist/workspace/trust/evidence.d.ts +93 -0
  309. package/dist/workspace/trust/evidence.d.ts.map +1 -0
  310. package/dist/workspace/trust/policy.d.ts +54 -1
  311. package/dist/workspace/trust/policy.d.ts.map +1 -1
  312. package/dist/workspace/trust/provenance.d.ts +21 -1
  313. package/dist/workspace/trust/provenance.d.ts.map +1 -1
  314. package/dist/workspace/trust/rotation.d.ts +132 -0
  315. package/dist/workspace/trust/rotation.d.ts.map +1 -0
  316. package/dist/workspace/trust/seal.d.ts +33 -0
  317. package/dist/workspace/trust/seal.d.ts.map +1 -1
  318. package/dist/workspace/trust/signers-cli.d.ts +49 -0
  319. package/dist/workspace/trust/signers-cli.d.ts.map +1 -0
  320. package/dist/workspace/trust/ssh-commit.d.ts +12 -0
  321. package/dist/workspace/trust/ssh-commit.d.ts.map +1 -1
  322. package/dist/workspace/trust/test-repo.d.ts +13 -0
  323. package/dist/workspace/trust/test-repo.d.ts.map +1 -1
  324. package/dist/workspace/trust/verify.d.ts +15 -0
  325. package/dist/workspace/trust/verify.d.ts.map +1 -1
  326. package/dist/workspace/work-cli.d.ts +39 -3
  327. package/dist/workspace/work-cli.d.ts.map +1 -1
  328. package/dist/workspace/work-evidence.d.ts +1 -1
  329. package/dist/workspace/work-evidence.d.ts.map +1 -1
  330. package/dist/workspace/work.d.ts +26 -1
  331. package/dist/workspace/work.d.ts.map +1 -1
  332. package/dist/workspace/workspace-principals.schema.json +60 -0
  333. package/dist/workspace/write-scope.d.ts +259 -0
  334. package/dist/workspace/write-scope.d.ts.map +1 -0
  335. package/dist/yaml.d.ts +21 -39
  336. package/dist/yaml.d.ts.map +1 -1
  337. package/package.json +2 -2
  338. package/src/audit/prior-art.ts +15 -0
  339. package/src/build.ts +8 -0
  340. package/src/cli/build-options.ts +4 -1
  341. package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +1 -1
  342. package/src/cli/commands/build.ts +2 -0
  343. package/src/cli/commands/carve-emit-state.test.ts +4 -0
  344. package/src/cli/commands/carve-emit.ts +5 -19
  345. package/src/cli/commands/check-lexicon.test.ts +62 -1
  346. package/src/cli/commands/check-lexicon.ts +36 -6
  347. package/src/cli/commands/import-live.test.ts +87 -1
  348. package/src/cli/commands/import-parser-options.test.ts +133 -0
  349. package/src/cli/commands/import.ts +69 -25
  350. package/src/cli/commands/init-lexicon/templates/project.ts +1 -1
  351. package/src/cli/commands/init.test.ts +8 -1
  352. package/src/cli/commands/init.ts +23 -8
  353. package/src/cli/commands/lint.test.ts +37 -0
  354. package/src/cli/commands/onboard.test.ts +53 -3
  355. package/src/cli/commands/onboard.ts +52 -4
  356. package/src/cli/commands/pinned-upgrade.ts +73 -33
  357. package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +2 -2
  358. package/src/cli/handlers/components.ts +2 -2
  359. package/src/cli/handlers/dev.ts +8 -6
  360. package/src/cli/handlers/fan-out.test.ts +6 -6
  361. package/src/cli/handlers/fan-out.ts +2 -1
  362. package/src/cli/handlers/graph.test.ts +42 -0
  363. package/src/cli/handlers/graph.ts +22 -0
  364. package/src/cli/handlers/lifecycle.test.ts +67 -0
  365. package/src/cli/handlers/lifecycle.ts +6 -5
  366. package/src/cli/handlers/misc.ts +2 -0
  367. package/src/cli/handlers/operator.ts +88 -4
  368. package/src/cli/handlers/run.test.ts +111 -0
  369. package/src/cli/handlers/run.ts +85 -24
  370. package/src/cli/handlers/scenario.ts +2 -2
  371. package/src/cli/main.test.ts +33 -0
  372. package/src/cli/main.ts +168 -17
  373. package/src/cli/mcp/workspace-tools.test.ts +5 -2
  374. package/src/cli/mcp/workspace-tools.ts +62 -4
  375. package/src/cli/registry.ts +33 -2
  376. package/src/cli/serve-mcp-workspace.test.ts +1 -1
  377. package/src/codegen/pinned-upgrade-multi.test.ts +157 -0
  378. package/src/codegen/pinned-upgrade-tags.test.ts +112 -0
  379. package/src/codegen/pinned-upgrade.test.ts +104 -0
  380. package/src/codegen/pinned-upgrade.ts +250 -56
  381. package/src/codegen/release-wiring.test.ts +5 -1
  382. package/src/codegen/testdata/postgres-tags.json +242 -0
  383. package/src/components/fan-out-output.test.ts +1 -1
  384. package/src/components/fan-out.test.ts +1 -1
  385. package/src/components/promote.test.ts +1 -1
  386. package/src/config.test.ts +21 -0
  387. package/src/config.ts +27 -0
  388. package/src/content-digest.test.ts +2 -2
  389. package/src/declarable.test.ts +21 -0
  390. package/src/declarable.ts +19 -0
  391. package/src/discovery/fold-composite.test.ts +41 -5
  392. package/src/discovery/fold-import.test.ts +129 -0
  393. package/src/discovery/fold-import.ts +643 -131
  394. package/src/discovery/fold-package-composite.test.ts +264 -0
  395. package/src/discovery/fold-specifiers.test.ts +191 -0
  396. package/src/discovery/param-deps.test.ts +66 -1
  397. package/src/discovery/param-deps.ts +45 -0
  398. package/src/discovery/specifier-resolve.ts +344 -0
  399. package/src/fold/fold.test.ts +39 -1
  400. package/src/fold/fold.ts +83 -9
  401. package/src/fold-provenance.test.ts +140 -0
  402. package/src/graph-ir.test.ts +83 -0
  403. package/src/graph-ir.ts +29 -0
  404. package/src/import/embedded.test.ts +20 -0
  405. package/src/import/embedded.ts +13 -0
  406. package/src/import/parser-options.ts +46 -0
  407. package/src/import/parser.ts +20 -0
  408. package/src/lexicon.ts +79 -3
  409. package/src/lifecycle/gate-ledger.test.ts +14 -0
  410. package/src/lifecycle/gate-ledger.ts +18 -1
  411. package/src/lifecycle/observe.test.ts +18 -0
  412. package/src/lifecycle/observe.ts +6 -4
  413. package/src/lifecycle/plan-digest.test.ts +54 -3
  414. package/src/lifecycle/plan-digest.ts +38 -8
  415. package/src/lifecycle/snapshot.ts +2 -2
  416. package/src/lifecycle/work-lease.test.ts +1 -1
  417. package/src/lifecycle/work-lease.ts +93 -2
  418. package/src/lint/engine.test.ts +55 -0
  419. package/src/lint/engine.ts +30 -4
  420. package/src/lint/output-docs.test.ts +59 -56
  421. package/src/op/activities/activity-contracts.ts +1 -0
  422. package/src/op/activities/apply.test.ts +189 -5
  423. package/src/op/activities/apply.ts +230 -8
  424. package/src/op/activities/index.ts +3 -0
  425. package/src/op/activities/lexicon-upgrade.test.ts +160 -6
  426. package/src/op/activities/lexicon-upgrade.ts +154 -51
  427. package/src/op/activities/propose-upgrade.ts +8 -5
  428. package/src/op/composites/apply-op.ts +12 -5
  429. package/src/op/composites/composites.test.ts +30 -3
  430. package/src/op/composites/lexicon-upgrade-op.test.ts +7 -2
  431. package/src/op/composites/lexicon-upgrade-op.ts +29 -8
  432. package/src/op/emulator-lifecycle.test.ts +23 -0
  433. package/src/op/emulator-lifecycle.ts +52 -14
  434. package/src/op/gate-approval.test.ts +91 -0
  435. package/src/op/gate-approval.ts +5 -1
  436. package/src/op/gate.ts +34 -8
  437. package/src/op/index.ts +3 -1
  438. package/src/op/local-executor.test.ts +15 -0
  439. package/src/op/local-executor.ts +40 -12
  440. package/src/op/local-output.ts +1 -1
  441. package/src/op/operator.ts +4 -0
  442. package/src/op/run-context.test.ts +141 -0
  443. package/src/op/run-context.ts +51 -0
  444. package/src/op/runtimes/local.test.ts +14 -0
  445. package/src/op/runtimes/local.ts +2 -0
  446. package/src/op/steward-beside.ts +2 -0
  447. package/src/op/types.ts +8 -3
  448. package/src/op/work-lease-run.test.ts +47 -0
  449. package/src/op/work-lease-run.ts +73 -11
  450. package/src/provenance.ts +35 -0
  451. package/src/serializer.ts +9 -0
  452. package/src/telemetry-attribution.test.ts +91 -0
  453. package/src/telemetry-attribution.ts +145 -0
  454. package/src/workspace/__fixtures__/studio-template-84c8b21.json +1 -1
  455. package/src/workspace/agent-cli.ts +138 -0
  456. package/src/workspace/agent.schema.json +378 -0
  457. package/src/workspace/behold-kinds.test.ts +1 -1
  458. package/src/workspace/box-factory.test.ts +320 -0
  459. package/src/workspace/box-factory.ts +103 -0
  460. package/src/workspace/changes-cli.ts +5 -0
  461. package/src/workspace/changes.schema.json +171 -1
  462. package/src/workspace/changes.ts +61 -4
  463. package/src/workspace/check-live.test.ts +192 -0
  464. package/src/workspace/check.schema.json +64 -0
  465. package/src/workspace/checks/boxes.test.ts +2 -0
  466. package/src/workspace/checks/links.ts +23 -2
  467. package/src/workspace/checks/live.ts +113 -0
  468. package/src/workspace/checks.test.ts +2 -2
  469. package/src/workspace/checks.ts +49 -8
  470. package/src/workspace/compose-graph.test.ts +47 -0
  471. package/src/workspace/compose-graph.ts +127 -3
  472. package/src/workspace/conformance/__fixture__/decisions/decision.kind.mjs +7 -1
  473. package/src/workspace/conformance/__fixture__/decisions/decision.schema.json +5 -0
  474. package/src/workspace/conformance/__writer_fixture__/answers/answer.kind.mjs +27 -0
  475. package/src/workspace/conformance/__writer_fixture__/answers/answer.schema.json +95 -0
  476. package/src/workspace/conformance/__writer_fixture__/decisions/points.json +132 -0
  477. package/src/workspace/conformance/__writer_fixture__/sessions/session.kind.mjs +48 -0
  478. package/src/workspace/conformance/__writer_fixture__/sessions/session.schema.json +108 -0
  479. package/src/workspace/conformance/__writer_fixture__/work/W-001-the-writer-suite-works-this-item.md +25 -0
  480. package/src/workspace/conformance/__writer_fixture__/work/work.kind.mjs +60 -0
  481. package/src/workspace/conformance/__writer_fixture__/work/work.schema.json +578 -0
  482. package/src/workspace/conformance/conformance.e2e.test.ts +35 -3
  483. package/src/workspace/conformance/index.mjs +30 -3
  484. package/src/workspace/conformance/index.ts +103 -15
  485. package/src/workspace/conformance/vitest.ts +44 -2
  486. package/src/workspace/conformance/writer-conformance.e2e.test.ts +131 -0
  487. package/src/workspace/conformance/writer.test.ts +156 -0
  488. package/src/workspace/conformance/writer.ts +880 -0
  489. package/src/workspace/decide.ts +7 -1
  490. package/src/workspace/declaration.schema.json +368 -4
  491. package/src/workspace/declaration.ts +270 -1
  492. package/src/workspace/declared-kinds.test.ts +33 -0
  493. package/src/workspace/evidence.schema.json +279 -0
  494. package/src/workspace/export-cli.ts +180 -0
  495. package/src/workspace/export-import.test.ts +282 -0
  496. package/src/workspace/export.ts +486 -0
  497. package/src/workspace/graph-cli.ts +51 -2
  498. package/src/workspace/graph-contract.test.ts +89 -4
  499. package/src/workspace/graph.schema.json +453 -1
  500. package/src/workspace/identity.test.ts +196 -0
  501. package/src/workspace/identity.ts +186 -0
  502. package/src/workspace/import.ts +325 -0
  503. package/src/workspace/intent-cli.ts +64 -10
  504. package/src/workspace/intent-record.schema.json +208 -0
  505. package/src/workspace/intent-record.ts +35 -1
  506. package/src/workspace/intent-why.test.ts +274 -0
  507. package/src/workspace/intent.schema.json +718 -7
  508. package/src/workspace/intent.test.ts +11 -2
  509. package/src/workspace/intent.ts +495 -32
  510. package/src/workspace/kinds.test.ts +5 -5
  511. package/src/workspace/kinds.ts +62 -33
  512. package/src/workspace/lineage-adopt-cli.ts +103 -0
  513. package/src/workspace/lineage-adopt.test.ts +552 -0
  514. package/src/workspace/lineage-adopt.ts +452 -0
  515. package/src/workspace/lineage-check.ts +26 -5
  516. package/src/workspace/lineage-cli.ts +12 -2
  517. package/src/workspace/lineage-hash-index.ts +305 -0
  518. package/src/workspace/lineage-init.test.ts +9 -0
  519. package/src/workspace/lineage-init.ts +29 -9
  520. package/src/workspace/lineage-lock.ts +54 -0
  521. package/src/workspace/lineage-migrations.test.ts +27 -0
  522. package/src/workspace/lineage-migrations.ts +44 -14
  523. package/src/workspace/lineage-provenance.ts +40 -0
  524. package/src/workspace/lineage-upgrade-cli.ts +9 -5
  525. package/src/workspace/lineage-upgrade.test.ts +92 -1
  526. package/src/workspace/lineage-upgrade.ts +111 -16
  527. package/src/workspace/lineage-versions.ts +348 -0
  528. package/src/workspace/links.test.ts +121 -2
  529. package/src/workspace/links.ts +97 -6
  530. package/src/workspace/ls-contract.test.ts +84 -1
  531. package/src/workspace/ls-generated.ts +111 -0
  532. package/src/workspace/ls.schema.json +19 -0
  533. package/src/workspace/ls.ts +8 -1
  534. package/src/workspace/member-commands.ts +3 -1
  535. package/src/workspace/nested-graph.test.ts +176 -0
  536. package/src/workspace/nested-graph.ts +169 -0
  537. package/src/workspace/nesting.ts +37 -0
  538. package/src/workspace/pin-cli.test.ts +71 -0
  539. package/src/workspace/pin-cli.ts +57 -0
  540. package/src/workspace/pin-integrity.test.ts +121 -0
  541. package/src/workspace/pin-integrity.ts +104 -0
  542. package/src/workspace/points-write.schema.json +2 -0
  543. package/src/workspace/principal-classes.test.ts +175 -0
  544. package/src/workspace/principal-classes.ts +307 -0
  545. package/src/workspace/read-contract.test.ts +24 -0
  546. package/src/workspace/reason-codes.test.ts +20 -2
  547. package/src/workspace/reason-codes.ts +53 -3
  548. package/src/workspace/record-sessions.ts +12 -14
  549. package/src/workspace/records-amend.schema.json +6 -0
  550. package/src/workspace/records-checkout.ts +106 -0
  551. package/src/workspace/records-cli.ts +195 -22
  552. package/src/workspace/records-close.schema.json +7 -2
  553. package/src/workspace/records-close.ts +10 -2
  554. package/src/workspace/records-formats.test.ts +13 -5
  555. package/src/workspace/records-new.schema.json +6 -0
  556. package/src/workspace/records-review.schema.json +6 -0
  557. package/src/workspace/records-sessions.test.ts +23 -6
  558. package/src/workspace/records-uncommitted.test.ts +171 -0
  559. package/src/workspace/records-write.test.ts +65 -8
  560. package/src/workspace/records-write.ts +92 -6
  561. package/src/workspace/records.schema.json +127 -4
  562. package/src/workspace/records.ts +123 -5
  563. package/src/workspace/returns.ts +328 -0
  564. package/src/workspace/runs-cli.ts +381 -0
  565. package/src/workspace/runs-write.schema.json +583 -0
  566. package/src/workspace/runs.schema.json +810 -0
  567. package/src/workspace/runs.test.ts +242 -0
  568. package/src/workspace/runs.ts +709 -0
  569. package/src/workspace/signers.schema.json +206 -0
  570. package/src/workspace/status-contract.test.ts +5 -0
  571. package/src/workspace/status-gates.ts +24 -9
  572. package/src/workspace/status.schema.json +127 -2
  573. package/src/workspace/status.ts +21 -1
  574. package/src/workspace/symbols.ts +180 -0
  575. package/src/workspace/template-manifest.ts +22 -4
  576. package/src/workspace/trailer-joins.test.ts +171 -0
  577. package/src/workspace/trailer-joins.ts +82 -0
  578. package/src/workspace/trailers.ts +148 -0
  579. package/src/workspace/trust/attestor.ts +15 -0
  580. package/src/workspace/trust/dsse.ts +134 -0
  581. package/src/workspace/trust/evidence-cli.ts +195 -0
  582. package/src/workspace/trust/evidence.test.ts +241 -0
  583. package/src/workspace/trust/evidence.ts +207 -0
  584. package/src/workspace/trust/policy.ts +110 -3
  585. package/src/workspace/trust/provenance.ts +41 -4
  586. package/src/workspace/trust/record-seal.test.ts +1 -1
  587. package/src/workspace/trust/rotation.test.ts +258 -0
  588. package/src/workspace/trust/rotation.ts +336 -0
  589. package/src/workspace/trust/seal.ts +63 -1
  590. package/src/workspace/trust/signers-cli.ts +178 -0
  591. package/src/workspace/trust/ssh-commit.ts +53 -4
  592. package/src/workspace/trust/test-repo.ts +18 -0
  593. package/src/workspace/trust/trust.test.ts +8 -2
  594. package/src/workspace/trust/verify-cli.ts +1 -0
  595. package/src/workspace/trust/verify.ts +22 -0
  596. package/src/workspace/work-cli.e2e.test.ts +28 -1
  597. package/src/workspace/work-cli.ts +133 -8
  598. package/src/workspace/work-evidence.schema.json +6 -0
  599. package/src/workspace/work-history.schema.json +73 -1
  600. package/src/workspace/work-history.test.ts +48 -5
  601. package/src/workspace/work-kind-fields.test.ts +154 -0
  602. package/src/workspace/work-lease.schema.json +14 -1
  603. package/src/workspace/work.ts +82 -0
  604. package/src/workspace/workspace-principals.schema.json +60 -0
  605. package/src/workspace/write-scope.test.ts +466 -0
  606. package/src/workspace/write-scope.ts +573 -0
  607. package/src/yaml.test.ts +111 -1
  608. package/src/yaml.ts +141 -549
@@ -0,0 +1,573 @@
1
+ /**
2
+ * Write scope per member and record kind, for each principal class, and the
3
+ * agent sessions bound to one member (#2524 D5, D20; #2548; ws-067).
4
+ *
5
+ * The declaration's `writeScope` block gives a restricted class the members
6
+ * whose files it may write and the record kinds it may write, with which
7
+ * verbs (`new`, `amend`, `review`, `close`). A class with no entry is not
8
+ * restricted. The declaration's `agents` list names agent sessions, each
9
+ * bound to one member: a session writes that member's files, and the records
10
+ * of kinds that member or the workspace declares, as `writeScope.agent`
11
+ * allows. An agent's members can't be widened.
12
+ *
13
+ * Two places apply it, both reading the scope from the base revision so a
14
+ * change can't widen its own scope (#2524 threat model):
15
+ *
16
+ * - The write paths: `chant workspace records new|amend|review|close` and the
17
+ * MCP record tools refuse a write outside the writer's scope with
18
+ * `write-scope-member` or `write-scope-kind`, and an unknown session with
19
+ * `agent-unknown`. The session comes from `CHANT_AGENT`.
20
+ * - The enforcement boundary: `chant workspace check --changes <range>`
21
+ * judges every commit in the range, by its `Chant-Agent` trailer, its
22
+ * attested principal, or its author, and reports each path written outside
23
+ * that writer's scope. Run in CI with an attestation policy, the principal
24
+ * is the signer the policy at base trusts; on a developer machine it is
25
+ * detection only (ws-002).
26
+ *
27
+ * A principal's class comes from the role grants in the trust policy at base,
28
+ * through {@link principalClass} alone (the agent, runner and service roles,
29
+ * then the role each domain class a pinned package supplies names, #3080),
30
+ * or from naming an agent session. Claiming a session only ever narrows what
31
+ * a writer may do, so an unverified `CHANT_AGENT` or trailer is safe to honour.
32
+ *
33
+ * A writeScope key no pinned package supplies fails closed: a writer judged
34
+ * `human` might be in that class, so it is refused with
35
+ * `write-scope-class-unknown` until the plugin is installed at the pinned
36
+ * version or the entry is removed.
37
+ */
38
+
39
+ import { execFileSync } from "node:child_process";
40
+ import { posix, relative, sep } from "node:path";
41
+ import { isDeepStrictEqual } from "node:util";
42
+ // @ts-ignore — picomatch has no types declaration
43
+ import picomatch from "picomatch";
44
+ import {
45
+ readDeclaration,
46
+ WorkspaceReadError,
47
+ type AgentDeclaration,
48
+ type ClassScope,
49
+ type Declaration,
50
+ type PrincipalClass,
51
+ type ProtectedPath,
52
+ type WriteVerb,
53
+ } from "./declaration";
54
+ import {
55
+ classesOf,
56
+ coreClassRegistry,
57
+ loadClassRegistry,
58
+ principalClass as classOf,
59
+ roleGrants,
60
+ unknownScopeClasses,
61
+ type ClassRegistry,
62
+ } from "./principal-classes";
63
+ import type { ReasonCode } from "./reason-codes";
64
+ import { memberHolding } from "./record-assets";
65
+ import { gitRoot } from "./record-source";
66
+ import { normalisePrincipal, parseFrontMatter } from "./records";
67
+ import type { WorkspaceTree } from "./tree";
68
+ import { emptyPolicy, type TrustPolicy } from "./trust/policy";
69
+ import { commitProvenance, policyAtBase, resolveBase } from "./trust/provenance";
70
+ import { AGENT_TRAILER } from "./trailers";
71
+ import { locateWorkspace } from "./which-chant";
72
+
73
+ /** The environment variable naming the agent session a write is made in. */
74
+ export const AGENT_ENV = "CHANT_AGENT";
75
+
76
+ /** The commit trailer naming the agent session a commit was made in, one of chant's trailers (`trailers.ts`). */
77
+ export { AGENT_TRAILER };
78
+
79
+ /** Why a write is outside its writer's scope. Closed. */
80
+ export const WRITE_SCOPE_CODES = ["write-scope-member", "write-scope-kind", "write-scope-class-unknown", "agent-unknown"] as const satisfies readonly ReasonCode[];
81
+ export type WriteScopeCode = (typeof WRITE_SCOPE_CODES)[number];
82
+
83
+ /**
84
+ * What `check --changes` reports a path with: the record-write codes, and a
85
+ * write to a protected path (#3146), which a record write never is. Closed.
86
+ */
87
+ export const SCOPE_FINDING_CODES = [...WRITE_SCOPE_CODES, "write-scope-protected"] as const satisfies readonly ReasonCode[];
88
+ export type ScopeFindingCode = (typeof SCOPE_FINDING_CODES)[number];
89
+
90
+ export { classesOf, roleGrants };
91
+
92
+ /**
93
+ * The class a principal is in: the first of agent, runner and service whose
94
+ * role it holds at base, then the first domain class in `classes` whose role
95
+ * it holds (#3080), or human.
96
+ */
97
+ export function principalClass(policy: TrustPolicy, principal: string | null, classes: ClassRegistry = coreClassRegistry()): PrincipalClass {
98
+ return classOf(policy, principal, classes);
99
+ }
100
+
101
+ /** Who is writing, as the scope judges it. */
102
+ export interface Writer {
103
+ /** The principal named, or null when none is. */
104
+ principal: string | null;
105
+ class: PrincipalClass;
106
+ /** The agent session the writer is bound to, or null. */
107
+ agent: AgentDeclaration | null;
108
+ }
109
+
110
+ export class WriteScopeError extends Error {
111
+ constructor(
112
+ readonly code: WriteScopeCode,
113
+ message: string,
114
+ ) {
115
+ super(message);
116
+ this.name = "WriteScopeError";
117
+ }
118
+ }
119
+
120
+ /**
121
+ * The writer: in the session `agent` names (refused with agent-unknown when
122
+ * the declaration declares none by that name), in the session that lists
123
+ * `principal`, or else in the class `principal`'s roles give it.
124
+ */
125
+ export function resolveWriter(
126
+ declaration: Declaration | null,
127
+ policy: TrustPolicy,
128
+ given: { agent?: string | null; principal?: string | null },
129
+ classes: ClassRegistry = coreClassRegistry(),
130
+ ): Writer {
131
+ const principal = given.principal ?? null;
132
+ const agents = declaration?.agents ?? [];
133
+ if (given.agent !== undefined && given.agent !== null) {
134
+ const agent = agents.find((a) => a.name === given.agent);
135
+ if (!agent) {
136
+ const known = agents.map((a) => a.name).join(", ");
137
+ throw new WriteScopeError(
138
+ "agent-unknown",
139
+ declaration === null
140
+ ? `agent session ${JSON.stringify(given.agent)} is not declared: there is no workspace declaration`
141
+ : `agent session ${JSON.stringify(given.agent)} is not declared: the declaration's agents ${known ? `are ${known}` : "list none"}`,
142
+ );
143
+ }
144
+ return { principal, class: "agent", agent };
145
+ }
146
+ if (principal !== null) {
147
+ const name = normalisePrincipal(principal);
148
+ const agent = agents.find((a) => a.principals.some((p) => normalisePrincipal(p) === name));
149
+ if (agent) return { principal, class: "agent", agent };
150
+ }
151
+ return { principal, class: principalClass(policy, principal, classes), agent: null };
152
+ }
153
+
154
+ /**
155
+ * Refuse `writer` when the declaration's writeScope names a class `classes`
156
+ * doesn't know and the writer is judged human (#3080): nothing says which
157
+ * role puts a principal in the unknown class, so this writer may be in it.
158
+ * A writer in another class is judged by that class, which comes first.
159
+ */
160
+ export function unknownClassVerdict(declaration: Declaration | null, writer: Writer, classes: ClassRegistry): { ok: true } | { ok: false; code: "write-scope-class-unknown"; message: string } {
161
+ if (writer.class !== "human") return OK;
162
+ const unknown = unknownScopeClasses(declaration?.writeScope ?? null, classes);
163
+ if (unknown.length === 0) return OK;
164
+ const list = unknown.map((c) => `writeScope.${c}`).join(", ");
165
+ return {
166
+ ok: false,
167
+ code: "write-scope-class-unknown",
168
+ message: `${who(writer)} may be in ${unknown.length === 1 ? "a class" : "classes"} no pinned package supplies (${list}), so its scope can't be told; install the package that supplies ${unknown.length === 1 ? "it" : "them"} at the pinned version, or remove the entry (known classes: ${classes.names().join(", ")})`,
169
+ };
170
+ }
171
+
172
+ /** The scope that applies to `writer`, or null when its class is not restricted. An agent is always restricted to its member. */
173
+ export function scopeOf(declaration: Declaration | null, writer: Writer): ClassScope | null {
174
+ const entry = declaration?.writeScope?.[writer.class];
175
+ if (writer.class === "agent") return { members: null, records: entry?.records ?? null, protected: entry?.protected ?? [], pointer: entry?.pointer ?? "/agents" };
176
+ return entry ?? null;
177
+ }
178
+
179
+ export type ScopeVerdict = { ok: true } | { ok: false; code: Exclude<ScopeFindingCode, "agent-unknown">; message: string };
180
+
181
+ /** A record write's verdict: never write-scope-protected, since protected paths judge files that aren't records. */
182
+ export type RecordVerdict = { ok: true } | { ok: false; code: Exclude<WriteScopeCode, "agent-unknown">; message: string };
183
+
184
+ const OK = { ok: true } as const;
185
+
186
+ function who(writer: Writer): string {
187
+ if (writer.agent) return `agent session ${writer.agent.name}, bound to member ${writer.agent.member},`;
188
+ const name = writer.principal !== null ? `${writer.principal} (${writer.class})` : `a ${writer.class} writer`;
189
+ return writer.class === "agent" ? `${name}, which no agent session lists,` : name;
190
+ }
191
+
192
+ /** Whether `writer` may write in `member` (null: a path in no member). */
193
+ function memberAllowed(writer: Writer, scope: ClassScope, member: string | null): boolean {
194
+ if (writer.class === "agent") return member !== null && member === writer.agent?.member;
195
+ return scope.members === null || (member !== null && scope.members.includes(member));
196
+ }
197
+
198
+ /** The record kind a write goes to, as the scope reads it. */
199
+ export interface ScopedKind {
200
+ /** The kind file's `recordKind.name`. */
201
+ name: string;
202
+ /** The name the declaration gives it, or null. */
203
+ declaredName: string | null;
204
+ /** The member that declares the kind, or null for the workspace's own kinds. */
205
+ member: string | null;
206
+ /** True when the declaration names the kind; an undeclared kind belongs to the member holding its records. */
207
+ declared: boolean;
208
+ }
209
+
210
+ /** Whether `writer` may write a record of `kind` with `verb`. A delete is never in a restricted scope. */
211
+ export function judgeRecord(declaration: Declaration | null, writer: Writer, kind: ScopedKind, verb: WriteVerb | "delete"): RecordVerdict {
212
+ const scope = scopeOf(declaration, writer);
213
+ if (scope === null) return OK;
214
+ // The workspace's own kinds are in every member's reach; a member's kinds only in its own.
215
+ const inReach = kind.declared && kind.member === null ? true : memberAllowed(writer, scope, kind.member);
216
+ if (!inReach) {
217
+ const where = kind.member === null ? "in no member" : `of member ${kind.member}`;
218
+ return {
219
+ ok: false,
220
+ code: "write-scope-member",
221
+ message: `${who(writer)} may not write ${kind.name} records, which are ${where}: ${writer.class === "agent" ? "an agent session writes only its own member" : `writeScope.${writer.class}.members leaves it out`}`,
222
+ };
223
+ }
224
+ if (verb === "delete") {
225
+ return { ok: false, code: "write-scope-kind", message: `${who(writer)} may not delete a ${kind.name} record: a record is never deleted, and a new one supersedes it` };
226
+ }
227
+ if (scope.records === null) return OK;
228
+ const names = [kind.name, ...(kind.declaredName !== null && kind.declaredName !== kind.name ? [kind.declaredName] : [])];
229
+ const verbs = names.flatMap((n) => scope.records![n] ?? []);
230
+ if (verbs.includes(verb)) return OK;
231
+ return {
232
+ ok: false,
233
+ code: "write-scope-kind",
234
+ message:
235
+ verbs.length === 0
236
+ ? `${who(writer)} may not write ${kind.name} records: writeScope.${writer.class}.records does not list the kind`
237
+ : `${who(writer)} may ${[...new Set(verbs)].join(", ")} ${kind.name} records, and not ${verb} them (writeScope.${writer.class}.records)`,
238
+ };
239
+ }
240
+
241
+ /**
242
+ * The protected entry of `entries` that covers `path` (#3146, ws-077): one
243
+ * whose glob matches the path or a directory above it. Null when none does.
244
+ */
245
+ export function protectedEntry(entries: readonly ProtectedPath[], path: string): ProtectedPath | null {
246
+ if (entries.length === 0) return null;
247
+ const parts = path.split("/");
248
+ const prefixes = parts.map((_, i) => parts.slice(0, i + 1).join("/"));
249
+ for (const entry of entries) {
250
+ const match = picomatch(entry.path, { dot: true }) as (p: string) => boolean;
251
+ if (prefixes.some((p) => match(p))) return entry;
252
+ }
253
+ return null;
254
+ }
255
+
256
+ /**
257
+ * Whether JSON text `before` and `after` differ only in the top-level keys
258
+ * `except` names. False when either is missing or isn't a JSON object.
259
+ */
260
+ export function onlyKeysChanged(before: string | null, after: string | null, except: readonly string[]): boolean {
261
+ if (before === null || after === null || except.length === 0) return false;
262
+ const strip = (text: string): Record<string, unknown> | null => {
263
+ try {
264
+ const value = JSON.parse(text) as unknown;
265
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
266
+ const copy = { ...(value as Record<string, unknown>) };
267
+ for (const key of except) delete copy[key];
268
+ return copy;
269
+ } catch {
270
+ return null;
271
+ }
272
+ };
273
+ const a = strip(before);
274
+ const b = strip(after);
275
+ return a !== null && b !== null && isDeepStrictEqual(a, b);
276
+ }
277
+
278
+ /** A file's text before and after a write, read only when a protected entry with `except` needs it. */
279
+ export interface PathChange {
280
+ before(): string | null;
281
+ after(): string | null;
282
+ }
283
+
284
+ /**
285
+ * Whether `writer` may write the file at `path`, from the workspace root:
286
+ * the path is in a member the writer may write, and no protected entry of
287
+ * its scope covers it, unless the entry's `except` allows the change (#3146).
288
+ * Without `change`, a write to a path an `except` entry covers is judged as a
289
+ * change outside the allowed keys.
290
+ */
291
+ export function judgePath(declaration: Declaration | null, writer: Writer, path: string, change?: PathChange): ScopeVerdict {
292
+ const scope = scopeOf(declaration, writer);
293
+ if (scope === null) return OK;
294
+ const member = memberHolding(path, declaration?.members ?? []);
295
+ if (!memberAllowed(writer, scope, member)) {
296
+ const where = member === null ? "is in no member" : `is in member ${member}`;
297
+ return {
298
+ ok: false,
299
+ code: "write-scope-member",
300
+ message: `${who(writer)} may not write ${path}, which ${where}: ${writer.class === "agent" ? "an agent session writes only its own member" : `writeScope.${writer.class}.members leaves it out`}`,
301
+ };
302
+ }
303
+ const guarded = protectedEntry(scope.protected, path);
304
+ if (guarded === null) return OK;
305
+ if (guarded.except.length > 0 && change && onlyKeysChanged(change.before(), change.after(), guarded.except)) return OK;
306
+ const allowance = guarded.except.length > 0 ? `, outside the top-level keys it allows (${guarded.except.join(", ")})` : "";
307
+ return {
308
+ ok: false,
309
+ code: "write-scope-protected",
310
+ message: `${who(writer)} may not write ${path}${allowance}: writeScope.${writer.class}.protected lists ${guarded.path}`,
311
+ };
312
+ }
313
+
314
+ // ── The scope a write is judged by ───────────────────────────────────────────
315
+
316
+ /** The declaration and policy a write on this machine is judged by. */
317
+ export interface ScopeSource {
318
+ declaration: Declaration | null;
319
+ /** Where the declaration was read: the base revision, or the working tree when there is no base or no declaration at it. */
320
+ from: "base" | "working-tree" | null;
321
+ /** The workspace root on disk, or null without a declaration. */
322
+ rootOnDisk: string | null;
323
+ /** The workspace root from the git root, "." for the git root, or null without a declaration. */
324
+ root: string | null;
325
+ policy: TrustPolicy;
326
+ /** The principal classes: the core four and those the declaration's pins supply, path pins read where the declaration was (#3080). */
327
+ classes: ClassRegistry;
328
+ }
329
+
330
+ function readAt(cwd: string, at?: string): { declaration: Declaration; rootOnDisk: string; root: string; tree: WorkspaceTree } | null {
331
+ try {
332
+ const located = locateWorkspace(cwd, at);
333
+ return { declaration: readDeclaration(located.tree), rootOnDisk: located.rootOnDisk, root: located.root, tree: located.tree };
334
+ } catch (err) {
335
+ if (err instanceof WorkspaceReadError) return null;
336
+ throw err;
337
+ }
338
+ }
339
+
340
+ /**
341
+ * The scope for a write from `cwd`: the declaration and the trust policy at
342
+ * the base revision (`origin/HEAD`, `main` or `master`), so a write can't
343
+ * widen its own scope by editing its copy of the declaration. Without a base,
344
+ * or without a declaration at it, the working tree's declaration.
345
+ */
346
+ export function scopeSource(cwd: string): ScopeSource {
347
+ const top = gitRoot(cwd);
348
+ const base = top ? resolveBase(top) : null;
349
+ const policy = top && base ? policyAtBase(top, base) : emptyPolicy(null);
350
+ const withClasses = (read: NonNullable<ReturnType<typeof readAt>>) => {
351
+ const { tree, ...rest } = read;
352
+ return { ...rest, classes: loadClassRegistry(read.declaration.pins, read.rootOnDisk, { tree }).registry };
353
+ };
354
+ if (base?.commit) {
355
+ const atBase = readAt(cwd, base.commit);
356
+ if (atBase) return { ...withClasses(atBase), from: "base", policy };
357
+ }
358
+ const here = readAt(cwd);
359
+ return here ? { ...withClasses(here), from: "working-tree", policy } : { declaration: null, from: null, rootOnDisk: null, root: null, policy, classes: coreClassRegistry() };
360
+ }
361
+
362
+ const toPosix = (p: string) => (sep === "/" ? p : p.split(sep).join("/"));
363
+
364
+ /**
365
+ * The kind a write goes to, as {@link judgeRecord} reads it: the declared
366
+ * entry whose file is `kindFile`, or, for a kind the declaration does not
367
+ * name, the member holding `recordsDir`.
368
+ */
369
+ export function scopedKind(source: ScopeSource, kindName: string, kindFile: string, recordsDir: string): ScopedKind {
370
+ const decl = source.declaration;
371
+ if (!decl || source.rootOnDisk === null) return { name: kindName, declaredName: null, member: null, declared: false };
372
+ const path = toPosix(relative(source.rootOnDisk, kindFile));
373
+ const declared = [...decl.records, ...decl.members.flatMap((m) => m.records)].find((r) => r.path === path);
374
+ if (declared) return { name: kindName, declaredName: declared.name, member: declared.member, declared: true };
375
+ const dir = toPosix(relative(source.rootOnDisk, recordsDir));
376
+ return { name: kindName, declaredName: null, member: dir.startsWith("..") ? null : memberHolding(dir === "" ? "." : dir, decl.members), declared: false };
377
+ }
378
+
379
+ /**
380
+ * Refuse a record write outside the writer's scope (#2548): throws a
381
+ * {@link WriteScopeError}. `agent` is the session the write names
382
+ * (`CHANT_AGENT`), and `principal` who the write names as its author.
383
+ * Returns what it read at base, for the identity rule (#3163).
384
+ */
385
+ export function refuseRecordWrite(
386
+ cwd: string,
387
+ write: { kindName: string; kindFile: string; recordsDir: string; verb: WriteVerb; agent?: string | null; principal?: string | null },
388
+ ): ScopeSource {
389
+ const source = scopeSource(cwd);
390
+ if (source.declaration === null && (write.agent === undefined || write.agent === null)) return source;
391
+ const writer = resolveWriter(source.declaration, source.policy, { agent: write.agent, principal: write.principal }, source.classes);
392
+ const unknown = unknownClassVerdict(source.declaration, writer, source.classes);
393
+ if (!unknown.ok) throw new WriteScopeError(unknown.code, unknown.message);
394
+ const verdict = judgeRecord(source.declaration, writer, scopedKind(source, write.kindName, write.kindFile, write.recordsDir), write.verb);
395
+ if (!verdict.ok) throw new WriteScopeError(verdict.code, verdict.message);
396
+ return source;
397
+ }
398
+
399
+ // ── The check over a range ───────────────────────────────────────────────────
400
+
401
+ /** A commit as the scope check judged it. */
402
+ export interface ScopeCommit {
403
+ commit: string;
404
+ subject: string;
405
+ /** The attested principal, or the author's email when the commit is not attested. */
406
+ principal: string;
407
+ /** Whether the principal is a signer the policy at base attests, or only the author the commit claims. */
408
+ attested: boolean;
409
+ class: PrincipalClass;
410
+ /** The agent session, from the Chant-Agent trailer or the principal, or null. */
411
+ agent: string | null;
412
+ /** Paths in the workspace the commit writes. */
413
+ paths: number;
414
+ }
415
+
416
+ export interface ScopeFinding {
417
+ /** `finding:<code>:<commit>:<path>`, or `finding:<code>:<commit>` for agent-unknown and write-scope-class-unknown, which judge the whole commit. */
418
+ id: string;
419
+ code: ScopeFindingCode;
420
+ commit: string;
421
+ /** From the workspace root, or null for a finding on the whole commit. */
422
+ path: string | null;
423
+ principal: string;
424
+ class: PrincipalClass;
425
+ agent: string | null;
426
+ /** For a record file: how it was written, as the records commands name it, or delete. */
427
+ verb: WriteVerb | "delete" | null;
428
+ message: string;
429
+ }
430
+
431
+ export interface ScopeReport {
432
+ /** The commit the declaration and the trust policy were read at. */
433
+ base: string;
434
+ /** The classes with a writeScope entry, and agent when any session is declared: core first, then domain classes in pin order, then any no pinned package supplies. */
435
+ restricted: PrincipalClass[];
436
+ agents: string[];
437
+ commits: ScopeCommit[];
438
+ findings: ScopeFinding[];
439
+ }
440
+
441
+ /** A record kind the check knows, to tell a record file from another path. */
442
+ export interface CheckKind {
443
+ scoped: ScopedKind;
444
+ /** The records directory, from the repository root. */
445
+ dir: string;
446
+ match: RegExp;
447
+ /** The reviews field, when the kind has one; the verdicts field of a session kind. */
448
+ reviewFields: string[];
449
+ /** The state field and closed states of a session kind, for telling a close. */
450
+ session: { stateField: string; closed: string[]; fills: string[] } | null;
451
+ format: string;
452
+ }
453
+
454
+ function git(top: string, args: string[]): string {
455
+ return execFileSync("git", args, { cwd: top, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], maxBuffer: 512 * 1024 * 1024 });
456
+ }
457
+
458
+ function show(top: string, rev: string, path: string): string | null {
459
+ try {
460
+ return git(top, ["show", `${rev}:${path}`]);
461
+ } catch {
462
+ return null;
463
+ }
464
+ }
465
+
466
+ /** How a commit wrote a record file it modified: review when only the reviews (or a session's verdicts) changed, close when a session moved into a closed state, else amend. */
467
+ function modifiedVerb(top: string, commit: string, path: string, kind: CheckKind): WriteVerb {
468
+ if (kind.format !== "markdown-front-matter") return "amend";
469
+ const before = show(top, `${commit}^`, path);
470
+ const after = show(top, commit, path);
471
+ if (before === null || after === null) return "amend";
472
+ const a = parseFrontMatter(before);
473
+ const b = parseFrontMatter(after);
474
+ if (!a.ok || !b.ok) return "amend";
475
+ const keys = new Set([...Object.keys(a.value), ...Object.keys(b.value)]);
476
+ const changed = [...keys].filter((k) => JSON.stringify(a.value[k]) !== JSON.stringify(b.value[k]));
477
+ if (kind.session) {
478
+ const state = b.value[kind.session.stateField];
479
+ if (typeof state === "string" && kind.session.closed.includes(state) && a.value[kind.session.stateField] !== state) return "close";
480
+ if (changed.every((k) => kind.reviewFields.includes(k) || kind.session!.fills.includes(k))) return "review";
481
+ return "amend";
482
+ }
483
+ return changed.length > 0 && changed.every((k) => kind.reviewFields.includes(k)) ? "review" : "amend";
484
+ }
485
+
486
+ /**
487
+ * Judge every commit in `base..head` against the write scope the declaration
488
+ * at `base` gives (#2548). Returns null when that declaration restricts no
489
+ * one: no writeScope block and no agent session. Merges are skipped; the
490
+ * commits they bring are judged instead.
491
+ */
492
+ export async function checkWriteScope(q: {
493
+ top: string;
494
+ base: string;
495
+ head: string;
496
+ /** The workspace root from the repository root, "" for the top. */
497
+ prefix: string;
498
+ declaration: Declaration;
499
+ kinds: CheckKind[];
500
+ /** The principal classes the declaration's pins supply at base (#3080). Without it, the core four. */
501
+ classes?: ClassRegistry;
502
+ }): Promise<ScopeReport | null> {
503
+ const { top, base, head, prefix, declaration } = q;
504
+ const classes = q.classes ?? coreClassRegistry();
505
+ if (declaration.writeScope === null && declaration.agents.length === 0) return null;
506
+ const policy = policyAtBase(top, { commit: base, from: "flag" });
507
+ const { activeAttestors } = await import("./trust/attestor");
508
+ const attestors = await activeAttestors();
509
+ const scoped = (c: string) => declaration.writeScope?.[c] !== undefined || (c === "agent" && declaration.agents.length > 0);
510
+ const restricted = [...classes.names().filter(scoped), ...unknownScopeClasses(declaration.writeScope, classes)];
511
+ const report: ScopeReport = { base, restricted, agents: declaration.agents.map((a) => a.name), commits: [], findings: [] };
512
+ const list = git(top, ["rev-list", "--reverse", "--no-merges", `${base}..${head}`]).split("\n").filter(Boolean);
513
+ for (const commit of list) {
514
+ const [subject, email, trailers] = git(top, ["log", "-1", `--format=%s%x00%ae%x00%(trailers:key=${AGENT_TRAILER},valueonly,separator=%x01)`, commit]).split("\0");
515
+ const named = (trailers ?? "").split("\x01").map((s) => s.trim()).filter(Boolean)[0] ?? null;
516
+ const prov = policy.active ? commitProvenance(top, policy, commit, attestors) : null;
517
+ const attested = prov?.level === "attested" && prov.principal !== undefined;
518
+ const principal = attested ? prov!.principal! : email.trim();
519
+ const changed = git(top, ["diff-tree", "--no-commit-id", "-r", "--name-status", "--no-renames", "-z", commit])
520
+ .split("\0")
521
+ .filter(Boolean);
522
+ const paths: { status: string; path: string }[] = [];
523
+ for (let i = 0; i + 1 < changed.length; i += 2) {
524
+ const full = changed[i + 1];
525
+ if (prefix !== "" && !full.startsWith(`${prefix}/`)) continue;
526
+ paths.push({ status: changed[i], path: full });
527
+ }
528
+ let writer: Writer;
529
+ try {
530
+ writer = resolveWriter(declaration, policy, { agent: named, principal }, classes);
531
+ } catch (err) {
532
+ if (!(err instanceof WriteScopeError)) throw err;
533
+ report.commits.push({ commit, subject, principal, attested, class: "agent", agent: named, paths: paths.length });
534
+ report.findings.push({ id: `finding:${err.code}:${commit}`, code: err.code, commit, path: null, principal, class: "agent", agent: named, verb: null, message: `${commit.slice(0, 8)} has ${AGENT_TRAILER}: ${named}: ${err.message}` });
535
+ continue;
536
+ }
537
+ report.commits.push({ commit, subject, principal, attested, class: writer.class, agent: writer.agent?.name ?? null, paths: paths.length });
538
+ const unknown = unknownClassVerdict(declaration, writer, classes);
539
+ if (!unknown.ok) {
540
+ report.findings.push({ id: `finding:${unknown.code}:${commit}`, code: unknown.code, commit, path: null, principal, class: writer.class, agent: null, verb: null, message: `${commit.slice(0, 8)}: ${unknown.message}` });
541
+ continue;
542
+ }
543
+ if (scopeOf(declaration, writer) === null) continue;
544
+ for (const p of paths) {
545
+ const inWorkspace = prefix === "" ? p.path : p.path.slice(prefix.length + 1);
546
+ const kind = q.kinds.find((k) => posix.dirname(p.path) === k.dir && k.match.test(posix.basename(p.path)));
547
+ let verb: WriteVerb | "delete" | null = null;
548
+ let verdict: ScopeVerdict;
549
+ if (kind) {
550
+ verb = p.status === "A" ? "new" : p.status === "D" ? "delete" : modifiedVerb(top, commit, p.path, kind);
551
+ verdict = judgeRecord(declaration, writer, kind.scoped, verb);
552
+ } else {
553
+ verdict = judgePath(declaration, writer, inWorkspace, {
554
+ before: () => (p.status === "A" ? null : show(top, `${commit}^`, p.path)),
555
+ after: () => (p.status === "D" ? null : show(top, commit, p.path)),
556
+ });
557
+ }
558
+ if (verdict.ok) continue;
559
+ report.findings.push({
560
+ id: `finding:${verdict.code}:${commit}:${inWorkspace}`,
561
+ code: verdict.code,
562
+ commit,
563
+ path: inWorkspace,
564
+ principal,
565
+ class: writer.class,
566
+ agent: writer.agent?.name ?? null,
567
+ verb,
568
+ message: `${commit.slice(0, 8)} ${p.status === "D" ? "deletes" : p.status === "A" ? "adds" : "changes"} ${inWorkspace}: ${verdict.message}`,
569
+ });
570
+ }
571
+ }
572
+ return report;
573
+ }