@intentius/chant 0.79.0 → 0.81.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 (462) hide show
  1. package/dist/audit/core.d.ts.map +1 -1
  2. package/dist/audit/discover.d.ts +30 -0
  3. package/dist/audit/discover.d.ts.map +1 -1
  4. package/dist/audit/report-html.d.ts +5 -0
  5. package/dist/audit/report-html.d.ts.map +1 -1
  6. package/dist/audit/report-model.d.ts +13 -0
  7. package/dist/audit/report-model.d.ts.map +1 -1
  8. package/dist/audit/terraform-state.d.ts +13 -0
  9. package/dist/audit/terraform-state.d.ts.map +1 -1
  10. package/dist/cli/command-group.d.ts +6 -0
  11. package/dist/cli/command-group.d.ts.map +1 -1
  12. package/dist/cli/commands/audit.d.ts +44 -3
  13. package/dist/cli/commands/audit.d.ts.map +1 -1
  14. package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
  15. package/dist/cli/commands/check-lexicon.d.ts +8 -0
  16. package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
  17. package/dist/cli/commands/doctor.d.ts.map +1 -1
  18. package/dist/cli/commands/import-agents.d.ts.map +1 -1
  19. package/dist/cli/commands/init.d.ts +6 -0
  20. package/dist/cli/commands/init.d.ts.map +1 -1
  21. package/dist/cli/commands/lint.d.ts.map +1 -1
  22. package/dist/cli/commands/onboard.d.ts.map +1 -1
  23. package/dist/cli/commands/update.d.ts +6 -0
  24. package/dist/cli/commands/update.d.ts.map +1 -1
  25. package/dist/cli/commands/vendor.d.ts +5 -0
  26. package/dist/cli/commands/vendor.d.ts.map +1 -1
  27. package/dist/cli/conflict-check.d.ts +6 -0
  28. package/dist/cli/conflict-check.d.ts.map +1 -1
  29. package/dist/cli/handlers/build.d.ts.map +1 -1
  30. package/dist/cli/handlers/components.d.ts.map +1 -1
  31. package/dist/cli/handlers/dev.d.ts.map +1 -1
  32. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  33. package/dist/cli/handlers/init.d.ts.map +1 -1
  34. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  35. package/dist/cli/handlers/misc.d.ts.map +1 -1
  36. package/dist/cli/handlers/operator.d.ts +7 -0
  37. package/dist/cli/handlers/operator.d.ts.map +1 -1
  38. package/dist/cli/handlers/promote.d.ts +20 -0
  39. package/dist/cli/handlers/promote.d.ts.map +1 -0
  40. package/dist/cli/handlers/run-generate.d.ts +39 -0
  41. package/dist/cli/handlers/run-generate.d.ts.map +1 -0
  42. package/dist/cli/handlers/run.d.ts +4 -1
  43. package/dist/cli/handlers/run.d.ts.map +1 -1
  44. package/dist/cli/handlers/vendor.d.ts +8 -3
  45. package/dist/cli/handlers/vendor.d.ts.map +1 -1
  46. package/dist/cli/main.d.ts +12 -0
  47. package/dist/cli/main.d.ts.map +1 -1
  48. package/dist/cli/mcp/op-tools.d.ts.map +1 -1
  49. package/dist/cli/plugins.d.ts +14 -0
  50. package/dist/cli/plugins.d.ts.map +1 -1
  51. package/dist/cli/registry.d.ts +46 -1
  52. package/dist/cli/registry.d.ts.map +1 -1
  53. package/dist/codegen/fetch.d.ts +10 -0
  54. package/dist/codegen/fetch.d.ts.map +1 -1
  55. package/dist/components/capability-plugin-loader.d.ts.map +1 -1
  56. package/dist/components/cli-support.d.ts +3 -1
  57. package/dist/components/cli-support.d.ts.map +1 -1
  58. package/dist/components/discover.d.ts.map +1 -1
  59. package/dist/components/driver.d.ts +16 -2
  60. package/dist/components/driver.d.ts.map +1 -1
  61. package/dist/components/fan-out-support.d.ts.map +1 -1
  62. package/dist/components/gate-plan.d.ts +40 -0
  63. package/dist/components/gate-plan.d.ts.map +1 -0
  64. package/dist/components/pilots/alb-ecs.pilot.d.ts +7 -1
  65. package/dist/components/pilots/alb-ecs.pilot.d.ts.map +1 -1
  66. package/dist/components/presets/ecs-fargate.d.ts +10 -4
  67. package/dist/components/presets/ecs-fargate.d.ts.map +1 -1
  68. package/dist/components/promote.d.ts +264 -0
  69. package/dist/components/promote.d.ts.map +1 -0
  70. package/dist/components/sandbox/driver.d.ts.map +1 -1
  71. package/dist/components/sandbox/run.d.ts.map +1 -1
  72. package/dist/components/verbs/build-archive.d.ts +22 -11
  73. package/dist/components/verbs/build-archive.d.ts.map +1 -1
  74. package/dist/components/verbs/run-agent.d.ts.map +1 -1
  75. package/dist/config-static.d.ts +59 -0
  76. package/dist/config-static.d.ts.map +1 -0
  77. package/dist/config.d.ts +104 -5
  78. package/dist/config.d.ts.map +1 -1
  79. package/dist/content-digest.d.ts +17 -0
  80. package/dist/content-digest.d.ts.map +1 -0
  81. package/dist/discovery/files.d.ts +48 -4
  82. package/dist/discovery/files.d.ts.map +1 -1
  83. package/dist/discovery/fold-import.d.ts +32 -1
  84. package/dist/discovery/fold-import.d.ts.map +1 -1
  85. package/dist/discovery/index.d.ts.map +1 -1
  86. package/dist/discovery/sandbox/driver.d.ts +20 -0
  87. package/dist/discovery/sandbox/driver.d.ts.map +1 -1
  88. package/dist/discovery/sandbox/fork.d.ts +20 -0
  89. package/dist/discovery/sandbox/fork.d.ts.map +1 -1
  90. package/dist/discovery/walk.d.ts +119 -0
  91. package/dist/discovery/walk.d.ts.map +1 -0
  92. package/dist/graph-ir.d.ts +17 -1
  93. package/dist/graph-ir.d.ts.map +1 -1
  94. package/dist/join-key.d.ts +31 -0
  95. package/dist/join-key.d.ts.map +1 -0
  96. package/dist/lexicon-module.d.ts +110 -0
  97. package/dist/lexicon-module.d.ts.map +1 -0
  98. package/dist/lexicon.d.ts +82 -1
  99. package/dist/lexicon.d.ts.map +1 -1
  100. package/dist/lifecycle/build-ledger-store.d.ts.map +1 -1
  101. package/dist/lifecycle/digest.d.ts +5 -1
  102. package/dist/lifecycle/digest.d.ts.map +1 -1
  103. package/dist/lifecycle/gate-ledger.d.ts +20 -5
  104. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  105. package/dist/lifecycle/git.d.ts +64 -0
  106. package/dist/lifecycle/git.d.ts.map +1 -1
  107. package/dist/lifecycle/lease.d.ts +8 -1
  108. package/dist/lifecycle/lease.d.ts.map +1 -1
  109. package/dist/lifecycle/legacy-digest.d.ts +59 -0
  110. package/dist/lifecycle/legacy-digest.d.ts.map +1 -0
  111. package/dist/lifecycle/member-ledger.d.ts +67 -0
  112. package/dist/lifecycle/member-ledger.d.ts.map +1 -0
  113. package/dist/lifecycle/plan-digest.d.ts +4 -3
  114. package/dist/lifecycle/plan-digest.d.ts.map +1 -1
  115. package/dist/lifecycle/release-ledger.d.ts +42 -1
  116. package/dist/lifecycle/release-ledger.d.ts.map +1 -1
  117. package/dist/lifecycle/status.d.ts +90 -5
  118. package/dist/lifecycle/status.d.ts.map +1 -1
  119. package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts +2 -3
  120. package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts.map +1 -1
  121. package/dist/op/activities/activity-contracts.d.ts +32 -0
  122. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  123. package/dist/op/activities/index.d.ts +2 -0
  124. package/dist/op/activities/index.d.ts.map +1 -1
  125. package/dist/op/activities/propose-upgrade.d.ts +75 -0
  126. package/dist/op/activities/propose-upgrade.d.ts.map +1 -0
  127. package/dist/op/activity-contract-registry.d.ts.map +1 -1
  128. package/dist/op/activity-registry.d.ts.map +1 -1
  129. package/dist/op/discover.d.ts.map +1 -1
  130. package/dist/op/gate-approval.d.ts.map +1 -1
  131. package/dist/op/gate-name.d.ts +7 -0
  132. package/dist/op/gate-name.d.ts.map +1 -1
  133. package/dist/op/gate-summary.d.ts +16 -0
  134. package/dist/op/gate-summary.d.ts.map +1 -1
  135. package/dist/op/gate.d.ts +16 -2
  136. package/dist/op/gate.d.ts.map +1 -1
  137. package/dist/op/generate-pipeline.d.ts.map +1 -1
  138. package/dist/op/runtimes/local.d.ts.map +1 -1
  139. package/dist/project-root.d.ts +26 -0
  140. package/dist/project-root.d.ts.map +1 -1
  141. package/dist/workspace/__fixtures__/contract-repo.d.ts +37 -0
  142. package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -0
  143. package/dist/workspace/__fixtures__/two-member-workspace.d.ts +11 -0
  144. package/dist/workspace/__fixtures__/two-member-workspace.d.ts.map +1 -0
  145. package/dist/workspace/checks/generated.d.ts +156 -0
  146. package/dist/workspace/checks/generated.d.ts.map +1 -0
  147. package/dist/workspace/checks/ledgers.d.ts +78 -0
  148. package/dist/workspace/checks/ledgers.d.ts.map +1 -0
  149. package/dist/workspace/checks/links.d.ts +25 -0
  150. package/dist/workspace/checks/links.d.ts.map +1 -0
  151. package/dist/workspace/checks/pipelines.d.ts +76 -0
  152. package/dist/workspace/checks/pipelines.d.ts.map +1 -0
  153. package/dist/workspace/checks.d.ts +161 -0
  154. package/dist/workspace/checks.d.ts.map +1 -0
  155. package/dist/workspace/compose-graph.d.ts +115 -0
  156. package/dist/workspace/compose-graph.d.ts.map +1 -0
  157. package/dist/workspace/compose-reports.d.ts +68 -0
  158. package/dist/workspace/compose-reports.d.ts.map +1 -0
  159. package/dist/workspace/declaration.d.ts +220 -0
  160. package/dist/workspace/declaration.d.ts.map +1 -0
  161. package/dist/workspace/declaration.schema.json +434 -0
  162. package/dist/workspace/generated-files.d.ts +65 -0
  163. package/dist/workspace/generated-files.d.ts.map +1 -0
  164. package/dist/workspace/glob.d.ts +13 -0
  165. package/dist/workspace/glob.d.ts.map +1 -0
  166. package/dist/workspace/graph-cli.d.ts +75 -0
  167. package/dist/workspace/graph-cli.d.ts.map +1 -0
  168. package/dist/workspace/init.d.ts +96 -0
  169. package/dist/workspace/init.d.ts.map +1 -0
  170. package/dist/workspace/jsonc.d.ts +38 -0
  171. package/dist/workspace/jsonc.d.ts.map +1 -0
  172. package/dist/workspace/kinds.d.ts +192 -0
  173. package/dist/workspace/kinds.d.ts.map +1 -0
  174. package/dist/workspace/lineage-check.d.ts +84 -0
  175. package/dist/workspace/lineage-check.d.ts.map +1 -0
  176. package/dist/workspace/lineage-cli.d.ts +31 -0
  177. package/dist/workspace/lineage-cli.d.ts.map +1 -0
  178. package/dist/workspace/lineage-init.d.ts +101 -0
  179. package/dist/workspace/lineage-init.d.ts.map +1 -0
  180. package/dist/workspace/lineage-lock.d.ts +258 -0
  181. package/dist/workspace/lineage-lock.d.ts.map +1 -0
  182. package/dist/workspace/lineage-merge.d.ts +14 -0
  183. package/dist/workspace/lineage-merge.d.ts.map +1 -0
  184. package/dist/workspace/lineage-migrations.d.ts +178 -0
  185. package/dist/workspace/lineage-migrations.d.ts.map +1 -0
  186. package/dist/workspace/lineage-update.d.ts +59 -0
  187. package/dist/workspace/lineage-update.d.ts.map +1 -0
  188. package/dist/workspace/lineage-upgrade-cli.d.ts +40 -0
  189. package/dist/workspace/lineage-upgrade-cli.d.ts.map +1 -0
  190. package/dist/workspace/lineage-upgrade.d.ts +146 -0
  191. package/dist/workspace/lineage-upgrade.d.ts.map +1 -0
  192. package/dist/workspace/lineage-vendor.d.ts +63 -0
  193. package/dist/workspace/lineage-vendor.d.ts.map +1 -0
  194. package/dist/workspace/lineage-version.d.ts +32 -0
  195. package/dist/workspace/lineage-version.d.ts.map +1 -0
  196. package/dist/workspace/links.d.ts +150 -0
  197. package/dist/workspace/links.d.ts.map +1 -0
  198. package/dist/workspace/ls.d.ts +99 -0
  199. package/dist/workspace/ls.d.ts.map +1 -0
  200. package/dist/workspace/member-commands.d.ts +139 -0
  201. package/dist/workspace/member-commands.d.ts.map +1 -0
  202. package/dist/workspace/member-handles.d.ts +54 -0
  203. package/dist/workspace/member-handles.d.ts.map +1 -0
  204. package/dist/workspace/member-pipeline.d.ts +143 -0
  205. package/dist/workspace/member-pipeline.d.ts.map +1 -0
  206. package/dist/workspace/member-run.d.ts +67 -0
  207. package/dist/workspace/member-run.d.ts.map +1 -0
  208. package/dist/workspace/reason-codes.d.ts +65 -0
  209. package/dist/workspace/reason-codes.d.ts.map +1 -0
  210. package/dist/workspace/record-source.d.ts +22 -0
  211. package/dist/workspace/record-source.d.ts.map +1 -0
  212. package/dist/workspace/records-cli.d.ts +81 -0
  213. package/dist/workspace/records-cli.d.ts.map +1 -0
  214. package/dist/workspace/records.d.ts +118 -0
  215. package/dist/workspace/records.d.ts.map +1 -0
  216. package/dist/workspace/root-refusal.d.ts +53 -0
  217. package/dist/workspace/root-refusal.d.ts.map +1 -0
  218. package/dist/workspace/status.d.ts +150 -0
  219. package/dist/workspace/status.d.ts.map +1 -0
  220. package/dist/workspace/template-manifest.d.ts +90 -0
  221. package/dist/workspace/template-manifest.d.ts.map +1 -0
  222. package/dist/workspace/tree.d.ts +36 -0
  223. package/dist/workspace/tree.d.ts.map +1 -0
  224. package/dist/workspace/trust/attestor.d.ts +67 -0
  225. package/dist/workspace/trust/attestor.d.ts.map +1 -0
  226. package/dist/workspace/trust/policy.d.ts +111 -0
  227. package/dist/workspace/trust/policy.d.ts.map +1 -0
  228. package/dist/workspace/trust/provenance.d.ts +67 -0
  229. package/dist/workspace/trust/provenance.d.ts.map +1 -0
  230. package/dist/workspace/trust/ssh-commit.d.ts +43 -0
  231. package/dist/workspace/trust/ssh-commit.d.ts.map +1 -0
  232. package/dist/workspace/trust/test-repo.d.ts +42 -0
  233. package/dist/workspace/trust/test-repo.d.ts.map +1 -0
  234. package/dist/workspace/trust/verify-cli.d.ts +9 -0
  235. package/dist/workspace/trust/verify-cli.d.ts.map +1 -0
  236. package/dist/workspace/trust/verify.d.ts +66 -0
  237. package/dist/workspace/trust/verify.d.ts.map +1 -0
  238. package/dist/workspace/which-chant.d.ts +58 -0
  239. package/dist/workspace/which-chant.d.ts.map +1 -0
  240. package/dist/workspace/workspace-kinds.schema.json +133 -0
  241. package/package.json +4 -3
  242. package/src/audit/core.ts +13 -2
  243. package/src/audit/discover.ts +102 -36
  244. package/src/audit/report-html.ts +3 -1
  245. package/src/audit/report-model.ts +14 -1
  246. package/src/audit/terraform-state.test.ts +81 -1
  247. package/src/audit/terraform-state.ts +25 -0
  248. package/src/cli/command-group.ts +7 -0
  249. package/src/cli/commands/audit-truncation.test.ts +167 -0
  250. package/src/cli/commands/audit.ts +132 -29
  251. package/src/cli/commands/check-lexicon-examples.ts +2 -1
  252. package/src/cli/commands/check-lexicon.ts +24 -0
  253. package/src/cli/commands/doctor.test.ts +30 -0
  254. package/src/cli/commands/doctor.ts +21 -0
  255. package/src/cli/commands/import-agents.ts +15 -4
  256. package/src/cli/commands/init.ts +76 -25
  257. package/src/cli/commands/lint.test.ts +52 -1
  258. package/src/cli/commands/lint.ts +41 -71
  259. package/src/cli/commands/onboard.ts +15 -0
  260. package/src/cli/commands/update.ts +18 -5
  261. package/src/cli/commands/vendor.ts +11 -6
  262. package/src/cli/conflict-check.test.ts +18 -1
  263. package/src/cli/conflict-check.ts +13 -0
  264. package/src/cli/discovery-skip.test.ts +168 -0
  265. package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +38 -0
  266. package/src/cli/handlers/build-member.test.ts +127 -0
  267. package/src/cli/handlers/build.test.ts +11 -0
  268. package/src/cli/handlers/build.ts +35 -1
  269. package/src/cli/handlers/components.test.ts +127 -0
  270. package/src/cli/handlers/components.ts +70 -8
  271. package/src/cli/handlers/dev.ts +6 -1
  272. package/src/cli/handlers/fan-out.ts +9 -2
  273. package/src/cli/handlers/graph.test.ts +20 -0
  274. package/src/cli/handlers/graph.ts +4 -2
  275. package/src/cli/handlers/init.ts +54 -1
  276. package/src/cli/handlers/lifecycle.ts +2 -1
  277. package/src/cli/handlers/misc.ts +16 -0
  278. package/src/cli/handlers/operator.test.ts +68 -0
  279. package/src/cli/handlers/operator.ts +39 -10
  280. package/src/cli/handlers/promote.test.ts +377 -0
  281. package/src/cli/handlers/promote.ts +391 -0
  282. package/src/cli/handlers/run-generate-member.test.ts +83 -0
  283. package/src/cli/handlers/run-generate.test.ts +158 -0
  284. package/src/cli/handlers/run-generate.ts +168 -0
  285. package/src/cli/handlers/run.test.ts +47 -0
  286. package/src/cli/handlers/run.ts +41 -6
  287. package/src/cli/handlers/vendor.ts +147 -43
  288. package/src/cli/main.test.ts +137 -1
  289. package/src/cli/main.ts +290 -24
  290. package/src/cli/mcp/op-tools.ts +2 -1
  291. package/src/cli/path-lexicon-messages.test.ts +152 -0
  292. package/src/cli/plugins.ts +43 -5
  293. package/src/cli/registry.ts +46 -1
  294. package/src/cli/static-config-read.test.ts +208 -0
  295. package/src/codegen/fetch.test.ts +63 -1
  296. package/src/codegen/fetch.ts +30 -1
  297. package/src/components/__fixtures__/alb-ecs-service.json +0 -8
  298. package/src/components/capability-plugin-loader.ts +3 -1
  299. package/src/components/cli-support.ts +9 -3
  300. package/src/components/discover.test.ts +27 -57
  301. package/src/components/discover.ts +20 -51
  302. package/src/components/driver.test.ts +90 -12
  303. package/src/components/driver.ts +43 -5
  304. package/src/components/fan-out-support.ts +2 -1
  305. package/src/components/gate-plan.ts +53 -0
  306. package/src/components/pilots/README.md +1 -1
  307. package/src/components/pilots/alb-ecs.pilot.ts +7 -7
  308. package/src/components/presets/ecs-fargate.ts +10 -5
  309. package/src/components/promote.test.ts +645 -0
  310. package/src/components/promote.ts +641 -0
  311. package/src/components/sandbox/driver.ts +8 -8
  312. package/src/components/sandbox/run.ts +4 -1
  313. package/src/components/verbs/build-archive.ts +27 -21
  314. package/src/components/verbs/run-agent.ts +3 -5
  315. package/src/config-static.test.ts +136 -0
  316. package/src/config-static.ts +232 -0
  317. package/src/config.test.ts +40 -0
  318. package/src/config.ts +161 -11
  319. package/src/content-digest.test.ts +120 -0
  320. package/src/content-digest.ts +21 -0
  321. package/src/discovery/files.test.ts +127 -1
  322. package/src/discovery/files.ts +91 -59
  323. package/src/discovery/fold-import.ts +127 -9
  324. package/src/discovery/index.ts +1 -0
  325. package/src/discovery/sandbox/driver.ts +73 -35
  326. package/src/discovery/sandbox/fork-diagnostic.test.ts +136 -13
  327. package/src/discovery/sandbox/fork.ts +83 -43
  328. package/src/discovery/sandbox/path-lexicon-multifile.test.ts +285 -0
  329. package/src/discovery/sandbox/path-lexicon-parity.test.ts +162 -0
  330. package/src/discovery/walk.test.ts +263 -0
  331. package/src/discovery/walk.ts +334 -0
  332. package/src/effect-receipt.ts +2 -2
  333. package/src/graph-ir.ts +18 -1
  334. package/src/join-key.test.ts +25 -0
  335. package/src/join-key.ts +39 -0
  336. package/src/lexicon-module.test.ts +203 -0
  337. package/src/lexicon-module.ts +215 -0
  338. package/src/lexicon.ts +105 -1
  339. package/src/lifecycle/build-ledger-store.ts +13 -2
  340. package/src/lifecycle/digest.ts +9 -5
  341. package/src/lifecycle/gate-ledger.test.ts +20 -0
  342. package/src/lifecycle/gate-ledger.ts +28 -6
  343. package/src/lifecycle/git.test.ts +13 -1
  344. package/src/lifecycle/git.ts +221 -176
  345. package/src/lifecycle/lease.ts +25 -10
  346. package/src/lifecycle/legacy-digest.test.ts +206 -0
  347. package/src/lifecycle/legacy-digest.ts +103 -0
  348. package/src/lifecycle/member-ledger.test.ts +319 -0
  349. package/src/lifecycle/member-ledger.ts +145 -0
  350. package/src/lifecycle/plan-digest.ts +7 -7
  351. package/src/lifecycle/release-ledger.test.ts +20 -0
  352. package/src/lifecycle/release-ledger.ts +53 -3
  353. package/src/lifecycle/status.test.ts +151 -1
  354. package/src/lifecycle/status.ts +228 -7
  355. package/src/lint/rules/comp/comp003-mutating-no-rollback.ts +2 -3
  356. package/src/op/activities/activity-contracts.ts +37 -0
  357. package/src/op/activities/index.ts +10 -0
  358. package/src/op/activities/propose-upgrade.ts +206 -0
  359. package/src/op/activity-contract-registry.ts +18 -4
  360. package/src/op/activity-registry.ts +26 -0
  361. package/src/op/discover.test.ts +14 -4
  362. package/src/op/discover.ts +19 -23
  363. package/src/op/gate-approval.ts +3 -1
  364. package/src/op/gate-name.ts +8 -0
  365. package/src/op/gate-summary.test.ts +14 -0
  366. package/src/op/gate-summary.ts +25 -2
  367. package/src/op/gate.ts +36 -7
  368. package/src/op/generate-pipeline.test.ts +14 -5
  369. package/src/op/generate-pipeline.ts +2 -1
  370. package/src/op/runtimes/local.ts +2 -1
  371. package/src/project-root.test.ts +66 -1
  372. package/src/project-root.ts +48 -0
  373. package/src/workspace/__fixtures__/contract-repo.ts +100 -0
  374. package/src/workspace/__fixtures__/two-member-workspace.ts +35 -0
  375. package/src/workspace/behold-kinds.test.ts +162 -0
  376. package/src/workspace/check-contract.test.ts +127 -0
  377. package/src/workspace/check.schema.json +347 -0
  378. package/src/workspace/checks/generated.test.ts +287 -0
  379. package/src/workspace/checks/generated.ts +475 -0
  380. package/src/workspace/checks/ledgers.test.ts +139 -0
  381. package/src/workspace/checks/ledgers.ts +246 -0
  382. package/src/workspace/checks/links.ts +183 -0
  383. package/src/workspace/checks/pipelines.test.ts +133 -0
  384. package/src/workspace/checks/pipelines.ts +225 -0
  385. package/src/workspace/checks-repo.test.ts +71 -0
  386. package/src/workspace/checks.test.ts +450 -0
  387. package/src/workspace/checks.ts +461 -0
  388. package/src/workspace/compose-graph.test.ts +146 -0
  389. package/src/workspace/compose-graph.ts +228 -0
  390. package/src/workspace/compose-reports.test.ts +109 -0
  391. package/src/workspace/compose-reports.ts +186 -0
  392. package/src/workspace/declaration.schema.json +434 -0
  393. package/src/workspace/declaration.test.ts +347 -0
  394. package/src/workspace/declaration.ts +623 -0
  395. package/src/workspace/generated-files.test.ts +114 -0
  396. package/src/workspace/generated-files.ts +94 -0
  397. package/src/workspace/glob.ts +49 -0
  398. package/src/workspace/graph-cli.ts +220 -0
  399. package/src/workspace/graph-contract.test.ts +204 -0
  400. package/src/workspace/graph.schema.json +662 -0
  401. package/src/workspace/init.test.ts +223 -0
  402. package/src/workspace/init.ts +585 -0
  403. package/src/workspace/jsonc.test.ts +65 -0
  404. package/src/workspace/jsonc.ts +231 -0
  405. package/src/workspace/kinds.test.ts +288 -0
  406. package/src/workspace/kinds.ts +506 -0
  407. package/src/workspace/lineage-check.ts +264 -0
  408. package/src/workspace/lineage-cli.ts +114 -0
  409. package/src/workspace/lineage-init.test.ts +231 -0
  410. package/src/workspace/lineage-init.ts +365 -0
  411. package/src/workspace/lineage-lock.test.ts +224 -0
  412. package/src/workspace/lineage-lock.ts +357 -0
  413. package/src/workspace/lineage-merge.ts +44 -0
  414. package/src/workspace/lineage-migrations.test.ts +178 -0
  415. package/src/workspace/lineage-migrations.ts +343 -0
  416. package/src/workspace/lineage-update.ts +160 -0
  417. package/src/workspace/lineage-upgrade-cli.ts +151 -0
  418. package/src/workspace/lineage-upgrade.test.ts +474 -0
  419. package/src/workspace/lineage-upgrade.ts +624 -0
  420. package/src/workspace/lineage-vendor.test.ts +126 -0
  421. package/src/workspace/lineage-vendor.ts +215 -0
  422. package/src/workspace/lineage-version.ts +127 -0
  423. package/src/workspace/links.test.ts +344 -0
  424. package/src/workspace/links.ts +331 -0
  425. package/src/workspace/ls-contract.test.ts +200 -0
  426. package/src/workspace/ls.schema.json +190 -0
  427. package/src/workspace/ls.ts +254 -0
  428. package/src/workspace/member-commands.e2e.test.ts +244 -0
  429. package/src/workspace/member-commands.test.ts +217 -0
  430. package/src/workspace/member-commands.ts +540 -0
  431. package/src/workspace/member-handles.test.ts +76 -0
  432. package/src/workspace/member-handles.ts +228 -0
  433. package/src/workspace/member-pipeline.test.ts +135 -0
  434. package/src/workspace/member-pipeline.ts +317 -0
  435. package/src/workspace/member-run.ts +146 -0
  436. package/src/workspace/read-contract.test.ts +129 -0
  437. package/src/workspace/reason-codes.test.ts +113 -0
  438. package/src/workspace/reason-codes.ts +81 -0
  439. package/src/workspace/record-source.ts +117 -0
  440. package/src/workspace/records-cli.ts +198 -0
  441. package/src/workspace/records-contract.test.ts +93 -0
  442. package/src/workspace/records.schema.json +150 -0
  443. package/src/workspace/records.test.ts +243 -0
  444. package/src/workspace/records.ts +384 -0
  445. package/src/workspace/root-refusal.ts +97 -0
  446. package/src/workspace/status-contract.test.ts +308 -0
  447. package/src/workspace/status.schema.json +210 -0
  448. package/src/workspace/status.ts +412 -0
  449. package/src/workspace/template-manifest.test.ts +79 -0
  450. package/src/workspace/template-manifest.ts +221 -0
  451. package/src/workspace/tree.ts +132 -0
  452. package/src/workspace/trust/attestor.ts +93 -0
  453. package/src/workspace/trust/policy.ts +306 -0
  454. package/src/workspace/trust/provenance.ts +181 -0
  455. package/src/workspace/trust/ssh-commit.ts +138 -0
  456. package/src/workspace/trust/test-repo.ts +138 -0
  457. package/src/workspace/trust/trust.test.ts +397 -0
  458. package/src/workspace/trust/verify-cli.ts +56 -0
  459. package/src/workspace/trust/verify.ts +194 -0
  460. package/src/workspace/which-chant.test.ts +117 -0
  461. package/src/workspace/which-chant.ts +151 -0
  462. package/src/workspace/workspace-kinds.schema.json +133 -0
