@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,320 @@
1
+ /**
2
+ * #3146, ws-077: the factory and listing on a box block, the protected paths
3
+ * in writeScope, and whether a workspace is plantable.
4
+ *
5
+ * The declaration reads the fields with their defaults filled in, and a
6
+ * factory that names an undeclared member, or a second factory, fails the
7
+ * read (declaration-invalid, so check fails with WSP001). Everything is
8
+ * opt-in: a records-only workspace and an infra workspace that declares a
9
+ * factory with no box services both read and check cleanly. `status --json`
10
+ * and `graph --json` print the factory, the listing (with the cover's hash)
11
+ * and `plantable`; `check --changes` reports a write to a protected path with
12
+ * write-scope-protected, unless the entry's except allows the change.
13
+ */
14
+
15
+ import { createHash } from "node:crypto";
16
+ import { writeFileSync } from "node:fs";
17
+ import { afterAll, beforeAll, describe, expect, test } from "vitest";
18
+ import { cleanScratch, commitAll, contract, git, repo, writeFiles } from "./__fixtures__/contract-repo";
19
+ import { agentSession } from "./agent-cli";
20
+ import agentSchema from "./agent.schema.json";
21
+ import { plantability } from "./box-factory";
22
+ import { checkChanges, type ChangesDocument } from "./changes";
23
+ import changesSchema from "./changes.schema.json";
24
+ import { runDeclarationChecks } from "./checks";
25
+ import { parseDeclaration, WorkspaceReadError, type Declaration } from "./declaration";
26
+ import { workspaceGraph } from "./graph-cli";
27
+ import graphSchema from "./graph.schema.json";
28
+ import { workspaceStatus } from "./status";
29
+ import statusSchema from "./status.schema.json";
30
+ import { judgePath, onlyKeysChanged, protectedEntry, resolveWriter } from "./write-scope";
31
+ import { emptyPolicy } from "./trust/policy";
32
+
33
+ afterAll(cleanScratch);
34
+
35
+ type Json = Record<string, unknown>;
36
+
37
+ const FACTORY = {
38
+ builds: ["studio"],
39
+ check: "npm run --silent check && npm test --silent",
40
+ checks: "checks",
41
+ builders: "steward",
42
+ publish: { repo: "arugula-salad/studio", branchPrefix: "box/" },
43
+ };
44
+
45
+ const SERVICES = [{ name: "app", cmd: "node app.mjs", health: "http://127.0.0.1:5173/health" }];
46
+
47
+ function declaration(over: { box?: Json; members?: Json[]; writeScope?: Json; agents?: Json[] } = {}): Json {
48
+ return {
49
+ name: "studio",
50
+ schema: 1,
51
+ members: over.members ?? [
52
+ { name: "studio", dir: ".", kind: "other", because: "the repo itself" },
53
+ { name: "steward", dir: "steward", kind: "other", because: "the box's steward", box: over.box ?? { services: SERVICES, factory: FACTORY } },
54
+ ],
55
+ ...(over.writeScope ? { writeScope: over.writeScope } : {}),
56
+ ...(over.agents ? { agents: over.agents } : {}),
57
+ };
58
+ }
59
+
60
+ const parse = (doc: Json): Declaration => parseDeclaration(JSON.stringify(doc, null, 2), "chant.workspace.json");
61
+
62
+ /** The error a declaration's read fails with, and the line of the file it points at. */
63
+ function readFailure(doc: Json): { code: string; message: string; at: string | undefined } {
64
+ const text = JSON.stringify(doc, null, 2);
65
+ try {
66
+ parseDeclaration(text, "chant.workspace.json");
67
+ } catch (err) {
68
+ if (err instanceof WorkspaceReadError) return { code: err.code, message: err.message, at: err.location ? text.split("\n")[err.location.line - 1]?.trim() : undefined };
69
+ throw err;
70
+ }
71
+ throw new Error("the declaration read");
72
+ }
73
+
74
+ describe("the factory on a box block (#3146)", () => {
75
+ test("reads with its defaults filled in", () => {
76
+ const box = parse(declaration()).members[1].box!;
77
+ expect(box.factory).toEqual({
78
+ builds: ["studio"],
79
+ check: { run: "npm run --silent check && npm test --silent", kind: "test" },
80
+ checks: "checks",
81
+ builders: "steward",
82
+ publish: { forge: "github", repo: "arugula-salad/studio", base: null, branchPrefix: "box/", head: null },
83
+ pointer: "/members/1/box/factory",
84
+ });
85
+ expect(box.listing).toBeNull();
86
+ });
87
+
88
+ test("a check says what it is, a publish target names a base and a fork, and the branch prefix defaults to the work branch", () => {
89
+ const f = parse(
90
+ declaration({
91
+ box: { factory: { builds: ["studio", "steward"], check: { run: "chant build && chant lint", kind: "lint" }, publish: { repo: "acme/infra", base: "main", head: "alex/infra" } } },
92
+ }),
93
+ ).members[1].box!.factory!;
94
+ expect(f.builds).toEqual(["studio", "steward"]);
95
+ expect(f.check).toEqual({ run: "chant build && chant lint", kind: "lint" });
96
+ expect(f.publish).toEqual({ forge: "github", repo: "acme/infra", base: "main", branchPrefix: "chant/work/", head: "alex/infra" });
97
+ expect(f.checks).toBeNull();
98
+ expect(f.builders).toBeNull();
99
+ });
100
+
101
+ test("builds must name a declared member", () => {
102
+ const failure = readFailure(declaration({ box: { factory: { builds: ["app"] } } }));
103
+ expect(failure.code).toBe("declaration-invalid");
104
+ expect(failure.message).toContain('the factory builds "app", which is not a declared member');
105
+ expect(failure.at).toBe('"app"');
106
+ });
107
+
108
+ test("builders must name a declared member", () => {
109
+ const failure = readFailure(declaration({ box: { factory: { builds: ["studio"], builders: "delivery" } } }));
110
+ expect(failure.message).toContain('the factory\'s builders names "delivery"');
111
+ expect(failure.at).toBe('"builders": "delivery"');
112
+ });
113
+
114
+ test("a workspace has one factory", () => {
115
+ const failure = readFailure(
116
+ declaration({
117
+ members: [
118
+ { name: "a", dir: "a", kind: "other", because: "a", box: { factory: { builds: ["a"] } } },
119
+ { name: "b", dir: "b", kind: "other", because: "b", box: { factory: { builds: ["b"] } } },
120
+ ],
121
+ }),
122
+ );
123
+ expect(failure.message).toContain("members a and b both declare a factory");
124
+ expect(failure.at).toBe('"factory": {');
125
+ });
126
+
127
+ test("the schema refuses a factory without builds, an unknown check kind, a bad repo and an unknown field", () => {
128
+ expect(readFailure(declaration({ box: { factory: {} } })).code).toBe("declaration-invalid");
129
+ expect(readFailure(declaration({ box: { factory: { builds: ["studio"], check: { run: "x", kind: "smoke" } } } })).code).toBe("declaration-invalid");
130
+ expect(readFailure(declaration({ box: { factory: { builds: ["studio"], publish: { repo: "not a repo" } } } })).code).toBe("declaration-invalid");
131
+ expect(readFailure(declaration({ box: { factory: { builds: ["studio"], app: "studio" } } })).message).toContain('unknown field "app"');
132
+ // An x- field is still allowed, so a runtime can carry its own knobs beside chant's.
133
+ expect(parse(declaration({ box: { factory: { builds: ["studio"], "x-studio": { budget: 3 } } } })).members[1].box!.factory!.builds).toEqual(["studio"]);
134
+ });
135
+ });
136
+
137
+ describe("the listing on a box block (#3146)", () => {
138
+ test("reads with its defaults: listed, empty title and line, no cover", () => {
139
+ expect(parse(declaration({ box: { listing: {} } })).members[1].box!.listing).toEqual({ published: true, title: "", line: "", cover: null });
140
+ expect(parse(declaration({ box: { listing: { published: false, title: "Fern", line: "A garden planner", cover: "steward/cover.png" } } })).members[1].box!.listing).toEqual({
141
+ published: false,
142
+ title: "Fern",
143
+ line: "A garden planner",
144
+ cover: "steward/cover.png",
145
+ });
146
+ });
147
+
148
+ test("a title over 60 characters, a line over 140 and a control character are refused", () => {
149
+ expect(readFailure(declaration({ box: { listing: { title: "x".repeat(61) } } })).code).toBe("declaration-invalid");
150
+ expect(readFailure(declaration({ box: { listing: { line: "x".repeat(141) } } })).code).toBe("declaration-invalid");
151
+ expect(readFailure(declaration({ box: { listing: { title: "two\nlines" } } })).code).toBe("declaration-invalid");
152
+ });
153
+ });
154
+
155
+ describe("plantable (#3146)", () => {
156
+ test("exactly one member's box block declares services", () => {
157
+ expect(plantability(parse(declaration()))).toEqual({ plantable: true, box: "steward", reason: null });
158
+ });
159
+
160
+ test("an infra workspace with a factory and no box services reads and checks cleanly, and isn't plantable (#3174)", async () => {
161
+ const doc = declaration({
162
+ members: [
163
+ { name: "estate", dir: "estate", kind: "other", because: "terraform" },
164
+ { name: "ops", dir: "ops", kind: "other", because: "the steward", box: { factory: { builds: ["estate"], check: { run: "terraform plan", kind: "plan" }, publish: { repo: "acme/estate" } } } },
165
+ ],
166
+ });
167
+ const d = parse(doc);
168
+ expect(plantability(d)).toMatchObject({ plantable: false, box: null, reason: { code: "box-none" } });
169
+ const root = repo({ "chant.workspace.json": JSON.stringify(doc), "estate/main.tf": "", "ops/README.md": "" });
170
+ const { diagnostics } = await runDeclarationChecks(root);
171
+ expect(diagnostics.filter((x) => x.severity === "error")).toEqual([]);
172
+ });
173
+
174
+ test("a records-only workspace isn't plantable, and two boxes with services aren't either", () => {
175
+ expect(plantability(parse({ name: "notes", schema: 1, members: [] }))).toMatchObject({ plantable: false, reason: { code: "box-none" } });
176
+ const two = declaration({
177
+ members: [
178
+ { name: "a", dir: "a", kind: "other", because: "a", box: { services: SERVICES } },
179
+ { name: "b", dir: "b", kind: "other", because: "b", box: { services: SERVICES } },
180
+ ],
181
+ });
182
+ expect(plantability(parse(two))).toMatchObject({ plantable: false, box: null, reason: { code: "box-several", message: expect.stringContaining("a, b") } });
183
+ });
184
+ });
185
+
186
+ describe("protected paths in writeScope (#3146)", () => {
187
+ const scope = {
188
+ agent: {
189
+ protected: ["ops", "decisions/**", { path: "chant.workspace.json", except: ["diagrams"] }],
190
+ },
191
+ };
192
+ const d = parse(declaration({ writeScope: scope, agents: [{ name: "builder", member: "studio" }] }));
193
+ const writer = resolveWriter(d, emptyPolicy(null), { agent: "builder" });
194
+
195
+ test("read in file order, with except empty for a glob", () => {
196
+ expect(d.writeScope!.agent!.protected).toEqual([
197
+ { path: "ops", except: [] },
198
+ { path: "decisions/**", except: [] },
199
+ { path: "chant.workspace.json", except: ["diagrams"] },
200
+ ]);
201
+ });
202
+
203
+ test("an entry covers the files it matches and everything under a directory it matches", () => {
204
+ expect(protectedEntry(d.writeScope!.agent!.protected, "ops/factory/guard.mjs")?.path).toBe("ops");
205
+ expect(protectedEntry(d.writeScope!.agent!.protected, "decisions/ws-001.md")?.path).toBe("decisions/**");
206
+ expect(protectedEntry(d.writeScope!.agent!.protected, "opsy.md")).toBeNull();
207
+ });
208
+
209
+ test("a write to a protected path is out of scope, even in the session's own member", () => {
210
+ expect(judgePath(d, writer, "src/server.mjs")).toEqual({ ok: true });
211
+ expect(judgePath(d, writer, "ops/factory/guard.mjs")).toMatchObject({ ok: false, code: "write-scope-protected", message: expect.stringContaining("writeScope.agent.protected lists ops") });
212
+ });
213
+
214
+ test("except lets a JSON file change only in the keys it names", () => {
215
+ const before = JSON.stringify({ name: "studio", diagrams: [] });
216
+ const onlyDiagrams = JSON.stringify({ name: "studio", diagrams: [{ name: "flow" }] });
217
+ const alsoName = JSON.stringify({ name: "other", diagrams: [{ name: "flow" }] });
218
+ expect(onlyKeysChanged(before, onlyDiagrams, ["diagrams"])).toBe(true);
219
+ expect(onlyKeysChanged(before, alsoName, ["diagrams"])).toBe(false);
220
+ expect(onlyKeysChanged(null, onlyDiagrams, ["diagrams"])).toBe(false);
221
+ expect(onlyKeysChanged("not json", onlyDiagrams, ["diagrams"])).toBe(false);
222
+ expect(judgePath(d, writer, "chant.workspace.json", { before: () => before, after: () => onlyDiagrams })).toEqual({ ok: true });
223
+ expect(judgePath(d, writer, "chant.workspace.json", { before: () => before, after: () => alsoName })).toMatchObject({
224
+ ok: false,
225
+ code: "write-scope-protected",
226
+ message: expect.stringContaining("outside the top-level keys it allows (diagrams)"),
227
+ });
228
+ // Without the change in hand, an except entry can't be satisfied.
229
+ expect(judgePath(d, writer, "chant.workspace.json")).toMatchObject({ ok: false, code: "write-scope-protected" });
230
+ });
231
+ });
232
+
233
+ describe("the read contract: status, graph, agent and check --changes (#3146)", () => {
234
+ const status = contract(statusSchema);
235
+ const graph = contract(graphSchema);
236
+ const agent = contract(agentSchema);
237
+ const changes = contract(changesSchema);
238
+ const cover = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 1, 2, 3]);
239
+ const doc = declaration({
240
+ box: { services: SERVICES, factory: FACTORY, listing: { title: "Studio", line: "Where boxes are planted", cover: "steward/cover.png" } },
241
+ writeScope: { agent: { protected: ["ops", { path: "chant.workspace.json", except: ["diagrams"] }] } },
242
+ agents: [{ name: "builder", member: "studio" }],
243
+ });
244
+ let root: string;
245
+
246
+ beforeAll(() => {
247
+ root = repo({ "chant.workspace.json": `${JSON.stringify(doc, null, 2)}\n`, "steward/README.md": "the steward\n", "ops/guard.mjs": "export {};\n", "server.mjs": "export {};\n" });
248
+ writeFileSync(`${root}/steward/cover.png`, cover);
249
+ git(root, "add", "-A");
250
+ git(root, "commit", "-q", "-m", "the workspace");
251
+ git(root, "branch", "-M", "main");
252
+ });
253
+
254
+ test("status --json prints the factory, the listing with the cover's hash, and plantable", async () => {
255
+ const out = await workspaceStatus({ cwd: root, env: "prod" });
256
+ status.expectValid(out);
257
+ if ("error" in out) throw new Error(out.error.message);
258
+ expect(out.plantable).toEqual({ plantable: true, box: "steward", reason: null });
259
+ const box = out.members.find((m) => m.name === "steward")!.box!;
260
+ expect(box.factory).toEqual({
261
+ builds: ["studio"],
262
+ check: { run: "npm run --silent check && npm test --silent", kind: "test" },
263
+ checks: "checks",
264
+ builders: "steward",
265
+ publish: { forge: "github", repo: "arugula-salad/studio", base: null, branchPrefix: "box/", head: null },
266
+ });
267
+ expect(box.listing).toEqual({
268
+ published: true,
269
+ title: "Studio",
270
+ line: "Where boxes are planted",
271
+ cover: { path: "steward/cover.png", sha256: createHash("sha256").update(cover).digest("hex") },
272
+ });
273
+ });
274
+
275
+ test("graph --json prints them too, at a revision with --at", async () => {
276
+ const head = git(root, "rev-parse", "HEAD");
277
+ for (const at of [undefined, head]) {
278
+ const { doc: out } = await workspaceGraph({ cwd: root, at });
279
+ graph.expectValid(out);
280
+ if ("error" in out) throw new Error(out.error.message);
281
+ expect(out.plantable).toMatchObject({ plantable: true, box: "steward" });
282
+ const byName = Object.fromEntries(out.members.map((m) => [m.name, m.box]));
283
+ expect(byName.studio).toBeNull();
284
+ expect(byName.steward?.factory?.builds).toEqual(["studio"]);
285
+ expect(byName.steward?.listing?.cover?.sha256).toBe(createHash("sha256").update(cover).digest("hex"));
286
+ }
287
+ });
288
+
289
+ test("workspace agent lists the protected paths", async () => {
290
+ const out = await agentSession({ cwd: root, name: "builder" });
291
+ agent.expectValid(out);
292
+ if ("error" in out) throw new Error(out.error.message);
293
+ expect(out.scope.protected).toEqual([
294
+ { path: "ops", except: [] },
295
+ { path: "chant.workspace.json", except: ["diagrams"] },
296
+ ]);
297
+ });
298
+
299
+ test("check --changes reports a write to a protected path, and lets an excepted key change", async () => {
300
+ git(root, "checkout", "-q", "-b", "work");
301
+ const trailer = "\n\nChant-Agent: builder";
302
+ writeFiles(root, { "server.mjs": "export const port = 1;\n" });
303
+ const ok = commitAll(root, `app: a port${trailer}`);
304
+ writeFiles(root, { "chant.workspace.json": `${JSON.stringify({ ...doc, diagrams: [] }, null, 2)}\n` });
305
+ const excepted = commitAll(root, `a diagram${trailer}`);
306
+ writeFiles(root, { "ops/guard.mjs": "export const off = true;\n" });
307
+ const guard = commitAll(root, `the steward${trailer}`);
308
+ writeFiles(root, { "chant.workspace.json": `${JSON.stringify({ ...doc, name: "renamed", diagrams: [] }, null, 2)}\n` });
309
+ const renamed = commitAll(root, `rename${trailer}`);
310
+ const { doc: out } = (await checkChanges({ cwd: root, range: "main..work" })) as { doc: Exclude<ChangesDocument, { error: unknown }> };
311
+ changes.expectValid(out);
312
+ const found = out.scope!.findings.map((f) => [f.commit, f.code, f.path]);
313
+ expect(found).toEqual([
314
+ [guard, "write-scope-protected", "ops/guard.mjs"],
315
+ [renamed, "write-scope-protected", "chant.workspace.json"],
316
+ ]);
317
+ expect(found.map(([c]) => c)).not.toContain(ok);
318
+ expect(found.map(([c]) => c)).not.toContain(excepted);
319
+ });
320
+ });
@@ -0,0 +1,103 @@
1
+ /**
2
+ * The factory and listing on a box block, and whether a workspace is
3
+ * plantable (#3146, ws-077), as `chant workspace status --json` and
4
+ * `chant workspace graph --json` print them.
5
+ *
6
+ * Every field here is opt-in (#3174): a workspace with no box, or a box with
7
+ * no `factory` or `listing`, reads as `null` for each, and plantability is a
8
+ * fact about the declaration, never a check that fails. A records-only
9
+ * workspace and an infra workspace with a factory and no box services both
10
+ * read and check cleanly; they just aren't plantable.
11
+ */
12
+
13
+ import { createHash } from "node:crypto";
14
+ import type { BoxFactory, BoxListing, Declaration, FactoryCheckKind } from "./declaration";
15
+ import type { ReasonCode } from "./reason-codes";
16
+ import type { WorkspaceTree } from "./tree";
17
+
18
+ /** Why a workspace isn't plantable. Closed. */
19
+ export const PLANTABLE_REASON_CODES = ["box-none", "box-several"] as const satisfies readonly ReasonCode[];
20
+ export type PlantableReasonCode = (typeof PLANTABLE_REASON_CODES)[number];
21
+
22
+ /**
23
+ * Whether a host such as a studio's planter can plant the workspace as one
24
+ * box: exactly one member's box block declares services. `box` names that
25
+ * member, and is null when the workspace isn't plantable.
26
+ */
27
+ export interface Plantable {
28
+ plantable: boolean;
29
+ box: string | null;
30
+ reason: { code: PlantableReasonCode; message: string } | null;
31
+ }
32
+
33
+ /** A box's factory as the read contract prints it: every field present, defaults filled in. */
34
+ export interface FactoryView {
35
+ builds: string[];
36
+ check: { run: string; kind: FactoryCheckKind } | null;
37
+ checks: string | null;
38
+ builders: string | null;
39
+ publish: { forge: "github"; repo: string; base: string | null; branchPrefix: string; head: string | null } | null;
40
+ }
41
+
42
+ /** A box's listing as the read contract prints it, with the cover's hash when the file is there. */
43
+ export interface ListingView {
44
+ published: boolean;
45
+ title: string;
46
+ line: string;
47
+ /** The cover file from the workspace root and the sha256 of its bytes, null when the file can't be read; null when none is declared. */
48
+ cover: { path: string; sha256: string | null } | null;
49
+ }
50
+
51
+ /** Whether the declaration is plantable (#3146): exactly one member's box block declares services. */
52
+ export function plantability(declaration: Declaration): Plantable {
53
+ const boxes = declaration.members.filter((m) => (m.box?.services.length ?? 0) > 0);
54
+ if (boxes.length === 1) return { plantable: true, box: boxes[0].name, reason: null };
55
+ if (boxes.length === 0) {
56
+ return { plantable: false, box: null, reason: { code: "box-none", message: "no member's box block declares services, so there is no box to plant" } };
57
+ }
58
+ return {
59
+ plantable: false,
60
+ box: null,
61
+ reason: { code: "box-several", message: `members ${boxes.map((m) => m.name).join(", ")} each declare box services, and a planted workspace runs one box` },
62
+ };
63
+ }
64
+
65
+ export function factoryView(f: BoxFactory | null): FactoryView | null {
66
+ if (f === null) return null;
67
+ return {
68
+ builds: [...f.builds],
69
+ check: f.check === null ? null : { ...f.check },
70
+ checks: f.checks,
71
+ builders: f.builders,
72
+ publish: f.publish === null ? null : { ...f.publish },
73
+ };
74
+ }
75
+
76
+ /** The listing, hashing the cover through `read` (bytes, or null when the file can't be read). */
77
+ export function listingView(l: BoxListing | null, read: (path: string) => Uint8Array | null): ListingView | null {
78
+ if (l === null) return null;
79
+ let cover: ListingView["cover"] = null;
80
+ if (l.cover !== null) {
81
+ const bytes = read(l.cover);
82
+ cover = { path: l.cover, sha256: bytes === null ? null : createHash("sha256").update(bytes).digest("hex") };
83
+ }
84
+ return { published: l.published, title: l.title, line: l.line, cover };
85
+ }
86
+
87
+ /** A reader of a file's bytes in `tree` for {@link listingView}: null when the file can't be read. */
88
+ export function treeBytes(tree: WorkspaceTree): (path: string) => Uint8Array | null {
89
+ return (path) => {
90
+ try {
91
+ if (tree.stat(path) !== "file") return null;
92
+ return tree.bytes ? tree.bytes(path) : new TextEncoder().encode(tree.read(path));
93
+ } catch {
94
+ return null;
95
+ }
96
+ };
97
+ }
98
+
99
+ /** The box fields the graph prints on a member (#3146): its factory and listing, or null when the member's box declares neither. */
100
+ export function graphBox(box: { factory: BoxFactory | null; listing: BoxListing | null } | null, tree: WorkspaceTree): { factory: FactoryView | null; listing: ListingView | null } | null {
101
+ if (box === null || (box.factory === null && box.listing === null)) return null;
102
+ return { factory: factoryView(box.factory), listing: listingView(box.listing, treeBytes(tree)) };
103
+ }
@@ -32,6 +32,11 @@ export function formatChanges(doc: Result): string {
32
32
  out.push(`${p.status.padEnd(12)} ${p.change.padEnd(8)} ${p.path}${p.from ? ` (from ${p.from})` : ""}${why ? `; ${why}` : ""}`);
33
33
  }
