@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,43 @@
1
+ /**
2
+ * The first attestor: ssh-signed commits, checked against the signers the
3
+ * base policy lists (#2524 D5, ws-001).
4
+ *
5
+ * It does not ask `git verify-commit`. That would read `gpg.ssh.program`,
6
+ * `gpg.ssh.allowedSignersFile` and the rest from git config, which whoever
7
+ * controls the machine can set, and it passes the commit's own date to
8
+ * ssh-keygen as the verification time. Instead it takes the signature and the
9
+ * signed payload out of the commit object itself and hands them to
10
+ * `ssh-keygen -Y verify` with a signers file built from the base policy, in
11
+ * the `git` namespace.
12
+ */
13
+ import type { CommitAttestor } from "./attestor.js";
14
+ import { type Signer } from "./policy.js";
15
+ /** A commit object split into its signed payload and its signature. */
16
+ export interface SignedCommit {
17
+ /** The bytes the signature covers: the commit object without its signature header. */
18
+ payload: Buffer;
19
+ /** The armored signature, or undefined when the commit is unsigned. */
20
+ signature?: string;
21
+ }
22
+ /**
23
+ * Split a raw commit object. `header` is `gpgsig` in a SHA-1 repository and
24
+ * `gpgsig-sha256` in a SHA-256 one; git removes exactly that header, with its
25
+ * continuation lines, to get the bytes it signed.
26
+ */
27
+ export declare function splitSignedCommit(raw: Buffer, header?: string): SignedCommit;
28
+ /**
29
+ * Check a detached ssh signature over `payload` in `namespace` against
30
+ * `signers`. Shared with the rotation check (#2553), which signs signer sets
31
+ * in their own namespace so a commit signature can never stand in for one.
32
+ */
33
+ export declare function verifySshSignature(signers: Signer[], payload: Buffer, signature: string, namespace: string): {
34
+ ok: true;
35
+ principal: string;
36
+ key?: string;
37
+ } | {
38
+ ok: false;
39
+ missing: boolean;
40
+ reason: string;
41
+ };
42
+ export declare const sshCommitAttestor: CommitAttestor;
43
+ //# sourceMappingURL=ssh-commit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssh-commit.d.ts","sourceRoot":"","sources":["../../../src/workspace/trust/ssh-commit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,EAAsC,cAAc,EAAE,MAAM,YAAY,CAAC;AACrF,OAAO,EAAwB,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AAI7D,uEAAuE;AACvE,MAAM,WAAW,YAAY;IAC3B,sFAAsF;IACtF,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,SAAW,GAAG,YAAY,CAsB9E;AAmBD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EAAE,EACjB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAuBjG;AAED,eAAO,MAAM,iBAAiB,EAAE,cAW/B,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Test support for the trust modules: throwaway git repositories whose commits
3
+ * are signed with throwaway ssh keys. Isolated from the user's git config, so a
4
+ * developer's own signing setup never leaks into a test.
5
+ */
6
+ export declare const hasSshKeygen: boolean;
7
+ export interface Key {
8
+ name: string;
9
+ /** Private key file. */
10
+ file: string;
11
+ /** `ssh-ed25519 AAAA...`, no comment. */
12
+ pub: string;
13
+ }
14
+ export declare class TestRepo {
15
+ readonly dir: string;
16
+ private readonly home;
17
+ private readonly scratch;
18
+ constructor(label: string);
19
+ env(extra?: Record<string, string>): NodeJS.ProcessEnv;
20
+ git(args: string[], opts?: {
21
+ env?: Record<string, string>;
22
+ input?: string | Buffer;
23
+ }): string;
24
+ key(name: string): Key;
25
+ write(path: string, text: string): void;
26
+ /** Stage everything and commit, signed with `key` when given. Returns the commit id. */
27
+ commit(message: string, key?: Key, env?: Record<string, string>): string;
28
+ head(): string;
29
+ /**
30
+ * Write a commit object by hand: the current index's tree, `HEAD` as parent,
31
+ * and `signature` spliced in as the gpgsig header. Returns the new commit,
32
+ * which HEAD then points at.
33
+ */
34
+ forgeCommit(message: string, sign: (payload: Buffer) => string): string;
35
+ /** An ssh signature over `payload` in `namespace`, by `key`. */
36
+ sshSign(key: Key, payload: Buffer, namespace: string): string;
37
+ cleanup(): void;
38
+ }
39
+ /** A record kind reading `records/*.md`, and its schema, written into `repo`. */
40
+ export declare function writeRecordKind(repo: TestRepo): string;
41
+ export declare function note(id: string, body?: string): string;
42
+ //# sourceMappingURL=test-repo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-repo.d.ts","sourceRoot":"","sources":["../../../src/workspace/trust/test-repo.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,eAAO,MAAM,YAAY,SAA2E,CAAC;AAErG,MAAM,WAAW,GAAG;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,GAAG,EAAE,MAAM,CAAC;CACb;AAED,qBAAa,QAAQ;IACnB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,KAAK,EAAE,MAAM;IASzB,GAAG,CAAC,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAAG,MAAM,CAAC,UAAU;IAc1D,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,GAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;KAAO,GAAG,MAAM;IAIjG,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG;IAOtB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAKvC,wFAAwF;IACxF,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAAG,MAAM;IAO5E,IAAI,IAAI,MAAM;IAId;;;;OAIG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM;IAgBvE,gEAAgE;IAChE,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAO7D,OAAO,IAAI,IAAI;CAGhB;AAED,iFAAiF;AACjF,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAqBtD;AAED,wBAAgB,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,SAAK,GAAG,MAAM,CAElD"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `chant workspace verify [--base <rev>] [--head <rev>] [--require attested] [--json]`
3
+ * (#2547): check a change against the trust policy at its base. See ./verify.ts.
4
+ *
5
+ * Exit 0 when the change passes, 1 when it does not or cannot be checked.
6
+ */
7
+ import type { CommandContext } from "../../cli/registry.js";
8
+ export declare function runWorkspaceVerify(ctx: CommandContext): Promise<number>;
9
+ //# sourceMappingURL=verify-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-cli.d.ts","sourceRoot":"","sources":["../../../src/workspace/trust/verify-cli.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAOzD,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAqB7E"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Verify a change against the policy at its base (#2524 D5, Threat model).
3
+ *
4
+ * This is what CI runs on a protected branch: every commit in `base..head` is
5
+ * asked of the attestors with the policy read at base, so nothing the change
6
+ * does to its own copy of the policy counts. A change that edits the policy
7
+ * (the signers file or `.chant/trust.json`) is a protected write: every commit
8
+ * that touches those files must be attested by a signer the base policy
9
+ * trusts, and by an admin when the base policy grants the admin role.
10
+ *
11
+ * Attestation is opt-in (#2525 rule 6). Without a signers file at base the
12
+ * check passes and says so, unless `--require attested` asks for more.
13
+ */
14
+ import { type CommitAttestor, type ProvenanceLevel } from "./attestor.js";
15
+ import { type BaseSource, type RecordProvenance } from "./provenance.js";
16
+ import { type ExcludedSigner } from "./policy.js";
17
+ export interface CommitVerdict extends RecordProvenance {
18
+ commit: string;
19
+ subject: string;
20
+ /** A merge that changes nothing of its own; its parents are checked instead. */
21
+ skipped?: string;
22
+ }
23
+ export interface ProtectedWrite {
24
+ commit: string;
25
+ paths: string[];
26
+ level: ProvenanceLevel;
27
+ principal?: string;
28
+ allowed: boolean;
29
+ reason: string;
30
+ }
31
+ export interface ChangeReport {
32
+ base: string | null;
33
+ baseFrom: BaseSource | null;
34
+ head: string | null;
35
+ policy: {
36
+ active: boolean;
37
+ signersPath: string;
38
+ principals: string[];
39
+ excluded: ExcludedSigner[];
40
+ writers: string[];
41
+ problems: string[];
42
+ };
43
+ require: ProvenanceLevel | null;
44
+ commits: CommitVerdict[];
45
+ protectedPaths: string[];
46
+ /** Protected paths whose content differs between the merge base and head. */
47
+ protectedChanged: string[];
48
+ protectedWrites: ProtectedWrite[];
49
+ notes: string[];
50
+ failures: string[];
51
+ ok: boolean;
52
+ }
53
+ export interface VerifyOptions {
54
+ repo: string;
55
+ base?: string;
56
+ head?: string;
57
+ require?: ProvenanceLevel;
58
+ attestors: readonly CommitAttestor[];
59
+ }
60
+ /**
61
+ * Commits in `range` that change any of `paths`, each with the paths it
62
+ * changes. Merges count only for what they change beyond every parent.
63
+ */
64
+ export declare function commitsTouching(repo: string, range: string, paths: string[]): Map<string, string[]>;
65
+ export declare function verifyChange(opts: VerifyOptions): ChangeReport;
66
+ //# sourceMappingURL=verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../../src/workspace/trust/verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,EAA+C,KAAK,UAAU,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACnH,OAAO,EAAiC,KAAK,cAAc,EAAoB,MAAM,UAAU,CAAC;AAEhG,MAAM,WAAW,aAAc,SAAQ,gBAAgB;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,eAAe,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,UAAU,GAAG,IAAI,CAAC;IAC5B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE;QACN,MAAM,EAAE,OAAO,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,QAAQ,EAAE,cAAc,EAAE,CAAC;QAC3B,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IACF,OAAO,EAAE,eAAe,GAAG,IAAI,CAAC;IAChC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,6EAA6E;IAC7E,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,eAAe,EAAE,cAAc,EAAE,CAAC;IAClC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,EAAE,EAAE,OAAO,CAAC;CACb;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,SAAS,EAAE,SAAS,cAAc,EAAE,CAAC;CACtC;AAMD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAUnG;AAQD,wBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa,GAAG,YAAY,CAqG9D"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Where a workspace read starts, and which chant does it (#2536; #2524 D15,
3
+ * ws-016, ws-021).
4
+ *
5
+ * {@link locateWorkspace} finds the declaration from a directory, in the
6
+ * working tree or in one commit's git objects (`--at <rev>`), and gives the
7
+ * tree every read-contract command reads from.
8
+ *
9
+ * Which chant reads the declaration: the root's. The root names its chant by
10
+ * pinning `@intentius/chant` in the declaration's `pins`. When it pins one,
11
+ * that version reads the declaration, and any other chant refuses with
12
+ * `root-chant-required` (`parseDeclaration`'s `rootChant` option). The CLI
13
+ * first hands the whole command to the pinned chant when the root has it
14
+ * installed ({@link handToRootChant}), so a user sees the refusal only when
15
+ * it isn't. When the root pins no chant, the reader's own chant reads the
16
+ * declaration if it meets `minReader`, and refuses with `reader-too-old`
17
+ * otherwise. Members are always read by their own toolchains.
18
+ */
19
+ import { type WorkspaceTree } from "./tree.js";
20
+ export interface LocatedWorkspace {
21
+ /** The files, rooted at the workspace root. */
22
+ tree: WorkspaceTree;
23
+ /** The workspace root relative to the git root, `"."` for the git root itself. Absolute outside git. */
24
+ root: string;
25
+ /** The workspace root on disk, where pinned packages and toolchains are installed. Also for `--at`. */
26
+ rootOnDisk: string;
27
+ /** The full commit id for `--at`, or null for the working tree. */
28
+ at: string | null;
29
+ /** The top of the git repository, when there is one. */
30
+ top: string | undefined;
31
+ }
32
+ /**
33
+ * Find the workspace whose declaration is nearest above `cwd`, in the working
34
+ * tree or at revision `at`. Throws a {@link WorkspaceReadError}:
35
+ * `declaration-missing`, or for `--at`, `not-a-git-repository` and
36
+ * `revision-unknown`.
37
+ */
38
+ export declare function locateWorkspace(cwd: string, at?: string): LocatedWorkspace;
39
+ /**
40
+ * The `bin/chant` of `@intentius/chant` at `version`, installed where the
41
+ * workspace root resolves packages. Undefined when it isn't installed there,
42
+ * or is installed at another version.
43
+ */
44
+ export declare function rootChantBin(rootOnDisk: string, version: string): string | undefined;
45
+ /** Set in the environment of a command handed to the root's chant, so it is handed on once at most. */
46
+ export declare const HANDED_TO_ROOT_ENV = "CHANT_WORKSPACE_ROOT_CHANT";
47
+ /** The version the declaration at `located` pins for `@intentius/chant`, or undefined. Never throws. */
48
+ export declare function declaredChantPin(located: LocatedWorkspace): string | undefined;
49
+ /**
50
+ * Hand a read-contract command to the root's chant (ws-021). When the
51
+ * declaration above `cwd` (at `at`, for `--at`) pins a chant other than this
52
+ * one, and the root has that chant installed, run the same command line
53
+ * under it and return its exit code. Otherwise return undefined, and this
54
+ * chant runs the command: it reads the declaration itself, or refuses with
55
+ * `root-chant-required` when the pinned chant isn't installed.
56
+ */
57
+ export declare function handToRootChant(cwd: string, at: string | undefined, argv?: string[]): Promise<number | undefined>;
58
+ //# sourceMappingURL=which-chant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"which-chant.d.ts","sourceRoot":"","sources":["../../src/workspace/which-chant.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AASH,OAAO,EAA+C,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAC;AAEzF,MAAM,WAAW,gBAAgB;IAC/B,+CAA+C;IAC/C,IAAI,EAAE,aAAa,CAAC;IACpB,wGAAwG;IACxG,IAAI,EAAE,MAAM,CAAC;IACb,uGAAuG;IACvG,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,wDAAwD;IACxD,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;CACzB;AAID;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAgC1E;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAcpF;AAED,uGAAuG;AACvG,eAAO,MAAM,kBAAkB,+BAA+B,CAAC;AAE/D,wGAAwG;AACxG,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAY9E;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,GAAE,MAAM,EAA0B,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAoB9I"}
@@ -0,0 +1,133 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://intentius.io/chant/schemas/workspace/kinds/v1/workspace-kinds.schema.json",
4
+ "title": "workspace-kinds.json",
5
+ "description": "Member kinds a plugin supplies (#2524 D3, #2535, ws-031). A package publishes this file as data at its ./workspace-kinds subpath. chant reads it with the file system and never imports the package, so reading kinds never loads a lexicon.",
6
+ "type": "object",
7
+ "required": [
8
+ "schema",
9
+ "kinds"
10
+ ],
11
+ "properties": {
12
+ "$schema": {
13
+ "type": "string",
14
+ "description": "An editor hint. chant ignores it."
15
+ },
16
+ "$comment": {
17
+ "type": "string",
18
+ "description": "A note for people. chant ignores it."
19
+ },
20
+ "schema": {
21
+ "const": 1,
22
+ "description": "The kinds format version."
23
+ },
24
+ "kinds": {
25
+ "type": "array",
26
+ "minItems": 1,
27
+ "items": {
28
+ "$ref": "#/$defs/kind"
29
+ }
30
+ }
31
+ },
32
+ "patternProperties": {
33
+ "^x-": true
34
+ },
35
+ "additionalProperties": false,
36
+ "$defs": {
37
+ "kind": {
38
+ "type": "object",
39
+ "required": [
40
+ "name",
41
+ "description",
42
+ "precedence",
43
+ "probe"
44
+ ],
45
+ "properties": {
46
+ "name": {
47
+ "type": "string",
48
+ "pattern": "^[a-z][a-z0-9-]{0,39}$",
49
+ "description": "The kind's name, as a member's kind field names it. chant, workspace, other and examples are built in and can't be supplied."
50
+ },
51
+ "description": {
52
+ "type": "string",
53
+ "minLength": 1,
54
+ "description": "One line saying what the member's directory holds, for listings and error messages."
55
+ },
56
+ "precedence": {
57
+ "type": "integer",
58
+ "minimum": 1,
59
+ "maximum": 999,
60
+ "description": "When the probes of several kinds claim one directory, the highest precedence decides. Two claiming kinds with the same highest precedence are a tie, and a tie fails. The built-in chant kind has 500 and workspace has 1000."
61
+ },
62
+ "probe": {
63
+ "$ref": "#/$defs/probe"
64
+ },
65
+ "outputs": {
66
+ "type": "array",
67
+ "uniqueItems": true,
68
+ "description": "The outputs every member of this kind exposes as link targets (#2524 D6, #2539). A member entry of the kind may list more in its own outputs field. Without this field the kind exposes only what each entry lists.",
69
+ "items": {
70
+ "type": "string",
71
+ "minLength": 1,
72
+ "maxLength": 255,
73
+ "pattern": "^[^\\u0000-\\u001f]+$"
74
+ }
75
+ }
76
+ },
77
+ "patternProperties": {
78
+ "^x-": true
79
+ },
80
+ "additionalProperties": false
81
+ },
82
+ "probe": {
83
+ "type": "object",
84
+ "description": "What the probe checks in a member's directory. It runs no code. It passes when any of its clauses does, and it needs at least one.",
85
+ "minProperties": 1,
86
+ "properties": {
87
+ "anyFile": {
88
+ "type": "array",
89
+ "minItems": 1,
90
+ "uniqueItems": true,
91
+ "description": "The directory passes when a file matching one of these names sits directly in it. A * in a name matches any run of characters, so *.tf matches every Terraform file.",
92
+ "items": {
93
+ "$ref": "#/$defs/fileName"
94
+ }
95
+ },
96
+ "anyBlock": {
97
+ "type": "object",
98
+ "description": "The directory passes when a file directly in it, whose name matches one of in, has a line opening an unlabelled block named in blocks: leading space, the name, optional space, then {. No file is parsed.",
99
+ "required": [
100
+ "in",
101
+ "blocks"
102
+ ],
103
+ "properties": {
104
+ "in": {
105
+ "type": "array",
106
+ "minItems": 1,
107
+ "uniqueItems": true,
108
+ "items": {
109
+ "$ref": "#/$defs/fileName"
110
+ }
111
+ },
112
+ "blocks": {
113
+ "type": "array",
114
+ "minItems": 1,
115
+ "uniqueItems": true,
116
+ "items": {
117
+ "type": "string",
118
+ "pattern": "^[A-Za-z_][A-Za-z0-9_-]{0,63}$"
119
+ }
120
+ }
121
+ },
122
+ "additionalProperties": false
123
+ }
124
+ },
125
+ "additionalProperties": false
126
+ },
127
+ "fileName": {
128
+ "type": "string",
129
+ "description": "A file name, with no directory part. A * matches any run of characters.",
130
+ "pattern": "^(?!\\.\\.?$)[^/\\\\]+$"
131
+ }
132
+ }
133
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant",
3
- "version": "0.79.0",
3
+ "version": "0.81.0",
4
4
  "description": "Declarative infrastructure-as-code toolkit — TypeScript on Node.js",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -49,6 +49,7 @@
49
49
  "types": "./dist/cli/index.d.ts",
50
50
  "default": "./src/cli/index.ts"
51
51
  },
52
+ "./workspace/*.schema.json": "./src/workspace/*.schema.json",
52
53
  "./cli/*": {
53
54
  "development": "./src/cli/*.ts",
54
55
  "types": "./dist/cli/*.d.ts",
@@ -91,6 +92,7 @@
91
92
  },
92
93
  "dependencies": {
93
94
  "@dagrejs/dagre": "^3.0.0",
95
+ "ajv": "^8.20.0",
94
96
  "esbuild": "^0.28.1",
95
97
  "fflate": "^0.8.2",
96
98
  "js-yaml": "^4.3.1",
@@ -100,8 +102,7 @@
100
102
  "zod": "^4.3.6"
101
103
  },
102
104
  "devDependencies": {
103
- "@cdktn/hcl2json": "^0.24.0",
104
- "ajv": "^8.20.0"
105
+ "@cdktn/hcl2json": "^0.24.0"
105
106
  },
106
107
  "overrides": {
107
108
  "esbuild": "^0.28.1"
package/src/audit/core.ts CHANGED
@@ -27,6 +27,7 @@ import { applyInlineSuppressions, type SuppressionMetaFinding } from "../lint/su
27
27
  import type { SerializerResult } from "../serializer";
28
28
  import type { AuditEntitiesInput, LexiconPlugin } from "../lexicon";
29
29
  import type { Declarable } from "../declarable";
30
+ import { lexiconModulePath, lexiconPackagesToInstall, lexiconSourceLabel } from "../lexicon-module";
30
31
 
31
32
  /**
32
33
  * The lexicon whose post-synth checks run against an audited file. Any lexicon
@@ -155,9 +156,19 @@ async function load(names: string[]): Promise<LexiconPlugin[]> {
155
156
  const { loadPlugins } = await import("../cli/plugins");
156
157
  return await loadPlugins(names);
157
158
  } catch (err) {
158
- const pkgs = names.map((n) => `@intentius/chant-lexicon-${n}`).join(" ");
159
+ const detail = err instanceof Error ? err.message : String(err);
160
+ // chant#2578 — a lexicon declared by path in chant.config.ts has no
161
+ // package: name its module and print no install line for it.
162
+ const pkgs = lexiconPackagesToInstall(names);
163
+ const paths = names.filter((n) => lexiconModulePath(n) !== undefined).map((n) => lexiconSourceLabel(n));
164
+ if (pkgs.length === 0) {
165
+ throw new MissingLexiconError(
166
+ `Could not load the lexicon module ${paths.join(", ")} needed to audit ${names.join("/")} workflows.\n(${detail})`,
167
+ );
168
+ }
169
+ const fromPath = paths.length > 0 ? ` The lexicon module ${paths.join(", ")} is declared by path and has nothing to install.` : "";
159
170
  throw new MissingLexiconError(
160
- `Missing lexicon package needed to audit ${names.join("/")} workflows. Install it with: npm i ${pkgs}\n(${err instanceof Error ? err.message : String(err)})`,
171
+ `Missing lexicon package needed to audit ${names.join("/")} workflows. Install it with: npm i ${pkgs.join(" ")}${fromPath}\n(${detail})`,
161
172
  );
162
173
  }
163
174
  }
@@ -34,13 +34,16 @@
34
34
  * normalized to a JSON string here.
35
35
  */
36
36
 
37
- import { readdirSync, readFileSync, statSync } from "fs";
37
+ import { readFileSync, statSync } from "fs";
38
38
  import { basename, join, relative, resolve } from "path";
39
39
  import { parseYAML } from "../yaml";
40
40
  import type { LexiconPlugin } from "../lexicon";
41
41
  import type { AuditInput, AuditLexicon } from "./core";
42
42
  import { isNginxConfigPath } from "./nginx";
43
- import { gitignoreCoversTerraformState, isTerraformStatePath } from "./terraform-state";
43
+ import { walkDiscovery } from "../discovery/walk";
44
+ import { findProjectConfig } from "../project-root";
45
+ import type { DiscoveryGlobs } from "../config";
46
+ import { gitignoreCoversTerraformState, isTerraformStatePath, nestedGitignoreCovering } from "./terraform-state";
44
47
 
45
48
  /** Lexicons the auditor knows how to detect and run checks for. */
46
49
  export const AUDIT_LEXICONS = ["github", "gitlab", "forgejo", "k8s", "docker", "aws", "azure", "gcp", "helm", "fountain", "terraform"] as const;
@@ -69,40 +72,65 @@ export async function loadAuditPlugins(names: readonly string[] = AUDIT_LEXICONS
69
72
  return plugins;
70
73
  }
71
74
 
72
- const WALK_SKIP = new Set(["node_modules", ".git", "dist"]);
73
75
  /** Terraform's local working directory: recorded by the walk, never entered (TF023). */
74
76
  const TERRAFORM_WORK_DIR = ".terraform";
75
- /** Dot-directories the walk descends into anyway (CI lives here). */
76
- const WALK_DOT_DIRS = new Set([".github", ".forgejo"]);
77
- const MAX_WALK_FILES = 1000;
77
+ /**
78
+ * How many files the local walk takes before it stops. Every file it reaches
79
+ * counts, candidate or not, so a tree with a large `docs/` or `assets/` can hit
80
+ * it long before a thousand auditable files. `--max-files` raises it (#2528).
81
+ */
82
+ export const DEFAULT_MAX_WALK_FILES = 1000;
78
83
  /** Skip absurdly large files before parsing — mirrors the fetch layer's caps. */
79
84
  const MAX_FILE_BYTES = 2 * 1024 * 1024;
80
85
 
81
- /** Recursively collect file paths under a root, skipping noise dirs. */
82
- function walkFiles(dir: string, out: string[]): void {
83
- if (out.length >= MAX_WALK_FILES) return;
84
- let entries;
86
+ /**
87
+ * The project's `exclude`/`include` globs for the audit walk, read from a
88
+ * `chant.config.json` only. The audit reads repositories it has no reason to
89
+ * trust, so it never runs a `chant.config.ts` to find them.
90
+ */
91
+ function auditGlobs(root: string): DiscoveryGlobs | undefined {
92
+ const { dir, configPath } = findProjectConfig(root);
93
+ if (!configPath?.endsWith(".json")) return undefined;
94
+ let raw: unknown;
85
95
  try {
86
- entries = readdirSync(dir, { withFileTypes: true });
96
+ raw = JSON.parse(readFileSync(configPath, "utf-8"));
87
97
  } catch {
88
- return;
89
- }
90
- for (const e of entries.sort((a, b) => (a.name < b.name ? -1 : 1))) {
91
- if (out.length >= MAX_WALK_FILES) return;
92
- if (e.name.startsWith(".") && e.isDirectory() && !WALK_DOT_DIRS.has(e.name)) {
93
- // `.terraform/` is never descended into: it is machine-generated,
94
- // routinely hundreds of megabytes of provider binaries, and nothing in
95
- // it is worth reading. Its presence is itself the finding (TF023), so
96
- // the directory path alone is recorded and `collectCandidates` hands it
97
- // on with no content.
98
- if (e.name === TERRAFORM_WORK_DIR) out.push(join(dir, e.name));
99
- continue;
100
- }
101
- if (WALK_SKIP.has(e.name)) continue;
102
- const full = join(dir, e.name);
103
- if (e.isDirectory()) walkFiles(full, out);
104
- else out.push(full);
98
+ return undefined;
105
99
  }
100
+ const list = (v: unknown): string[] => (Array.isArray(v) ? v.filter((x): x is string => typeof x === "string" && x !== "") : []);
101
+ const exclude = list((raw as Record<string, unknown>)?.exclude);
102
+ const include = list((raw as Record<string, unknown>)?.include);
103
+ return exclude.length === 0 && include.length === 0 ? undefined : { root: dir, exclude, include };
104
+ }
105
+
106
+ /**
107
+ * Collect file paths under a root through the one discovery walk
108
+ * (`../discovery/walk.ts`, #2527), sorted per directory.
109
+ *
110
+ * The audit's own departures from the other walkers: `.github` and `.forgejo`
111
+ * are entered, since they hold the CI files it audits; `.terraform/` is
112
+ * recorded but never entered, since it is machine-generated, routinely
113
+ * hundreds of megabytes of provider binaries, and its presence is itself the
114
+ * finding (TF023); and Terraform state paths are left to TF023's own
115
+ * `.gitignore` reading rather than dropped for being git-ignored.
116
+ *
117
+ * Stops at `state.max` paths. Once full it keeps looking only until it meets
118
+ * one more path it would have taken, which is what sets `truncated`: a tree of
119
+ * exactly `max` files, or one whose remaining entries are all skipped
120
+ * directories, was not truncated.
121
+ */
122
+ function walkFiles(root: string, state: { max: number; truncated: boolean }, excludeDirs?: readonly string[]): string[] {
123
+ return walkDiscovery({
124
+ walker: "audit",
125
+ root,
126
+ excludeDirs,
127
+ globs: auditGlobs(root),
128
+ sorted: true,
129
+ limit: state,
130
+ takeSkippedDir: (name) => name === TERRAFORM_WORK_DIR,
131
+ keepIgnored: (full) => isTerraformStatePath(relative(root, full)),
132
+ accept: () => true,
133
+ });
106
134
  }
107
135
 
108
136
  function readSafe(full: string): string | undefined {
@@ -594,23 +622,61 @@ export function discoverByDetection(root: string, plugins: DetectPlugin[]): Audi
594
622
  return classifyFiles(collectCandidates(root), plugins, { baseDir: root });
595
623
  }
596
624
 
625
+ /** Options for the local walk. */
626
+ export interface CandidateWalkOptions {
627
+ /** Files the walk takes before it stops, counting every file. Defaults to {@link DEFAULT_MAX_WALK_FILES}. */
628
+ maxFiles?: number;
629
+ /**
630
+ * Directories the walk leaves out, absolute: the workspace members and
631
+ * example-group matches below the root, from `workspaceMemberDirs`. The
632
+ * caller resolves them, since that is async and this walk is not.
633
+ */
634
+ excludeDirs?: readonly string[];
635
+ }
636
+
637
+ /** What the local walk read, plus what it knows about its own limits. */
638
+ export interface CandidateWalk {
639
+ files: RepoFile[];
640
+ /** The limit the walk ran with. */
641
+ maxFiles: number;
642
+ /** True when the walk stopped at `maxFiles` with files left unread. */
643
+ truncated: boolean;
644
+ }
645
+
597
646
  /** The walk half of `discoverByDetection`: candidate files read into memory, paths relative to the root. */
598
647
  export function collectCandidates(root: string): RepoFile[] {
599
- const all: string[] = [];
600
- walkFiles(root, all);
648
+ return walkCandidates(root).files;
649
+ }
650
+
651
+ /**
652
+ * `collectCandidates` plus whether the walk stopped at its limit with files
653
+ * left unread, which the audit report states (#2528).
654
+ */
655
+ export function walkCandidates(root: string, opts: CandidateWalkOptions = {}): CandidateWalk {
656
+ const state = { max: opts.maxFiles ?? DEFAULT_MAX_WALK_FILES, truncated: false };
657
+ const all = walkFiles(root, state, opts.excludeDirs);
601
658
  // TF023 reads paths, not content: a `.tfstate` can be tens of megabytes of
602
659
  // machine-generated JSON, and the finding is that it is in the repository at
603
- // all. The root `.gitignore` is what separates "committed" from "merely on
604
- // disk" here — a Terraform working tree almost always has an ignored
660
+ // all. The `.gitignore` files are what separate "committed" from "merely on
661
+ // disk" here. A Terraform working tree almost always has an ignored
605
662
  // `.terraform/` in it, and reporting that would make TF023 fire on every
606
- // local audit. A fetched repository has no such ambiguity (its file list is
607
- // the tracked files) and is not filtered.
663
+ // local audit. Every `.gitignore` from the scan root down to the file's own
664
+ // directory is read (#2528), so auditing from the root and from a
665
+ // subdirectory agree. A fetched repository has no such ambiguity (its file
666
+ // list is the tracked files) and is not filtered.
608
667
  const gitignore = readSafe(join(root, ".gitignore")) ?? "";
668
+ const nestedBodies = new Map<string, string | undefined>();
669
+ const nestedGitignoreAt = (dir: string): string | undefined => {
670
+ if (!nestedBodies.has(dir)) nestedBodies.set(dir, readSafe(join(root, dir, ".gitignore")));
671
+ return nestedBodies.get(dir);
672
+ };
609
673
  const files: RepoFile[] = [];
610
674
  for (const full of all) {
611
675
  const path = relative(root, full);
612
676
  if (isTerraformStatePath(path)) {
613
- if (!gitignoreCoversTerraformState(gitignore, path)) files.push({ path, content: "" });
677
+ const ignored =
678
+ gitignoreCoversTerraformState(gitignore, path) || nestedGitignoreCovering(path, nestedGitignoreAt) !== undefined;
679
+ if (!ignored) files.push({ path, content: "" });
614
680
  continue;
615
681
  }
616
682
  // `.tf` is read locally so `classifyTerraform` can bundle real content.
@@ -624,5 +690,5 @@ export function collectCandidates(root: string): RepoFile[] {
624
690
  const content = readSafe(full);
625
691
  if (content !== undefined) files.push({ path, content });
626
692
  }
627
- return files;
693
+ return { files, maxFiles: state.max, truncated: state.truncated };
628
694
  }
@@ -31,6 +31,8 @@ export interface RenderHtmlOptions extends BuildModelOptions {
31
31
  /** Full template override; `{{title}} {{accent}} {{logo}} {{meta}} {{body}} {{footer}}` slots. */
32
32
  template?: string;
33
33
  notes?: string[];
34
+ /** A truncated local walk (#2528), carried into the embedded JSON report. */
35
+ truncated?: { limit: number; flag: string };
34
36
  }
35
37
 
36
38
  const DEFAULT_ACCENT = "#7c3aed";
@@ -194,7 +196,7 @@ export function renderHtml(findings: AuditFinding[], opts: RenderHtmlOptions = {
194
196
 
195
197
  // Machine-readable data embedded so the HTML report is also parseable.
196
198
  // `<` escaped so the JSON can't break out of the <script> element.
197
- const dataJson = JSON.stringify(buildReportJson(findings, { snapshot: opts.snapshot }), null, 0).replace(/</g, "\\u003c");
199
+ const dataJson = JSON.stringify(buildReportJson(findings, { snapshot: opts.snapshot, truncated: opts.truncated }), null, 0).replace(/</g, "\\u003c");
198
200
  const dataScript = `<script type="application/json" id="chant-audit-report">${dataJson}</script>`;
199
201
 
200
202
  let body: string;