@@ -0,0 +1,450 @@
1
+ import { execFileSync, spawnSync } from "node:child_process";
2
+ import { existsSync, mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { dirname, join, resolve } from "node:path";
5
+ import { pathToFileURL } from "node:url";
6
+ import { afterAll, afterEach, beforeEach, describe, expect, test, vi } from "vitest";
7
+ import { parseArgs } from "../cli/main";
8
+ import { runDeclarationChecks, WORKSPACE_CHECKS, workspaceCheckRules } from "./checks";
9
+ import { runWorkspaceCheck } from "./lineage-check";
10
+
11
+ const scratch: string[] = [];
12
+ afterAll(() => {
13
+ for (const d of scratch) rmSync(d, { recursive: true, force: true });
14
+ });
15
+ function repo(files: Record<string, string>): string {
16
+ const root = realpathSync(mkdtempSync(join(tmpdir(), "chant-wsp-")));
17
+ scratch.push(root);
18
+ execFileSync("git", ["init", "-q"], { cwd: root });
19
+ for (const [path, text] of Object.entries(files)) {
20
+ mkdirSync(dirname(join(root, path)), { recursive: true });
21
+ writeFileSync(join(root, path), text);
22
+ }
23
+ return root;
24
+ }
25
+
26
+ const declaration = (members: unknown[], extra: Record<string, unknown> = {}) =>
27
+ JSON.stringify({ name: "acme", schema: 1, members, ...extra }, null, 2);
28
+
29
+ const kindsPackage = (dir: string, kinds: unknown[]) => ({
30
+ [`${dir}/package.json`]: JSON.stringify({ name: dir.split("/").pop(), version: "1.0.0", exports: { "./workspace-kinds": "./k.json" } }),
31
+ [`${dir}/k.json`]: JSON.stringify({ schema: 1, kinds }),
32
+ });
33
+ const tf = { name: "terraform", description: "a Terraform root module, with a main.tf", precedence: 400, probe: { anyFile: ["main.tf"] } };
34
+
35
+ const ids = async (root: string) => (await runDeclarationChecks(root)).diagnostics.map((d) => `${d.ruleId}:${d.entity ?? ""}`);
36
+
37
+ describe("the WSP catalog", () => {
38
+ test("ids are WSP and three digits, unique and in order", async () => {
39
+ const list = WORKSPACE_CHECKS.map((c) => c.id);
40
+ expect(list.every((id) => /^WSP\d{3}$/.test(id))).toBe(true);
41
+ expect([...list].sort()).toEqual(list);
42
+ expect(new Set(list).size).toBe(list.length);
43
+ });
44
+
45
+ test("the checks D3 and D6 say always fail are fixed, and so are the ledger checks", async () => {
46
+ const fixed = WORKSPACE_CHECKS.filter((c) => !c.configurable).map((c) => c.name);
47
+ expect(fixed).toEqual([
48
+ "declaration-unreadable",
49
+ "kinds-unreadable",
50
+ "kind-unknown",
51
+ "kind-probe-tie",
52
+ "other-claimed",
53
+ "check-settings-invalid",
54
+ // A member that ignores either writes its records or marks its resources as another's (#2538).
55
+ "ownership-stack-shared",
56
+ "flat-ledger-environment-shared",
57
+ "link-target-unknown",
58
+ "link-kind-unknown",
59
+ ]);
60
+ });
61
+
62
+ test("no two modules share an id: ledgers, pipelines, links and generated files each have their own range (#2641)", async () => {
63
+ const list = WORKSPACE_CHECKS.map((c) => c.id);
64
+ expect(list.filter((id) => /^WSP(07|08|09|10)\d$/.test(id))).toEqual([
65
+ "WSP071", "WSP072", "WSP073",
66
+ "WSP081", "WSP082", "WSP083",
67
+ "WSP091", "WSP092", "WSP093", "WSP094", "WSP095", "WSP096", "WSP097",
68
+ "WSP101", "WSP102", "WSP103", "WSP104", "WSP105", "WSP106",
69
+ ]);
70
+ });
71
+
72
+ test("each check is also a rule for the SARIF reporter's metadata", async () => {
73
+ expect(workspaceCheckRules().map((r) => r.id)).toEqual(WORKSPACE_CHECKS.map((c) => c.id));
74
+ });
75
+ });
76
+
77
+ describe("declaration checks (#2535)", () => {
78
+ test("a clean workspace has no finding", async () => {
79
+ const root = repo({
80
+ "chant.workspace.json": declaration([
81
+ { name: "root", dir: ".", kind: "chant" },
82
+ { name: "api", dir: "api", kind: "chant" },
83
+ { name: "examples", kind: "examples", glob: "examples/*" },
84
+ ]),
85
+ "chant.config.ts": "",
86
+ "api/chant.config.ts": "",
87
+ "examples/one/chant.config.ts": "",
88
+ });
89
+ expect(await runDeclarationChecks(root)).toEqual({ file: "chant.workspace.json", diagnostics: [], suppressed: [], links: [], ok: true });
90
+ });
91
+
92
+ test("an unknown kind fails closed and lists the known kinds", async () => {
93
+ const root = repo({ "chant.workspace.json": declaration([{ name: "infra", dir: "infra", kind: "terraform" }]), "infra/main.tf": "" });
94
+ const report = await runDeclarationChecks(root);
95
+ expect(report.ok).toBe(false);
96
+ expect(report.diagnostics).toEqual([
97
+ {
98
+ file: "chant.workspace.json",
99
+ line: 8,
100
+ column: 15,
101
+ ruleId: "WSP003",
102
+ severity: "error",
103
+ message: "member infra has kind terraform, which no built-in kind or pinned package supplies; known kinds: chant, other, workspace",
104
+ entity: "infra",
105
+ },
106
+ ]);
107
+ });
108
+
109
+ test("a pinned package supplies a kind, read as data", async () => {
110
+ const root = repo({
111
+ "chant.workspace.json": declaration([{ name: "infra", dir: "infra", kind: "terraform" }], { pins: [{ path: "plugins/tf" }] }),
112
+ ...kindsPackage("plugins/tf", [tf]),
113
+ "infra/main.tf": "",
114
+ });
115
+ expect(await ids(root)).toEqual([]);
116
+ });
117
+
118
+ test("a pin whose kinds can't be read is WSP002, and its kinds are unknown", async () => {
119
+ const root = repo({
120
+ "chant.workspace.json": declaration([{ name: "infra", dir: "infra", kind: "terraform" }], { pins: [{ package: "tf-kinds", version: "1.0.0" }] }),
121
+ "infra/main.tf": "",
122
+ });
123
+ // Ordered by where they sit in the file: the member comes before pins.
124
+ expect(await ids(root)).toEqual(["WSP003:infra", "WSP002:"]);
125
+ });
126
+
127
+ test("an other member needs because, is a warning, and fails when a registered probe claims it", async () => {
128
+ const root = repo({
129
+ "chant.workspace.json": declaration([
130
+ { name: "docs", dir: "docs", kind: "other", because: "an npm package" },
131
+ { name: "legacy", dir: "legacy", kind: "other", because: "old scripts" },
132
+ ]),
133
+ "docs/package.json": "{}",
134
+ "legacy/chant.config.ts": "",
135
+ });
136
+ const report = await runDeclarationChecks(root);
137
+ expect(report.diagnostics.map((d) => `${d.ruleId}:${d.entity}:${d.severity}`)).toEqual([
138
+ "WSP009:docs:warning",
139
+ "WSP009:legacy:warning",
140
+ "WSP008:legacy:error",
141
+ ]);
142
+ expect(report.diagnostics[2].message).toMatch(/kind chant claims legacy: it is a chant project/);
143
+ expect(report.ok).toBe(false);
144
+ });
145
+
146
+ test("a plugin kind's probe also claims an other directory", async () => {
147
+ const root = repo({
148
+ "chant.workspace.json": declaration([{ name: "infra", dir: "infra", kind: "other", because: "not ours" }], { pins: [{ path: "plugins/tf" }] }),
149
+ ...kindsPackage("plugins/tf", [tf]),
150
+ "infra/main.tf": "",
151
+ });
152
+ expect(await ids(root)).toEqual(["WSP009:infra", "WSP008:infra"]);
153
+ });
154
+
155
+ test("overlapping probes: the precedence decides, and a tie fails", async () => {
156
+ const tofu = { ...tf, name: "opentofu" };
157
+ const root = repo({
158
+ "chant.workspace.json": declaration(
159
+ [
160
+ { name: "infra", dir: "infra", kind: "terraform" },
161
+ { name: "app", dir: "app", kind: "terraform" },
162
+ ],
163
+ { pins: [{ path: "plugins/tf" }, { path: "plugins/tofu" }] },
164
+ ),
165
+ ...kindsPackage("plugins/tf", [tf]),
166
+ ...kindsPackage("plugins/tofu", [tofu]),
167
+ "infra/main.tf": "",
168
+ "app/main.tf": "",
169
+ "app/chant.config.ts": "",
170
+ });
171
+ const report = await runDeclarationChecks(root);
172
+ const infra = report.diagnostics.find((d) => d.entity === "infra");
173
+ const app = report.diagnostics.find((d) => d.entity === "app");
174
+ expect(infra).toMatchObject({ ruleId: "WSP006", message: expect.stringMatching(/opentofu \(plugins\/tofu\) and terraform \(plugins\/tf\) all claim infra with precedence 400; a tie fails/) });
175
+ // chant (500) outranks terraform (400): no tie, but the declared kind is not the one picked.
176
+ expect(app).toMatchObject({ ruleId: "WSP007", message: expect.stringMatching(/declared terraform, and kind chant \(precedence 500\) claims app first/) });
177
+ });
178
+
179
+ test("the root member is not outranked by the workspace's own declaration", async () => {
180
+ const root = repo({ "chant.workspace.json": declaration([{ name: "root", dir: ".", kind: "chant" }]), "chant.config.ts": "" });
181
+ expect(await ids(root)).toEqual([]);
182
+ });
183
+
184
+ test("a missing directory, a failed probe and an empty group", async () => {
185
+ const root = repo({
186
+ "chant.workspace.json": declaration([
187
+ { name: "gone", dir: "gone", kind: "chant" },
188
+ { name: "empty", dir: "empty", kind: "chant" },
189
+ { name: "samples", kind: "examples", glob: "samples/*" },
190
+ ]),
191
+ "empty/README.md": "",
192
+ });
193
+ expect(await ids(root)).toEqual(["WSP004:gone", "WSP005:empty", "WSP010:samples"]);
194
+ });
195
+
196
+ test("a declaration that can't be read is one WSP001 finding, with its location", async () => {
197
+ const root = repo({ "chant.workspace.json": declaration([{ name: "a", dir: "a", kind: "chant", dependsOn: [] }]) });
198
+ const report = await runDeclarationChecks(root);
199
+ expect(report.ok).toBe(false);
200
+ expect(report.diagnostics).toHaveLength(1);
201
+ expect(report.diagnostics[0]).toMatchObject({ ruleId: "WSP001", line: 9, message: expect.stringMatching(/^declaration-invalid: unknown field "dependsOn"/) });
202
+ });
203
+ });
204
+
205
+ describe("severity and suppression from the declaration", () => {
206
+ const other = { name: "docs", dir: "docs", kind: "other", because: "an npm package" };
207
+
208
+ test("checks sets a configurable check's severity, or turns it off", async () => {
209
+ const files = { "docs/x": "", "samples/.keep": "" };
210
+ const group = { name: "samples", kind: "examples", glob: "samples/*" };
211
+ const error = await runDeclarationChecks(repo({ ...files, "chant.workspace.json": declaration([other, group], { checks: { WSP009: "error" } }) }));
212
+ expect(error.diagnostics.map((d) => `${d.ruleId}:${d.severity}`)).toEqual(["WSP009:error", "WSP010:warning"]);
213
+ expect(error.ok).toBe(false);
214
+ const off = await runDeclarationChecks(repo({ ...files, "chant.workspace.json": declaration([other, group], { checks: { WSP009: "off", WSP010: "info" } }) }));
215
+ expect(off.diagnostics.map((d) => `${d.ruleId}:${d.severity}`)).toEqual(["WSP010:info"]);
216
+ });
217
+
218
+ test("an entry's suppress moves the finding to suppressed, with its reason", async () => {
219
+ const root = repo({ "docs/x": "", "chant.workspace.json": declaration([{ ...other, suppress: [{ check: "WSP009", because: "decided in #2557" }] }]) });
220
+ const report = await runDeclarationChecks(root);
221
+ expect(report.diagnostics).toEqual([]);
222
+ expect(report.suppressed).toEqual([expect.objectContaining({ ruleId: "WSP009", entity: "docs", reason: "decided in #2557" })]);
223
+ expect(report.ok).toBe(true);
224
+ });
225
+
226
+ test("a fixed check can't be turned down or suppressed, and an unknown id is refused", async () => {
227
+ const root = repo({
228
+ "legacy/chant.config.ts": "",
229
+ "chant.workspace.json": declaration(
230
+ [{ name: "legacy", dir: "legacy", kind: "other", because: "old", suppress: [{ check: "WSP008", because: "please" }, { check: "WSP999", because: "typo" }] }],
231
+ { checks: { WSP003: "off" } },
232
+ ),
233
+ });
234
+ const report = await runDeclarationChecks(root);
235
+ expect(report.suppressed).toEqual([]);
236
+ expect(report.diagnostics.map((d) => d.ruleId).sort()).toEqual(["WSP008", "WSP009", "WSP011", "WSP011", "WSP011"]);
237
+ const settings = report.diagnostics.filter((d) => d.ruleId === "WSP011").map((d) => d.message);
238
+ expect(settings).toEqual([
239
+ "legacy suppresses WSP008, which is fixed: it can't be turned down or suppressed",
240
+ expect.stringMatching(/^legacy suppresses WSP999, which is not a declaration check; known checks: WSP001, /),
241
+ "checks sets WSP003, which is fixed: it can't be turned down or suppressed",
242
+ ]);
243
+ });
244
+
245
+ test("the schema refuses a checks key that isn't a WSP id and a suppression without because", async () => {
246
+ expect(await ids(repo({ "chant.workspace.json": declaration([], { checks: { lint: "off" } }) }))).toEqual(["WSP001:"]);
247
+ expect(await ids(repo({ "docs/x": "", "chant.workspace.json": declaration([{ ...other, suppress: [{ check: "WSP009" }] }]) }))).toEqual(["WSP001:"]);
248
+ });
249
+ });
250
+
251
+ describe("chant workspace check with a declaration", () => {
252
+ let out: string[];
253
+ let err: string[];
254
+ beforeEach(() => {
255
+ out = [];
256
+ err = [];
257
+ vi.spyOn(console, "log").mockImplementation((...a: unknown[]) => void out.push(a.join(" ")));
258
+ vi.spyOn(console, "error").mockImplementation((...a: unknown[]) => void err.push(a.join(" ")));
259
+ });
260
+ afterEach(() => vi.restoreAllMocks());
261
+
262
+ const run = (cwd: string, ...argv: string[]) => {
263
+ vi.spyOn(process, "cwd").mockReturnValue(cwd);
264
+ return runWorkspaceCheck({ args: parseArgs(["workspace", "check", ...argv]), plugins: [] } as never);
265
+ };
266
+
267
+ const files = {
268
+ "chant.workspace.json": declaration([
269
+ { name: "docs", dir: "docs", kind: "other", because: "an npm package", suppress: [{ check: "WSP009", because: "decided" }] },
270
+ { name: "infra", dir: "infra", kind: "terraform" },
271
+ ]),
272
+ "docs/x": "",
273
+ "infra/main.tf": "",
274
+ };
275
+
276
+ test("--json adds the declaration's report to the lock report, and the exit code covers both", async () => {
277
+ const root = repo(files);
278
+ mkdirSync(join(root, "sub"));
279
+ expect(await run(join(root, "sub"), "--json")).toBe(1);
280
+ const doc = JSON.parse(out.join("\n"));
281
+ expect(doc).toMatchObject({ lock: null, ok: false, findings: [], declaration: { file: "../chant.workspace.json", ok: false } });
282
+ expect(doc.declaration.diagnostics.map((d: { ruleId: string }) => d.ruleId)).toEqual(["WSP003"]);
283
+ expect(doc.declaration.suppressed.map((d: { ruleId: string; reason: string }) => `${d.ruleId}:${d.reason}`)).toEqual(["WSP009:decided"]);
284
+ });
285
+
286
+ test("--format sarif goes through lint's reporter, with the suppression marked external", async () => {
287
+ const root = repo(files);
288
+ expect(await run(root, "--format", "sarif")).toBe(1);
289
+ const sarif = JSON.parse(out.join("\n"));
290
+ expect(sarif.version).toBe("2.1.0");
291
+ const results = sarif.runs[0].results as { ruleId: string; level: string; suppressions?: { kind: string; justification: string }[] }[];
292
+ expect(results.map((r) => `${r.ruleId}:${r.level}`)).toEqual(["WSP003:error", "WSP009:warning"]);
293
+ expect(results[1].suppressions).toEqual([{ kind: "external", justification: "decided" }]);
294
+ expect(sarif.runs[0].tool.driver.rules.map((r: { id: string }) => r.id)).toEqual(["WSP003", "WSP009"]);
295
+ });
296
+
297
+ test("--format json prints the read-contract document, with the active diagnostics in lint's shape (#2536)", async () => {
298
+ const root = repo(files);
299
+ expect(await run(root, "--format", "json")).toBe(1);
300
+ const doc = JSON.parse(out.join("\n")) as { contract: number; declaration: { diagnostics: { ruleId: string }[] } };
301
+ expect(doc.contract).toBe(1);
302
+ expect(doc.declaration.diagnostics.map((d) => d.ruleId)).toEqual(["WSP003"]);
303
+ });
304
+
305
+ test("stylish lists the findings and the suppressed ones", async () => {
306
+ const root = repo(files);
307
+ expect(await run(root)).toBe(1);
308
+ const text = out.join("\n");
309
+ expect(text).toMatch(/chant\.workspace\.json/);
310
+ expect(text).toMatch(/WSP003/);
311
+ expect(text).toMatch(/Suppressed/);
312
+ expect(err.join("\n")).not.toMatch(/nothing to check/);
313
+ });
314
+
315
+ test("with no declaration the report is the lock report alone", async () => {
316
+ const root = repo({ "README.md": "" });
317
+ expect(await run(root, "--json")).toBe(0);
318
+ expect(JSON.parse(out.join("\n"))).toEqual({ lock: null, ok: true, findings: [] });
319
+ });
320
+
321
+ test("an unknown --format is refused", async () => {
322
+ expect(await run(repo({}), "--format", "xml")).toBe(1);
323
+ expect(err.join("\n")).toMatch(/--format xml is not a check format/);
324
+ });
325
+ });
326
+
327
+ describe("member checks from chant workspace check (#2641)", () => {
328
+ let out: string[];
329
+ beforeEach(() => {
330
+ out = [];
331
+ vi.spyOn(console, "log").mockImplementation((...a: unknown[]) => void out.push(a.join(" ")));
332
+ vi.spyOn(console, "error").mockImplementation(() => undefined);
333
+ });
334
+ afterEach(() => vi.restoreAllMocks());
335
+
336
+ const run = (cwd: string, ...argv: string[]) => {
337
+ vi.spyOn(process, "cwd").mockReturnValue(cwd);
338
+ return runWorkspaceCheck({ args: parseArgs(["workspace", "check", ...argv]), plugins: [] } as never);
339
+ };
340
+
341
+ /** A generator that writes "fresh" to its -o path. */
342
+ const writer = `const i = process.argv.indexOf("-o"); require("node:fs").writeFileSync(process.argv[i + 1], "fresh\\n");`;
343
+ const deploy = JSON.stringify({ schema: 1, files: [{ path: ".github/workflows/deploy.yml", command: "chant build --components --generate github" }] });
344
+
345
+ /**
346
+ * The root member and api both write the flat ledger layout (api runs a
347
+ * chant below the member-ledger floor) and share prod; api and web both
348
+ * record deploy.yml; tools' out.txt differs from what its generator writes.
349
+ */
350
+ const files = {
351
+ "chant.workspace.json": declaration([
352
+ { name: "root", dir: ".", kind: "chant" },
353
+ { name: "api", dir: "api", kind: "chant" },
354
+ { name: "web", dir: "web", kind: "chant" },
355
+ { name: "tools", dir: "tools", kind: "other", because: "scripts", generated: [{ path: "out.txt", generator: "node ../gen.cjs -o out.txt" }] },
356
+ ]),
357
+ "chant.config.json": JSON.stringify({ environments: ["prod"] }),
358
+ "api/chant.config.json": JSON.stringify({ environments: ["prod", "staging"] }),
359
+ "api/node_modules/@intentius/chant/package.json": JSON.stringify({ name: "@intentius/chant", version: "0.70.0" }),
360
+ "api/.chant/generated.json": deploy,
361
+ "web/chant.config.json": JSON.stringify({ environments: ["prod"] }),
362
+ "web/.chant/generated.json": deploy,
363
+ "gen.cjs": writer,
364
+ "tools/out.txt": "edited by hand\n",
365
+ };
366
+
367
+ test("a ledger collision, a pipeline collision and a drifted generated file are WSP072, WSP081 and WSP101", async () => {
368
+ const root = repo(files);
369
+ expect(await run(root, "--json", "--generated")).toBe(1);
370
+ const doc = JSON.parse(out.join("\n"));
371
+ const found = doc.declaration.diagnostics.map((d: { ruleId: string; entity: string; severity: string }) => `${d.ruleId}:${d.entity}:${d.severity}`);
372
+ expect(found).toEqual(["WSP072:root:error", "WSP081:api:error", "WSP009:tools:warning", "WSP101:tools:error"]);
373
+ const message = (id: string) => doc.declaration.diagnostics.find((d: { ruleId: string }) => d.ruleId === id).message;
374
+ expect(message("WSP072")).toMatch(/members root, api both write the flat ledger layout and share the environment "prod"/);
375
+ expect(message("WSP081")).toMatch(/members api, web all declare \.github\/workflows\/deploy\.yml/);
376
+ expect(message("WSP101")).toMatch(/tools\/out\.txt differs from what `node \.\.\/gen\.cjs -o out\.txt` writes/);
377
+ });
378
+
379
+ test("without --generated the declared generator is not run: WSP105, not WSP101", async () => {
380
+ const root = repo(files);
381
+ expect(await run(root, "--json")).toBe(1);
382
+ const found = JSON.parse(out.join("\n")).declaration.diagnostics.map((d: { ruleId: string }) => d.ruleId);
383
+ expect(found).toContain("WSP105");
384
+ expect(found).not.toContain("WSP101");
385
+ });
386
+
387
+ test("a settable member check follows checks and suppress; a fixed one does not", async () => {
388
+ const decl = JSON.parse(files["chant.workspace.json"]) as { members: Record<string, unknown>[]; checks?: Record<string, string> };
389
+ decl.members[0].suppress = [{ check: "WSP072", because: "please" }];
390
+ decl.members[1].suppress = [{ check: "WSP081", because: "web's copy goes away in the next release" }];
391
+ decl.checks = { WSP105: "off" };
392
+ const report = await runDeclarationChecks(repo({ ...files, "chant.workspace.json": JSON.stringify(decl, null, 2) }));
393
+ expect(report.suppressed.map((d) => `${d.ruleId}:${d.entity}`)).toEqual(["WSP081:api"]);
394
+ expect(report.diagnostics.map((d) => d.ruleId).sort()).toEqual(["WSP009", "WSP011", "WSP072"]);
395
+ });
396
+
397
+ test("gather: false runs the declaration checks alone, as chant doctor does", async () => {
398
+ const report = await runDeclarationChecks(repo(files), undefined, { gather: false });
399
+ expect(report.diagnostics.map((d) => d.ruleId)).toEqual(["WSP009"]);
400
+ });
401
+ });
402
+
403
+ describe("chant workspace check runs no member config (#2641)", () => {
404
+ const repoRoot = resolve(import.meta.dirname, "../../../..");
405
+
406
+ /**
407
+ * Both chant members' configs write a marker file when they run. Their
408
+ * ownership and environments are literals, so they are read statically;
409
+ * web's stack comes from the environment, so it can't be.
410
+ */
411
+ const files = (marker: string) => {
412
+ const sideEffect = `import { writeFileSync } from "node:fs";\nwriteFileSync(${JSON.stringify(marker)}, "ran");\n`;
413
+ return {
414
+ "chant.workspace.json": declaration([
415
+ { name: "root", dir: ".", kind: "chant" },
416
+ { name: "api", dir: "api", kind: "chant" },
417
+ { name: "web", dir: "web", kind: "chant" },
418
+ ]),
419
+ "chant.config.ts": `${sideEffect}export default { environments: ["prod"], ownership: { stack: "shop" } };\n`,
420
+ "api/chant.config.ts": `${sideEffect}export default { environments: ["prod"], ownership: { stack: "shop" } };\n`,
421
+ "web/chant.config.ts": `${sideEffect}export default { ownership: { stack: process.env.WEB_STACK ?? "web" } };\n`,
422
+ };
423
+ };
424
+
425
+ test("configs with a side effect are read statically and never run", async () => {
426
+ const marker = join(realpathSync(mkdtempSync(join(tmpdir(), "chant-2641-marker-"))), "ran");
427
+ scratch.push(dirname(marker));
428
+ const report = await runDeclarationChecks(repo(files(marker)));
429
+ expect(existsSync(marker)).toBe(false);
430
+ expect(report.diagnostics.map((d) => `${d.ruleId}:${d.entity}:${d.severity}`)).toEqual([
431
+ "WSP071:root:error",
432
+ "WSP073:web:info",
433
+ ]);
434
+ expect(report.diagnostics[1].message).toMatch(/web\/chant\.config\.ts can't be read without running it/);
435
+ });
436
+
437
+ test("the CLI loads no config before dispatch either", () => {
438
+ const marker = join(realpathSync(mkdtempSync(join(tmpdir(), "chant-2641-marker-"))), "ran");
439
+ scratch.push(dirname(marker));
440
+ const root = repo(files(marker));
441
+ const run = spawnSync(
442
+ process.execPath,
443
+ ["--import", pathToFileURL(join(repoRoot, "node_modules/tsx/dist/loader.mjs")).href, join(repoRoot, "packages/core/src/cli/main.ts"), "workspace", "check", "--json"],
444
+ { cwd: root, encoding: "utf-8", timeout: 60_000, env: { ...process.env, NO_COLOR: "1", TSX_DISABLE_CACHE: "1" } },
445
+ );
446
+ expect(run.status, run.stderr).toBe(1);
447
+ expect(JSON.parse(run.stdout).declaration.diagnostics.map((d: { ruleId: string }) => d.ruleId)).toEqual(["WSP071", "WSP073"]);
448
+ expect(existsSync(marker)).toBe(false);
449
+ });
450
+ });