34
34
  for (const f of doc.findings) out.push(`${f.severity === "fail" ? "error " : "warning"} ${f.code}: ${f.message}`);
35
+ if (doc.scope) {
36
+ const sc = doc.scope;
37
+ out.push(`scope ${sc.commits.length} commits judged against the declaration at ${sc.base.slice(0, 8)}; restricted: ${sc.restricted.join(", ") || "none"}`);
38
+ for (const f of sc.findings) out.push(`error ${f.code}: ${f.message}`);
39
+ }
35
40
  const s = doc.summary;
36
41
  out.push(`${s.paths} changed paths: ${s.covered} covered, ${s.uncovered} uncovered, ${s.outOfScope} out of scope, ${s.ignored} ignored, ${s.records} records; ${doc.findings.length} findings`);
37
42
  return out.join("\n");
@@ -28,6 +28,7 @@
28
28
  "paths",
29
29
  "findings",
30
30
  "summary",
31
+ "scope",
31
32
  "ok"
32
33
  ],
33
34
  "properties": {
@@ -198,9 +199,20 @@
198
199
  }
199
200
  }
200
201
  },
202
+ "scope": {
203
+ "description": "The write scope check (#2548, ws-067): each commit in the range, merges left out, judged against the writeScope and agents of the declaration at the range's base, with the trust policy read there. Null when the base has no declaration, or one with no writeScope block and no agents. Every finding fails the check, whatever severity says.",
204
+ "oneOf": [
205
+ {
206
+ "type": "null"
207
+ },
208
+ {
209
+ "$ref": "#/$defs/scope"
210
+ }
211
+ ]
212
+ },
201
213
  "ok": {
202
214
  "type": "boolean",
203
- "description": "False only when severity is fail and there is a finding."
215
+ "description": "False when severity is fail and there is a finding, or when scope has a finding."
204
216
  }
205
217
  }
206
218
  },
@@ -467,6 +479,164 @@
467
479
  },
468
480
  "paths": false
469
481
  }
482
+ },
483
+ "scope": {
484
+ "type": "object",
485
+ "required": [
486
+ "base",
487
+ "restricted",
488
+ "agents",
489
+ "commits",
490
+ "findings"
491
+ ],
492
+ "properties": {
493
+ "base": {
494
+ "type": "string",
495
+ "pattern": "^[0-9a-f]{40,64}$",
496
+ "description": "The commit the declaration and the trust policy were read at: the range's base."
497
+ },
498
+ "restricted": {
499
+ "type": "array",
500
+ "items": {
501
+ "$ref": "#/$defs/principalClass"
502
+ },
503
+ "description": "The principal classes with a writeScope entry, and agent when any agent session is declared: the core classes first, then domain classes in pin order, then any class writeScope names that no pinned package supplies."
504
+ },
505
+ "agents": {
506
+ "type": "array",
507
+ "items": {
508
+ "type": "string"
509
+ },
510
+ "description": "The agent sessions the declaration at base names."
511
+ },
512
+ "commits": {
513
+ "type": "array",
514
+ "items": {
515
+ "$ref": "#/$defs/scopeCommit"
516
+ }
517
+ },
518
+ "findings": {
519
+ "type": "array",
520
+ "items": {
521
+ "$ref": "#/$defs/scopeFinding"
522
+ }
523
+ }
524
+ }
525
+ },
526
+ "principalClass": {
527
+ "type": "string",
528
+ "pattern": "^(?!x-)[a-z][a-z0-9-]{0,39}$",
529
+ "description": "A principal class: human, agent, runner or service, or a domain class a pinned package supplies at its ./workspace-principals subpath (#3080). A reader that knows only the core classes treats any other name as a domain class."
530
+ },
531
+ "scopeCommit": {
532
+ "type": "object",
533
+ "required": [
534
+ "commit",
535
+ "subject",
536
+ "principal",
537
+ "attested",
538
+ "class",
539
+ "agent",
540
+ "paths"
541
+ ],
542
+ "properties": {
543
+ "commit": {
544
+ "type": "string",
545
+ "pattern": "^[0-9a-f]{40,64}$"
546
+ },
547
+ "subject": {
548
+ "type": "string"
549
+ },
550
+ "principal": {
551
+ "type": "string",
552
+ "description": "The signer that attests the commit under the policy at base, or else the author's email, which the commit only claims."
553
+ },
554
+ "attested": {
555
+ "type": "boolean",
556
+ "description": "Whether principal is an attested signer."
557
+ },
558
+ "class": {
559
+ "$ref": "#/$defs/principalClass",
560
+ "description": "agent for a commit naming a session with a Chant-Agent trailer or by a principal a session lists; otherwise the class the principal's role grants at base give."
561
+ },
562
+ "agent": {
563
+ "type": [
564
+ "string",
565
+ "null"
566
+ ],
567
+ "description": "The agent session, or null."
568
+ },
569
+ "paths": {
570
+ "type": "integer",
571
+ "minimum": 0,
572
+ "description": "How many paths in the workspace the commit writes."
573
+ }
574
+ }
575
+ },
576
+ "scopeFinding": {
577
+ "type": "object",
578
+ "required": [
579
+ "id",
580
+ "code",
581
+ "commit",
582
+ "path",
583
+ "principal",
584
+ "class",
585
+ "agent",
586
+ "verb",
587
+ "message"
588
+ ],
589
+ "properties": {
590
+ "id": {
591
+ "type": "string"
592
+ },
593
+ "code": {
594
+ "enum": [
595
+ "write-scope-member",
596
+ "write-scope-kind",
597
+ "write-scope-class-unknown",
598
+ "write-scope-protected",
599
+ "agent-unknown"
600
+ ]
601
+ },
602
+ "commit": {
603
+ "type": "string",
604
+ "pattern": "^[0-9a-f]{40,64}$"
605
+ },
606
+ "path": {
607
+ "type": [
608
+ "string",
609
+ "null"
610
+ ],
611
+ "description": "From the workspace root; null for agent-unknown and write-scope-class-unknown, findings on the whole commit."
612
+ },
613
+ "principal": {
614
+ "type": "string"
615
+ },
616
+ "class": {
617
+ "$ref": "#/$defs/principalClass"
618
+ },
619
+ "agent": {
620
+ "type": [
621
+ "string",
622
+ "null"
623
+ ]
624
+ },
625
+ "verb": {
626
+ "enum": [
627
+ "new",
628
+ "amend",
629
+ "review",
630
+ "close",
631
+ "delete",
632
+ null
633
+ ],
634
+ "description": "For a record file of a kind the declaration at base names: how the commit wrote it. A modified record is review when only its reviews (or a session's verdicts) changed, close when a session entered a closed state, and amend otherwise."
635
+ },
636
+ "message": {
637
+ "type": "string"
638
+ }
639
+ }
470
640
  }
471
641
  }
472
642
  }