@intentius/chant 0.13.0 → 0.15.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 (453) hide show
  1. package/dist/audit/catalog.d.ts +45 -5
  2. package/dist/audit/catalog.d.ts.map +1 -1
  3. package/dist/audit/fetch.d.ts.map +1 -1
  4. package/dist/audit/proof.d.ts +3 -0
  5. package/dist/audit/proof.d.ts.map +1 -1
  6. package/dist/audit/report-model.d.ts +4 -1
  7. package/dist/audit/report-model.d.ts.map +1 -1
  8. package/dist/audit/report.d.ts +3 -0
  9. package/dist/audit/report.d.ts.map +1 -1
  10. package/dist/audit/rules-doc.d.ts +8 -2
  11. package/dist/audit/rules-doc.d.ts.map +1 -1
  12. package/dist/cli/commands/audit.d.ts.map +1 -1
  13. package/dist/cli/commands/doctor.d.ts.map +1 -1
  14. package/dist/cli/commands/lexicon-rolling-upgrade.d.ts +37 -0
  15. package/dist/cli/commands/lexicon-rolling-upgrade.d.ts.map +1 -0
  16. package/dist/cli/commands/lexicon-surface-diff.d.ts +48 -0
  17. package/dist/cli/commands/lexicon-surface-diff.d.ts.map +1 -0
  18. package/dist/cli/commands/lint.d.ts.map +1 -1
  19. package/dist/cli/commands/pinned-upgrade.d.ts +41 -0
  20. package/dist/cli/commands/pinned-upgrade.d.ts.map +1 -0
  21. package/dist/cli/handlers/build.d.ts.map +1 -1
  22. package/dist/cli/handlers/components.d.ts +39 -0
  23. package/dist/cli/handlers/components.d.ts.map +1 -0
  24. package/dist/cli/handlers/dev.d.ts +3 -0
  25. package/dist/cli/handlers/dev.d.ts.map +1 -1
  26. package/dist/cli/handlers/graph.d.ts +4 -2
  27. package/dist/cli/handlers/graph.d.ts.map +1 -1
  28. package/dist/cli/handlers/misc.d.ts.map +1 -1
  29. package/dist/cli/handlers/run.d.ts +49 -0
  30. package/dist/cli/handlers/run.d.ts.map +1 -1
  31. package/dist/cli/main.d.ts.map +1 -1
  32. package/dist/cli/registry.d.ts +35 -0
  33. package/dist/cli/registry.d.ts.map +1 -1
  34. package/dist/codegen/fetch.d.ts +22 -0
  35. package/dist/codegen/fetch.d.ts.map +1 -1
  36. package/dist/codegen/lexicon-regen.d.ts +110 -0
  37. package/dist/codegen/lexicon-regen.d.ts.map +1 -0
  38. package/dist/codegen/pinned-upgrade.d.ts +153 -0
  39. package/dist/codegen/pinned-upgrade.d.ts.map +1 -0
  40. package/dist/codegen/rolling-upgrade.d.ts +138 -0
  41. package/dist/codegen/rolling-upgrade.d.ts.map +1 -0
  42. package/dist/codegen/surface-snapshot.d.ts +173 -0
  43. package/dist/codegen/surface-snapshot.d.ts.map +1 -0
  44. package/dist/codegen/version-bump.d.ts +21 -0
  45. package/dist/codegen/version-bump.d.ts.map +1 -0
  46. package/dist/components/auto-release.d.ts +93 -0
  47. package/dist/components/auto-release.d.ts.map +1 -0
  48. package/dist/components/builders.d.ts +37 -0
  49. package/dist/components/builders.d.ts.map +1 -0
  50. package/dist/components/capability-plugin-loader.d.ts +111 -0
  51. package/dist/components/capability-plugin-loader.d.ts.map +1 -0
  52. package/dist/components/capability-plugin-schema.d.ts +24 -0
  53. package/dist/components/capability-plugin-schema.d.ts.map +1 -0
  54. package/dist/components/capability-plugin.d.ts +85 -0
  55. package/dist/components/capability-plugin.d.ts.map +1 -0
  56. package/dist/components/capability.d.ts +89 -0
  57. package/dist/components/capability.d.ts.map +1 -0
  58. package/dist/components/cli-support.d.ts +204 -0
  59. package/dist/components/cli-support.d.ts.map +1 -0
  60. package/dist/components/component.d.ts +179 -0
  61. package/dist/components/component.d.ts.map +1 -0
  62. package/dist/components/config-defaults.d.ts +41 -0
  63. package/dist/components/config-defaults.d.ts.map +1 -0
  64. package/dist/components/discover.d.ts +60 -0
  65. package/dist/components/discover.d.ts.map +1 -0
  66. package/dist/components/driver-output.d.ts +20 -0
  67. package/dist/components/driver-output.d.ts.map +1 -0
  68. package/dist/components/driver.d.ts +209 -0
  69. package/dist/components/driver.d.ts.map +1 -0
  70. package/dist/components/index.d.ts +22 -0
  71. package/dist/components/index.d.ts.map +1 -0
  72. package/dist/components/manifest-persistence.d.ts +79 -0
  73. package/dist/components/manifest-persistence.d.ts.map +1 -0
  74. package/dist/components/pilots/alb-ecs.pilot.d.ts +27 -0
  75. package/dist/components/pilots/alb-ecs.pilot.d.ts.map +1 -0
  76. package/dist/components/pilots/dynamodb.pilot.d.ts +22 -0
  77. package/dist/components/pilots/dynamodb.pilot.d.ts.map +1 -0
  78. package/dist/components/pilots/jar-emr.pilot.d.ts +37 -0
  79. package/dist/components/pilots/jar-emr.pilot.d.ts.map +1 -0
  80. package/dist/components/pilots/lambda.pilot.d.ts +35 -0
  81. package/dist/components/pilots/lambda.pilot.d.ts.map +1 -0
  82. package/dist/components/pilots/neo4j-fanout.pilot.d.ts +17 -0
  83. package/dist/components/pilots/neo4j-fanout.pilot.d.ts.map +1 -0
  84. package/dist/components/pilots/project.d.ts +13 -0
  85. package/dist/components/pilots/project.d.ts.map +1 -0
  86. package/dist/components/presets/ecs-fargate.d.ts +59 -0
  87. package/dist/components/presets/ecs-fargate.d.ts.map +1 -0
  88. package/dist/components/presets/index.d.ts +29 -0
  89. package/dist/components/presets/index.d.ts.map +1 -0
  90. package/dist/components/presets/lambda.d.ts +35 -0
  91. package/dist/components/presets/lambda.d.ts.map +1 -0
  92. package/dist/components/presets/single-host-compose.d.ts +43 -0
  93. package/dist/components/presets/single-host-compose.d.ts.map +1 -0
  94. package/dist/components/registry.d.ts +29 -0
  95. package/dist/components/registry.d.ts.map +1 -0
  96. package/dist/components/starter-plugin.d.ts +51 -0
  97. package/dist/components/starter-plugin.d.ts.map +1 -0
  98. package/dist/components/temporal-codegen-loader.d.ts +27 -0
  99. package/dist/components/temporal-codegen-loader.d.ts.map +1 -0
  100. package/dist/components/verbs/__tests__/mock-cloud-executor.d.ts +35 -0
  101. package/dist/components/verbs/__tests__/mock-cloud-executor.d.ts.map +1 -0
  102. package/dist/components/verbs/__tests__/mock-process-runner.d.ts +45 -0
  103. package/dist/components/verbs/__tests__/mock-process-runner.d.ts.map +1 -0
  104. package/dist/components/verbs/__tests__/mock-sbom-generator.d.ts +33 -0
  105. package/dist/components/verbs/__tests__/mock-sbom-generator.d.ts.map +1 -0
  106. package/dist/components/verbs/bom-writer.d.ts +155 -0
  107. package/dist/components/verbs/bom-writer.d.ts.map +1 -0
  108. package/dist/components/verbs/build-archive.d.ts +205 -0
  109. package/dist/components/verbs/build-archive.d.ts.map +1 -0
  110. package/dist/components/verbs/build.d.ts +150 -0
  111. package/dist/components/verbs/build.d.ts.map +1 -0
  112. package/dist/components/verbs/cloud-executor.d.ts +88 -0
  113. package/dist/components/verbs/cloud-executor.d.ts.map +1 -0
  114. package/dist/components/verbs/component-bom.d.ts +103 -0
  115. package/dist/components/verbs/component-bom.d.ts.map +1 -0
  116. package/dist/components/verbs/index.d.ts +49 -0
  117. package/dist/components/verbs/index.d.ts.map +1 -0
  118. package/dist/components/verbs/license-policy.d.ts +56 -0
  119. package/dist/components/verbs/license-policy.d.ts.map +1 -0
  120. package/dist/components/verbs/lockfile-sbom-generator.d.ts +81 -0
  121. package/dist/components/verbs/lockfile-sbom-generator.d.ts.map +1 -0
  122. package/dist/components/verbs/process-runner.d.ts +64 -0
  123. package/dist/components/verbs/process-runner.d.ts.map +1 -0
  124. package/dist/components/verbs/reproducibility.d.ts +80 -0
  125. package/dist/components/verbs/reproducibility.d.ts.map +1 -0
  126. package/dist/components/verbs/sbom-generator.d.ts +157 -0
  127. package/dist/components/verbs/sbom-generator.d.ts.map +1 -0
  128. package/dist/components/verbs/sbom.d.ts +79 -0
  129. package/dist/components/verbs/sbom.d.ts.map +1 -0
  130. package/dist/components/verbs/shell.d.ts +35 -0
  131. package/dist/components/verbs/shell.d.ts.map +1 -0
  132. package/dist/components/verbs/sign.d.ts +222 -0
  133. package/dist/components/verbs/sign.d.ts.map +1 -0
  134. package/dist/components/verbs/stub.d.ts +18 -0
  135. package/dist/components/verbs/stub.d.ts.map +1 -0
  136. package/dist/components/verbs/tool-sbom-generator.d.ts +51 -0
  137. package/dist/components/verbs/tool-sbom-generator.d.ts.map +1 -0
  138. package/dist/components/verbs/verify.d.ts +106 -0
  139. package/dist/components/verbs/verify.d.ts.map +1 -0
  140. package/dist/components/verbs/vex.d.ts +65 -0
  141. package/dist/components/verbs/vex.d.ts.map +1 -0
  142. package/dist/components/verbs/vuln-gate.d.ts +91 -0
  143. package/dist/components/verbs/vuln-gate.d.ts.map +1 -0
  144. package/dist/components/verbs/vuln-scan.d.ts +116 -0
  145. package/dist/components/verbs/vuln-scan.d.ts.map +1 -0
  146. package/dist/components/verbs/wait-verify.d.ts +85 -0
  147. package/dist/components/verbs/wait-verify.d.ts.map +1 -0
  148. package/dist/config.d.ts +205 -0
  149. package/dist/config.d.ts.map +1 -1
  150. package/dist/index.d.ts +4 -0
  151. package/dist/index.d.ts.map +1 -1
  152. package/dist/lexicon.d.ts +78 -0
  153. package/dist/lexicon.d.ts.map +1 -1
  154. package/dist/lifecycle/build-ledger-store.d.ts +121 -0
  155. package/dist/lifecycle/build-ledger-store.d.ts.map +1 -0
  156. package/dist/lifecycle/build-ledger.d.ts +225 -0
  157. package/dist/lifecycle/build-ledger.d.ts.map +1 -0
  158. package/dist/lifecycle/git.d.ts +84 -0
  159. package/dist/lifecycle/git.d.ts.map +1 -1
  160. package/dist/lifecycle/index.d.ts +5 -0
  161. package/dist/lifecycle/index.d.ts.map +1 -1
  162. package/dist/lifecycle/oras-referrer-lookup.d.ts +41 -0
  163. package/dist/lifecycle/oras-referrer-lookup.d.ts.map +1 -0
  164. package/dist/lifecycle/release-ledger.d.ts +122 -0
  165. package/dist/lifecycle/release-ledger.d.ts.map +1 -0
  166. package/dist/lifecycle/status.d.ts +150 -0
  167. package/dist/lifecycle/status.d.ts.map +1 -0
  168. package/dist/lint/component-checks.d.ts +95 -0
  169. package/dist/lint/component-checks.d.ts.map +1 -0
  170. package/dist/lint/engine.d.ts +23 -0
  171. package/dist/lint/engine.d.ts.map +1 -1
  172. package/dist/lint/presets/strict.json +8 -1
  173. package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.d.ts +4 -0
  174. package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.d.ts.map +1 -0
  175. package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.d.ts +4 -0
  176. package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.d.ts.map +1 -0
  177. package/dist/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.d.ts +4 -0
  178. package/dist/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.d.ts.map +1 -0
  179. package/dist/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.d.ts +4 -0
  180. package/dist/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.d.ts.map +1 -0
  181. package/dist/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.d.ts +4 -0
  182. package/dist/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.d.ts.map +1 -0
  183. package/dist/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.d.ts +10 -0
  184. package/dist/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.d.ts.map +1 -0
  185. package/dist/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.d.ts +3 -0
  186. package/dist/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.d.ts.map +1 -0
  187. package/dist/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.d.ts +4 -0
  188. package/dist/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.d.ts.map +1 -0
  189. package/dist/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.d.ts +4 -0
  190. package/dist/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.d.ts.map +1 -0
  191. package/dist/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.d.ts +4 -0
  192. package/dist/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.d.ts.map +1 -0
  193. package/dist/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.d.ts +4 -0
  194. package/dist/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.d.ts.map +1 -0
  195. package/dist/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.d.ts +4 -0
  196. package/dist/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.d.ts.map +1 -0
  197. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts +4 -0
  198. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts.map +1 -0
  199. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts +4 -0
  200. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts.map +1 -0
  201. package/dist/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.d.ts +4 -0
  202. package/dist/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.d.ts.map +1 -0
  203. package/dist/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.d.ts +4 -0
  204. package/dist/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.d.ts.map +1 -0
  205. package/dist/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.d.ts +4 -0
  206. package/dist/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.d.ts.map +1 -0
  207. package/dist/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.d.ts +4 -0
  208. package/dist/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.d.ts.map +1 -0
  209. package/dist/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.d.ts +4 -0
  210. package/dist/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.d.ts.map +1 -0
  211. package/dist/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.d.ts +4 -0
  212. package/dist/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.d.ts.map +1 -0
  213. package/dist/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.d.ts +3 -0
  214. package/dist/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.d.ts.map +1 -0
  215. package/dist/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.d.ts +4 -0
  216. package/dist/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.d.ts.map +1 -0
  217. package/dist/lint/rules/comp/comp001-publishes-never-applies.d.ts +20 -0
  218. package/dist/lint/rules/comp/comp001-publishes-never-applies.d.ts.map +1 -0
  219. package/dist/lint/rules/comp/comp002-dangling-wiring-ref.d.ts +28 -0
  220. package/dist/lint/rules/comp/comp002-dangling-wiring-ref.d.ts.map +1 -0
  221. package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts +39 -0
  222. package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts.map +1 -0
  223. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts +37 -0
  224. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts.map +1 -0
  225. package/dist/lint/rules/comp/comp005-capability-kind-is-noun.d.ts +43 -0
  226. package/dist/lint/rules/comp/comp005-capability-kind-is-noun.d.ts.map +1 -0
  227. package/dist/lint/rules/comp/comp006-shell-needs-reason.d.ts +22 -0
  228. package/dist/lint/rules/comp/comp006-shell-needs-reason.d.ts.map +1 -0
  229. package/dist/lint/rules/comp/comp007-composition-sprawl.d.ts +33 -0
  230. package/dist/lint/rules/comp/comp007-composition-sprawl.d.ts.map +1 -0
  231. package/dist/lint/rules/comp/index.d.ts +18 -0
  232. package/dist/lint/rules/comp/index.d.ts.map +1 -0
  233. package/dist/lint/rules/comp/support.d.ts +71 -0
  234. package/dist/lint/rules/comp/support.d.ts.map +1 -0
  235. package/dist/lsp/lexicon-providers.d.ts.map +1 -1
  236. package/dist/op/activity-registry.d.ts +11 -6
  237. package/dist/op/activity-registry.d.ts.map +1 -1
  238. package/dist/op/activity-runtime.d.ts +24 -0
  239. package/dist/op/activity-runtime.d.ts.map +1 -0
  240. package/dist/op/builders.d.ts +56 -0
  241. package/dist/op/builders.d.ts.map +1 -1
  242. package/dist/op/index.d.ts +2 -1
  243. package/dist/op/index.d.ts.map +1 -1
  244. package/dist/ownership.d.ts +20 -14
  245. package/dist/ownership.d.ts.map +1 -1
  246. package/package.json +14 -1
  247. package/src/audit/catalog.test.ts +74 -22
  248. package/src/audit/catalog.ts +74 -471
  249. package/src/audit/fetch.test.ts +112 -8
  250. package/src/audit/fetch.ts +53 -8
  251. package/src/audit/proof.ts +4 -2
  252. package/src/audit/report-html.test.ts +14 -7
  253. package/src/audit/report-model.ts +20 -9
  254. package/src/audit/report.test.ts +36 -13
  255. package/src/audit/report.ts +2 -0
  256. package/src/audit/rules-doc.test.ts +8 -6
  257. package/src/audit/rules-doc.ts +13 -5
  258. package/src/cli/commands/audit.ts +23 -18
  259. package/src/cli/commands/component-lint.test.ts +158 -0
  260. package/src/cli/commands/doctor.test.ts +116 -2
  261. package/src/cli/commands/doctor.ts +46 -0
  262. package/src/cli/commands/lexicon-rolling-upgrade.ts +198 -0
  263. package/src/cli/commands/lexicon-surface-diff.test.ts +104 -0
  264. package/src/cli/commands/lexicon-surface-diff.ts +185 -0
  265. package/src/cli/commands/lint.ts +142 -1
  266. package/src/cli/commands/pinned-upgrade.ts +204 -0
  267. package/src/cli/handlers/build.ts +58 -1
  268. package/src/cli/handlers/components.test.ts +504 -0
  269. package/src/cli/handlers/components.ts +404 -0
  270. package/src/cli/handlers/dev.ts +60 -1
  271. package/src/cli/handlers/graph.ts +47 -2
  272. package/src/cli/handlers/misc.ts +45 -1
  273. package/src/cli/handlers/run.test.ts +967 -3
  274. package/src/cli/handlers/run.ts +618 -11
  275. package/src/cli/main.test.ts +66 -0
  276. package/src/cli/main.ts +105 -2
  277. package/src/cli/registry.ts +35 -0
  278. package/src/codegen/fetch.test.ts +84 -3
  279. package/src/codegen/fetch.ts +76 -2
  280. package/src/codegen/lexicon-regen.test.ts +298 -0
  281. package/src/codegen/lexicon-regen.ts +545 -0
  282. package/src/codegen/pinned-upgrade.test.ts +399 -0
  283. package/src/codegen/pinned-upgrade.ts +423 -0
  284. package/src/codegen/rolling-upgrade.test.ts +333 -0
  285. package/src/codegen/rolling-upgrade.ts +320 -0
  286. package/src/codegen/surface-snapshot.test.ts +461 -0
  287. package/src/codegen/surface-snapshot.ts +546 -0
  288. package/src/codegen/version-bump.test.ts +47 -0
  289. package/src/codegen/version-bump.ts +39 -0
  290. package/src/components/README.md +130 -0
  291. package/src/components/SPRAWL-VALIDATION.md +181 -0
  292. package/src/components/__fixtures__/alb-ecs-service.json +51 -0
  293. package/src/components/__fixtures__/dynamodb-infra.json +32 -0
  294. package/src/components/__fixtures__/emr-job-consumer.json +26 -0
  295. package/src/components/__fixtures__/jar-lib-producer.json +16 -0
  296. package/src/components/__fixtures__/lambda-image-processor.json +28 -0
  297. package/src/components/__fixtures__/neo4j-fanout.json +34 -0
  298. package/src/components/__fixtures__/single-host-compose.json +29 -0
  299. package/src/components/auto-release.test.ts +217 -0
  300. package/src/components/auto-release.ts +216 -0
  301. package/src/components/builders.test.ts +45 -0
  302. package/src/components/builders.ts +71 -0
  303. package/src/components/capability-plugin-loader.ts +218 -0
  304. package/src/components/capability-plugin-schema.ts +56 -0
  305. package/src/components/capability-plugin.test.ts +235 -0
  306. package/src/components/capability-plugin.ts +108 -0
  307. package/src/components/capability.ts +117 -0
  308. package/src/components/cli-support.test.ts +579 -0
  309. package/src/components/cli-support.ts +481 -0
  310. package/src/components/component-schema.test.ts +265 -0
  311. package/src/components/component.schema.json +235 -0
  312. package/src/components/component.test.ts +251 -0
  313. package/src/components/component.ts +255 -0
  314. package/src/components/config-defaults.test.ts +263 -0
  315. package/src/components/config-defaults.ts +145 -0
  316. package/src/components/discover.test.ts +352 -0
  317. package/src/components/discover.ts +167 -0
  318. package/src/components/driver-output.ts +71 -0
  319. package/src/components/driver.test.ts +638 -0
  320. package/src/components/driver.ts +690 -0
  321. package/src/components/index.ts +113 -0
  322. package/src/components/manifest-persistence.test.ts +152 -0
  323. package/src/components/manifest-persistence.ts +164 -0
  324. package/src/components/pilots/README.md +33 -0
  325. package/src/components/pilots/alb-ecs.pilot.ts +63 -0
  326. package/src/components/pilots/dynamodb.pilot.ts +59 -0
  327. package/src/components/pilots/jar-emr.pilot.ts +69 -0
  328. package/src/components/pilots/lambda.pilot.ts +55 -0
  329. package/src/components/pilots/neo4j-fanout.pilot.ts +92 -0
  330. package/src/components/pilots/pilots-lint-clean.test.ts +78 -0
  331. package/src/components/pilots/pilots.test.ts +126 -0
  332. package/src/components/pilots/project.ts +13 -0
  333. package/src/components/presets/comp007-reduction.test.ts +122 -0
  334. package/src/components/presets/ecs-fargate.test.ts +84 -0
  335. package/src/components/presets/ecs-fargate.ts +103 -0
  336. package/src/components/presets/index.ts +32 -0
  337. package/src/components/presets/lambda.test.ts +35 -0
  338. package/src/components/presets/lambda.ts +53 -0
  339. package/src/components/presets/single-host-compose.test.ts +65 -0
  340. package/src/components/presets/single-host-compose.ts +64 -0
  341. package/src/components/registry.test.ts +112 -0
  342. package/src/components/registry.ts +37 -0
  343. package/src/components/starter-plugin.ts +105 -0
  344. package/src/components/temporal-codegen-loader.ts +41 -0
  345. package/src/components/verbs/__fixtures__/package-lock.json +45 -0
  346. package/src/components/verbs/__fixtures__/pom.xml +37 -0
  347. package/src/components/verbs/__fixtures__/schemas/cyclonedx-1.5.schema.json +3800 -0
  348. package/src/components/verbs/__fixtures__/schemas/cyclonedx-spdx-license.schema.json +621 -0
  349. package/src/components/verbs/__fixtures__/schemas/jsf-0.82.schema.json +240 -0
  350. package/src/components/verbs/__fixtures__/schemas/spdx-2.3.schema.json +740 -0
  351. package/src/components/verbs/__tests__/mock-cloud-executor.ts +102 -0
  352. package/src/components/verbs/__tests__/mock-process-runner.ts +79 -0
  353. package/src/components/verbs/__tests__/mock-sbom-generator.ts +85 -0
  354. package/src/components/verbs/bom-writer.test.ts +331 -0
  355. package/src/components/verbs/bom-writer.ts +386 -0
  356. package/src/components/verbs/build-archive.test.ts +251 -0
  357. package/src/components/verbs/build-archive.ts +296 -0
  358. package/src/components/verbs/build.test.ts +222 -0
  359. package/src/components/verbs/build.ts +252 -0
  360. package/src/components/verbs/cloud-executor.ts +181 -0
  361. package/src/components/verbs/component-bom.test.ts +219 -0
  362. package/src/components/verbs/component-bom.ts +189 -0
  363. package/src/components/verbs/index.ts +49 -0
  364. package/src/components/verbs/license-policy.ts +115 -0
  365. package/src/components/verbs/lockfile-sbom-generator.test.ts +195 -0
  366. package/src/components/verbs/lockfile-sbom-generator.ts +236 -0
  367. package/src/components/verbs/process-runner.test.ts +79 -0
  368. package/src/components/verbs/process-runner.ts +113 -0
  369. package/src/components/verbs/reproducibility.test.ts +24 -0
  370. package/src/components/verbs/reproducibility.ts +100 -0
  371. package/src/components/verbs/sbom-generator.test.ts +96 -0
  372. package/src/components/verbs/sbom-generator.ts +209 -0
  373. package/src/components/verbs/sbom.test.ts +184 -0
  374. package/src/components/verbs/sbom.ts +152 -0
  375. package/src/components/verbs/shell.test.ts +36 -0
  376. package/src/components/verbs/shell.ts +51 -0
  377. package/src/components/verbs/sign.test.ts +210 -0
  378. package/src/components/verbs/sign.ts +350 -0
  379. package/src/components/verbs/stub.ts +34 -0
  380. package/src/components/verbs/tool-sbom-generator.test.ts +194 -0
  381. package/src/components/verbs/tool-sbom-generator.ts +192 -0
  382. package/src/components/verbs/verify.test.ts +159 -0
  383. package/src/components/verbs/verify.ts +179 -0
  384. package/src/components/verbs/vex.ts +182 -0
  385. package/src/components/verbs/vuln-gate.test.ts +201 -0
  386. package/src/components/verbs/vuln-gate.ts +170 -0
  387. package/src/components/verbs/vuln-scan.test.ts +145 -0
  388. package/src/components/verbs/vuln-scan.ts +266 -0
  389. package/src/components/verbs/wait-verify.test.ts +111 -0
  390. package/src/components/verbs/wait-verify.ts +171 -0
  391. package/src/config.test.ts +74 -1
  392. package/src/config.ts +219 -0
  393. package/src/index.ts +32 -0
  394. package/src/lexicon.ts +88 -0
  395. package/src/lifecycle/build-ledger-store.test.ts +217 -0
  396. package/src/lifecycle/build-ledger-store.ts +165 -0
  397. package/src/lifecycle/build-ledger.test.ts +349 -0
  398. package/src/lifecycle/build-ledger.ts +305 -0
  399. package/src/lifecycle/git.test.ts +118 -0
  400. package/src/lifecycle/git.ts +149 -14
  401. package/src/lifecycle/index.ts +5 -0
  402. package/src/lifecycle/oras-referrer-lookup.test.ts +116 -0
  403. package/src/lifecycle/oras-referrer-lookup.ts +110 -0
  404. package/src/lifecycle/release-ledger.test.ts +209 -0
  405. package/src/lifecycle/release-ledger.ts +185 -0
  406. package/src/lifecycle/status.test.ts +330 -0
  407. package/src/lifecycle/status.ts +278 -0
  408. package/src/lint/component-checks.ts +148 -0
  409. package/src/lint/config.test.ts +21 -0
  410. package/src/lint/engine.ts +11 -2
  411. package/src/lint/presets/relaxed.json +8 -1
  412. package/src/lint/presets/strict.json +8 -1
  413. package/src/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.ts +9 -0
  414. package/src/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.ts +9 -0
  415. package/src/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.ts +11 -0
  416. package/src/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.ts +13 -0
  417. package/src/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.ts +11 -0
  418. package/src/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.ts +27 -0
  419. package/src/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.ts +10 -0
  420. package/src/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.ts +23 -0
  421. package/src/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.ts +10 -0
  422. package/src/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.ts +19 -0
  423. package/src/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.ts +10 -0
  424. package/src/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.ts +20 -0
  425. package/src/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.ts +23 -0
  426. package/src/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.ts +10 -0
  427. package/src/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.ts +14 -0
  428. package/src/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.ts +18 -0
  429. package/src/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.ts +10 -0
  430. package/src/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.ts +14 -0
  431. package/src/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.ts +13 -0
  432. package/src/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.ts +13 -0
  433. package/src/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.ts +12 -0
  434. package/src/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.ts +18 -0
  435. package/src/lint/rules/comp/comp.test.ts +378 -0
  436. package/src/lint/rules/comp/comp001-publishes-never-applies.ts +78 -0
  437. package/src/lint/rules/comp/comp002-dangling-wiring-ref.ts +94 -0
  438. package/src/lint/rules/comp/comp003-mutating-no-rollback.ts +115 -0
  439. package/src/lint/rules/comp/comp004-gate-needs-temporal.ts +66 -0
  440. package/src/lint/rules/comp/comp005-capability-kind-is-noun.ts +113 -0
  441. package/src/lint/rules/comp/comp006-shell-needs-reason.ts +55 -0
  442. package/src/lint/rules/comp/comp007-composition-sprawl.ts +71 -0
  443. package/src/lint/rules/comp/index.ts +37 -0
  444. package/src/lint/rules/comp/support.ts +173 -0
  445. package/src/lsp/lexicon-providers.test.ts +55 -0
  446. package/src/lsp/lexicon-providers.ts +13 -3
  447. package/src/op/activity-registry.test.ts +38 -0
  448. package/src/op/activity-registry.ts +31 -12
  449. package/src/op/activity-runtime.ts +73 -0
  450. package/src/op/builders.ts +92 -0
  451. package/src/op/index.ts +3 -1
  452. package/src/ownership.test.ts +24 -26
  453. package/src/ownership.ts +26 -38
@@ -188,6 +188,51 @@ describe("parseArgs", () => {
188
188
  expect(result.extraPositional).toBe(".");
189
189
  expect(result.extraPositional2).toBe(undefined);
190
190
  });
191
+
192
+ // ── components release/status flags (#568) ──────────────────────────────
193
+
194
+ test("parses --component, --digest, --git-sha, --run-id, --actor for components release", () => {
195
+ const result = parseArgs([
196
+ "components", "release", "prod",
197
+ "--component", "search-service",
198
+ "--digest", "sha256:abc123",
199
+ "--git-sha", "deadbeef",
200
+ "--run-id", "run-1",
201
+ "--actor", "alice",
202
+ ]);
203
+ expect(result.command).toBe("components");
204
+ expect(result.path).toBe("release");
205
+ expect(result.extraPositional).toBe("prod");
206
+ expect(result.component).toBe("search-service");
207
+ expect(result.digest).toBe("sha256:abc123");
208
+ expect(result.gitSha).toBe("deadbeef");
209
+ expect(result.runId).toBe("run-1");
210
+ expect(result.actor).toBe("alice");
211
+ });
212
+
213
+ test("parses --compare-to and --live for components status", () => {
214
+ const result = parseArgs(["components", "status", "prod", "--compare-to", "staging", "--live", "--json"]);
215
+ expect(result.command).toBe("components");
216
+ expect(result.path).toBe("status");
217
+ expect(result.extraPositional).toBe("prod");
218
+ expect(result.compareTo).toBe("staging");
219
+ expect(result.live).toBe(true);
220
+ expect(result.json).toBe(true);
221
+ });
222
+
223
+ // ── --no-release-record (#597) ───────────────────────────────────────────
224
+
225
+ test("parses --no-release-record for run --components", () => {
226
+ const result = parseArgs(["run", "--components", "search-service", "--env", "staging", "--no-release-record"]);
227
+ expect(result.components).toBe(true);
228
+ expect(result.env).toBe("staging");
229
+ expect(result.noReleaseRecord).toBe(true);
230
+ });
231
+
232
+ test("--no-release-record is undefined (not false) when omitted", () => {
233
+ const result = parseArgs(["run", "--components", "search-service"]);
234
+ expect(result.noReleaseRecord).toBeUndefined();
235
+ });
191
236
  });
192
237
 
193
238
  // ── resolveCommand tests ──────────────────────────────────────────
@@ -205,6 +250,9 @@ describe("resolveCommand", () => {
205
250
  { name: "dev", handler: noop },
206
251
  { name: "lifecycle plan", handler: noop },
207
252
  { name: "lifecycle", handler: noop },
253
+ { name: "components status", handler: noop },
254
+ { name: "components release", handler: noop },
255
+ { name: "components", handler: noop },
208
256
  ];
209
257
 
210
258
  function makeArgs(overrides: Partial<ParsedArgs>): ParsedArgs {
@@ -286,6 +334,24 @@ describe("resolveCommand", () => {
286
334
  expect(result!.compound).toBe(false);
287
335
  });
288
336
 
337
+ test("resolves components status as compound command", () => {
338
+ const result = resolveCommand(makeArgs({ command: "components", path: "status" }), testRegistry);
339
+ expect(result!.def.name).toBe("components status");
340
+ expect(result!.compound).toBe(true);
341
+ });
342
+
343
+ test("resolves components release as compound command", () => {
344
+ const result = resolveCommand(makeArgs({ command: "components", path: "release" }), testRegistry);
345
+ expect(result!.def.name).toBe("components release");
346
+ expect(result!.compound).toBe(true);
347
+ });
348
+
349
+ test("falls back to unknown components subcommand handler", () => {
350
+ const result = resolveCommand(makeArgs({ command: "components", path: "bogus" }), testRegistry);
351
+ expect(result!.def.name).toBe("components");
352
+ expect(result!.compound).toBe(false);
353
+ });
354
+
289
355
  test("resolves run status as compound command", () => {
290
356
  const registry: CommandDef[] = [
291
357
  { name: "run list", handler: noop },
package/src/cli/main.ts CHANGED
@@ -9,13 +9,14 @@ import { ENV_VAR, unknownEnvError } from "../env";
9
9
  import { initRuntime } from "../runtime-adapter";
10
10
  import { runBuild } from "./handlers/build";
11
11
  import { runLint } from "./handlers/lint";
12
- import { runDevGenerate, runDevPublish, runDevOnboard, runDevCheckLexicon, runDevUnknown } from "./handlers/dev";
12
+ import { runDevGenerate, runDevPublish, runDevOnboard, runDevCheckLexicon, runDevSurfaceDiff, runDevPinnedUpgrade, runDevRollingUpgrade, runDevUnknown } from "./handlers/dev";
13
13
  import { runServeLsp, runServeMcp, runServeUnknown } from "./handlers/serve";
14
14
  import { runInit, runInitLexicon } from "./handlers/init";
15
15
  import { runList, runDescribe, runImport, runAudit, runUpdate, runDoctor } from "./handlers/misc";
16
16
  import { runVendor } from "./handlers/vendor";
17
17
  import { runMigrate } from "./handlers/migrate";
18
18
  import { runLifecycleSnapshot, runLifecycleShow, runLifecycleDiff, runLifecyclePlan, runLifecycleAffected, runLifecycleLog, runLifecycleUnknown } from "./handlers/lifecycle";
19
+ import { runComponentsStatus, runComponentsReleaseRecord, runComponentsUnknown } from "./handlers/components";
19
20
  import { runGraph } from "./handlers/graph";
20
21
  import { runOp, runOpList, runOpStatus, runOpSignal, runOpCancel, runOpLog } from "./handlers/run";
21
22
 
@@ -127,6 +128,14 @@ export function parseArgs(args: string[]): ParsedArgs {
127
128
  result.theme = args[++i];
128
129
  } else if (arg === "--stacks") {
129
130
  result.stacks = true;
131
+ } else if (arg === "--components") {
132
+ result.components = true;
133
+ } else if (arg === "--generate") {
134
+ result.generate = args[++i];
135
+ } else if (arg === "--dump-outputs") {
136
+ result.dumpOutputs = args[++i];
137
+ } else if (arg === "--seed-outputs") {
138
+ (result.seedOutputs ??= []).push(args[++i]);
130
139
  } else if (arg === "--detail") {
131
140
  result.detail = Number(args[++i]);
132
141
  } else if (arg === "--lens") {
@@ -151,6 +160,30 @@ export function parseArgs(args: string[]): ParsedArgs {
151
160
  result.temporal = true;
152
161
  } else if (arg === "--json") {
153
162
  result.json = true;
163
+ } else if (arg === "--update-snapshot") {
164
+ result.updateSnapshot = true;
165
+ } else if (arg === "--run-examples") {
166
+ result.runExamples = true;
167
+ } else if (arg === "--pinned-digest") {
168
+ result.pinnedDigest = args[++i];
169
+ } else if (arg === "--check") {
170
+ result.check = true;
171
+ } else if (arg === "--bump") {
172
+ result.bump = true;
173
+ } else if (arg === "--component") {
174
+ result.component = args[++i];
175
+ } else if (arg === "--digest") {
176
+ result.digest = args[++i];
177
+ } else if (arg === "--git-sha") {
178
+ result.gitSha = args[++i];
179
+ } else if (arg === "--run-id") {
180
+ result.runId = args[++i];
181
+ } else if (arg === "--actor") {
182
+ result.actor = args[++i];
183
+ } else if (arg === "--compare-to") {
184
+ result.compareTo = args[++i];
185
+ } else if (arg === "--no-release-record") {
186
+ result.noReleaseRecord = true;
154
187
  } else if (!arg.startsWith("-")) {
155
188
  if (!result.command) {
156
189
  result.command = arg;
@@ -183,6 +216,10 @@ Commands:
183
216
  init Initialize a new chant project
184
217
  init lexicon <name> Scaffold a new lexicon plugin project
185
218
  build Build infrastructure from specification files
219
+ (--components --generate gitlab: generate mode (#563) —
220
+ synthesize a thin .gitlab-ci.yml that triggers each
221
+ discovered component's own deploy in wave order,
222
+ instead of a normal lexicon build)
186
223
  lint Check specifications for issues
187
224
  list List discovered entities
188
225
  describe Show the effective config for one component
@@ -198,10 +235,21 @@ Commands:
198
235
  Ops:
199
236
  run <name> Start an Op workflow (spawns worker + submits to Temporal)
200
237
  run list List all Ops with current run status
238
+ --components: list discovered Components instead (--temporal
239
+ also annotates each with its latest run status; #599)
201
240
  run status <name> Show current workflow run state
241
+ --components: show a Component's durable run state instead (#599)
202
242
  run signal <name> <signal> Send a named signal to unblock a gate
243
+ --components: signal a Component's workflow instead (#589)
203
244
  run cancel <name> Cancel the active workflow run (requires --force)
245
+ --components: cancel a Component's workflow instead (#589)
204
246
  run log <name> Show run history for an Op
247
+ --components: show a Component's run history instead (#599)
248
+ run --components <name|all> Run discovered Component(s) through the interpret
249
+ driver on the local executor (--env <env>; #585).
250
+ On success, auto-emits a release-ledger record per
251
+ component that published a digest (default: on;
252
+ --no-release-record to opt out; #597)
205
253
 
206
254
  graph Show Op dependency graph (--stacks for cross-stack order,
207
255
  --format ir|mermaid|dot|layout for the lint-gated graph IR,
@@ -222,11 +270,33 @@ Lifecycle (alias: lc):
222
270
  --json: emit the ChangeSet as JSON
223
271
  lifecycle log [env] History of lifecycle snapshots
224
272
 
273
+ Component release ledger + status:
274
+ components status [env] What's built vs what's deployed where, joined by
275
+ digest (--live: reconcile against live+ownership;
276
+ --json: stable machine-readable contract;
277
+ --compare-to <env>: cross-check the same
278
+ component's recorded digest against another env)
279
+ components release <env> Append one immutable release record
280
+ (--component <name> --digest <sha256:...>
281
+ [--git-sha <sha>] [--run-id <id>] [--actor <name>])
282
+
225
283
  Lexicon development:
226
284
  dev generate Generate lexicon artifacts (+ validate + coverage)
227
285
  dev publish Package lexicon for distribution
228
286
  dev onboard <name> Patch CI, Dockerfiles, and workflows for a new lexicon
229
287
  dev check-lexicon <dir> Check lexicon completeness (tier 1/2/3)
288
+ dev surface-diff <dir> Regen lexicon, validate, diff API surface vs committed baseline
289
+ (--force: bypass spec cache; --update-snapshot: write new baseline;
290
+ --bump: with --update-snapshot, bump package version by drift severity;
291
+ --check: fail if the committed baseline drifted (never writes);
292
+ --run-examples: also run example build harness;
293
+ --pinned-digest <file>: verify spec digest before regen)
294
+ dev pinned-upgrade <dir> Report if a pinned lexicon (k8s|gcp|docker|gitlab) has a newer
295
+ upstream release; dry-run bump + regen + surface-diff, then revert
296
+ (reports only; --force bypasses the spec cache, -f json for JSON)
297
+ dev rolling-upgrade <dir> Report rolling-spec drift (aws, azure, github): regen from
298
+ latest, diff surface vs committed baseline, print delta + PR
299
+ label (dry run; --force bypasses the spec cache, --format json)
230
300
 
231
301
  Servers:
232
302
  serve lsp Start the LSP server (stdio)
@@ -268,12 +338,25 @@ Options:
268
338
  --strict Escalate needs-review/validation to errors (migrate)
269
339
  --validate Run external validator (glci/glab) after migrate
270
340
  --use-composites Rewrite to composite calls when patterns match (migrate)
341
+ --components Target discovered Component declarations instead of
342
+ lexicon resources (list, describe, graph, build, run)
343
+ --generate <lexicon> Generate mode (build --components only): synthesize CI
344
+ YAML for <lexicon> instead of running a normal build.
345
+ Only "gitlab" is implemented for v1.
346
+ --no-release-record Skip auto-emitting a release-ledger record after a
347
+ successful \`run --components\` deploy (default: on;
348
+ also settable via chant.config.ts's
349
+ release.autoRecord: false; #597)
271
350
 
272
351
  Examples:
273
352
  chant build ./infra/
274
353
  chant build ./infra/ --output stack.json
275
354
  chant build ./infra/ --format yaml
276
355
  chant build ./infra/ --watch
356
+ chant build ./infra/ --components --generate gitlab
357
+ chant build ./infra/ --components --generate gitlab --output .gitlab-ci.yml
358
+ chant run --components search-service --env staging
359
+ chant run --components all --env production
277
360
  chant import template.json --output ./infra/
278
361
  chant import --from prod --name my-bucket --output src/
279
362
  chant lint ./infra/
@@ -332,6 +415,9 @@ const registry: CommandDef[] = [
332
415
  { name: "dev publish", requiresPlugins: true, handler: runDevPublish },
333
416
  { name: "dev onboard", handler: runDevOnboard },
334
417
  { name: "dev check-lexicon", handler: runDevCheckLexicon },
418
+ { name: "dev surface-diff", handler: runDevSurfaceDiff },
419
+ { name: "dev pinned-upgrade", handler: runDevPinnedUpgrade },
420
+ { name: "dev rolling-upgrade", handler: runDevRollingUpgrade },
335
421
 
336
422
  // Op / run subcommands
337
423
  { name: "run list", handler: runOpList },
@@ -352,6 +438,10 @@ const registry: CommandDef[] = [
352
438
  { name: "lifecycle affected", requiresPlugins: true, handler: runLifecycleAffected },
353
439
  { name: "lifecycle log", handler: runLifecycleLog },
354
440
 
441
+ // Component release ledger + status surface (#568, epic #551)
442
+ { name: "components status", requiresPlugins: true, handler: runComponentsStatus },
443
+ { name: "components release", handler: runComponentsReleaseRecord },
444
+
355
445
  // Serve subcommands
356
446
  { name: "serve lsp", requiresPlugins: true, handler: runServeLsp },
357
447
  { name: "serve mcp", requiresPlugins: true, handler: runServeMcp },
@@ -360,6 +450,7 @@ const registry: CommandDef[] = [
360
450
  { name: "lifecycle", handler: runLifecycleUnknown },
361
451
  { name: "dev", handler: runDevUnknown },
362
452
  { name: "serve", handler: runServeUnknown },
453
+ { name: "components", handler: runComponentsUnknown },
363
454
  ];
364
455
 
365
456
  /**
@@ -414,8 +505,20 @@ async function main(): Promise<void> {
414
505
  // which then fell through to import-detection on an empty file set and failed
415
506
  // with "No lexicon detected" even though chant.config.ts lists the lexicons.
416
507
  const projectPath = match.compound ? "." : args.path;
508
+ // `chant build --components --generate <lexicon>` (#563, generate mode)
509
+ // discovers `Component` declarations, not lexicon resources — a project
510
+ // made entirely of components has no reason to declare a chant lexicon
511
+ // plugin at all. Load plugins best-effort here instead of exiting, so
512
+ // generate mode works whether or not `chant.config.ts` names a lexicon.
513
+ // `components status` (#568) is the same shape: a components-only project
514
+ // may have no lexicon plugin, and `--live` reconciliation is opt-in, so
515
+ // missing plugins there is "no live evidence" (a warning), not a hard exit.
516
+ const isGenerateComponents = match.def.name === "build" && args.components && !!args.generate;
517
+ const isComponentsStatus = match.def.name === "components status";
417
518
  const plugins = match.def.requiresPlugins
418
- ? await loadPluginsOrExit(projectPath)
519
+ ? isGenerateComponents || isComponentsStatus
520
+ ? await loadPlugins(await resolveProjectLexicons(resolve(projectPath)).catch(() => [])).catch(() => [])
521
+ : await loadPluginsOrExit(projectPath)
419
522
  : [];
420
523
  const serializers = plugins.map((p) => p.serializer);
421
524
  const ctx = { args, plugins, serializers };
@@ -58,6 +58,13 @@ export interface ParsedArgs {
58
58
  env?: string;
59
59
  /** `chant graph --stacks` — render the cross-stack apply-ordering graph */
60
60
  stacks?: boolean;
61
+ /** `chant list --components` / `chant graph --components` — surface discovered
62
+ * `Component` declarations (#560) instead of/alongside lexicon resources. */
63
+ components?: boolean;
64
+ /** `chant build --components --generate <lexicon>` — generate mode (#563):
65
+ * synthesize CI YAML from discovered components instead of running a normal
66
+ * lexicon build. Only "gitlab" is implemented for v1. */
67
+ generate?: string;
61
68
  /** `chant graph --format ir --detail <0..3>` — graph IR detail tier */
62
69
  detail?: number;
63
70
  /** `chant graph --lens <kind>:<target>` — focus the graph IR on a slice */
@@ -83,6 +90,34 @@ export interface ParsedArgs {
83
90
  failOn?: string;
84
91
  /** `chant audit --theme <file>` — JSON theme knobs for the HTML report */
85
92
  theme?: string;
93
+ /** `chant dev surface-diff --update-snapshot` — write the fresh snapshot as the new baseline */
94
+ updateSnapshot?: boolean;
95
+ /** `chant dev surface-diff --run-examples` — also run the example build harness */
96
+ runExamples?: boolean;
97
+ /** `chant dev surface-diff --pinned-digest <file>` — path to SHA-256 digest file for supply-chain verification */
98
+ pinnedDigest?: string;
99
+ /** `chant dev surface-diff --check` — verify the committed baseline matches a fresh regen; exit non-zero if it drifted. Never writes the snapshot. */
100
+ check?: boolean;
101
+ /** `chant dev surface-diff --update-snapshot --bump` — bump the lexicon's package.json version by the drift severity so the accepted surface is publishable (#616). */
102
+ bump?: boolean;
103
+ /** `chant components release record --component <name>` (#568) — component name for the release record being appended. */
104
+ component?: string;
105
+ /** `chant components release record --digest <sha256:...>` (#568) — artifact digest to record, joining this release to the build archive/ledger. */
106
+ digest?: string;
107
+ /** `chant components release record --git-sha <sha>` (#568) — git commit the deploy was built from. */
108
+ gitSha?: string;
109
+ /** `chant components release record --run-id <id>` (#568) — orchestrator/CI run identifier. */
110
+ runId?: string;
111
+ /** `chant components release record --actor <name>` (#568) — who/what triggered the deploy. */
112
+ actor?: string;
113
+ /** `chant components status <env> --compare-to <env>` (#568) — a second environment to cross-check the same component's recorded digest against. */
114
+ compareTo?: string;
115
+ /** `chant run --components <name> --env <env> --no-release-record` (#597) — opt out of auto-emitting a release-ledger record after a successful component deploy. Default (flag omitted): recording is ON. Also settable project-wide via `chant.config.ts`'s `release.autoRecord: false`. */
116
+ noReleaseRecord?: boolean;
117
+ /** `chant run --components <name> --dump-outputs <file>` — after the run, write the accumulated cross-component/cross-stack outputs (JSON, keyed by component name) to `<file>`, for a downstream job to `--seed-outputs`. */
118
+ dumpOutputs?: string;
119
+ /** `chant run --components <name> --seed-outputs <file>` (repeatable) — before the run, load each JSON outputs file (as written by `--dump-outputs`) and seed cross-component/cross-stack resolution with it, so a `stackOutput()`/`@<dep>.publish.*` reference to a component that ran in an earlier job resolves. */
120
+ seedOutputs?: string[];
86
121
  }
87
122
 
88
123
  /**
@@ -1,5 +1,5 @@
1
1
  import { describe, test, expect, vi, afterEach } from "vitest";
2
- import { extractFromTar, fetchWithRetry } from "./fetch";
2
+ import { extractFromTar, fetchWithRetry, TransientFetchError } from "./fetch";
3
3
 
4
4
  /**
5
5
  * Build a minimal valid tar buffer with a single file entry.
@@ -168,11 +168,16 @@ describe("fetchWithRetry", () => {
168
168
  expect(fetchMock).toHaveBeenCalledTimes(1);
169
169
  });
170
170
 
171
- test("throws after exhausting retries on a transient status", async () => {
171
+ test("throws TransientFetchError after exhausting retries on a transient status", async () => {
172
172
  const fetchMock = vi.fn().mockResolvedValue(status(504));
173
173
  vi.stubGlobal("fetch", fetchMock);
174
174
 
175
- await expect(fetchWithRetry("https://example.test/x", 2, 1)).rejects.toThrow("returned 504");
175
+ const err = await fetchWithRetry("https://example.test/x", 2, 1).catch((e: unknown) => e);
176
+ expect(err).toBeInstanceOf(TransientFetchError);
177
+ expect((err as TransientFetchError).url).toBe("https://example.test/x");
178
+ expect((err as TransientFetchError).lastStatus).toBe(504);
179
+ expect((err as TransientFetchError).message).toContain("Transient fetch failure");
180
+ expect((err as TransientFetchError).message).toContain("HTTP 504");
176
181
  // initial attempt + 2 retries
177
182
  expect(fetchMock).toHaveBeenCalledTimes(3);
178
183
  });
@@ -217,4 +222,80 @@ describe("fetchWithRetry", () => {
217
222
  await expect(fetchWithRetry("https://example.test/x", 4, 1, init)).rejects.toThrow("returned 403");
218
223
  expect(fetchMock).toHaveBeenCalledTimes(1);
219
224
  });
225
+
226
+ test("429 then 200 succeeds after retry", async () => {
227
+ const fetchMock = vi
228
+ .fn()
229
+ .mockResolvedValueOnce(status(429))
230
+ .mockResolvedValueOnce(ok());
231
+ vi.stubGlobal("fetch", fetchMock);
232
+
233
+ const resp = await fetchWithRetry("https://example.test/x", 4, 1);
234
+ expect(resp.ok).toBe(true);
235
+ expect(fetchMock).toHaveBeenCalledTimes(2);
236
+ });
237
+
238
+ test("honors Retry-After header (seconds) on 429", async () => {
239
+ const sleepCalls: number[] = [];
240
+ // Intercept sleep without really waiting
241
+ vi.useFakeTimers();
242
+
243
+ const r429 = new Response("", {
244
+ status: 429,
245
+ headers: { "Retry-After": "2" },
246
+ });
247
+ const fetchMock = vi
248
+ .fn()
249
+ .mockResolvedValueOnce(r429)
250
+ .mockResolvedValueOnce(ok());
251
+ vi.stubGlobal("fetch", fetchMock);
252
+
253
+ // Run with fake timers — advance all pending timers automatically
254
+ const resultPromise = fetchWithRetry("https://example.test/x", 4, 1);
255
+ await vi.runAllTimersAsync();
256
+ const resp = await resultPromise;
257
+
258
+ expect(resp.ok).toBe(true);
259
+ expect(fetchMock).toHaveBeenCalledTimes(2);
260
+
261
+ vi.useRealTimers();
262
+ void sleepCalls;
263
+ });
264
+
265
+ test("throws TransientFetchError with clear message on persistent 429", async () => {
266
+ const r429 = new Response("", { status: 429 });
267
+ const fetchMock = vi.fn().mockResolvedValue(r429);
268
+ vi.stubGlobal("fetch", fetchMock);
269
+
270
+ const err = await fetchWithRetry("https://example.test/rate-limited", 2, 1).catch((e: unknown) => e);
271
+ expect(err).toBeInstanceOf(TransientFetchError);
272
+ const tfe = err as TransientFetchError;
273
+ expect(tfe.url).toBe("https://example.test/rate-limited");
274
+ expect(tfe.lastStatus).toBe(429);
275
+ expect(tfe.message).toContain("Transient fetch failure");
276
+ expect(tfe.message).toContain("HTTP 429");
277
+ expect(tfe.message).toContain("rate-limit or upstream outage");
278
+ expect(tfe.name).toBe("TransientFetchError");
279
+ });
280
+
281
+ test("throws TransientFetchError with clear message on persistent 5xx", async () => {
282
+ const fetchMock = vi.fn().mockResolvedValue(status(503));
283
+ vi.stubGlobal("fetch", fetchMock);
284
+
285
+ const err = await fetchWithRetry("https://example.test/unavailable", 1, 1).catch((e: unknown) => e);
286
+ expect(err).toBeInstanceOf(TransientFetchError);
287
+ const tfe = err as TransientFetchError;
288
+ expect(tfe.lastStatus).toBe(503);
289
+ expect(tfe.message).toContain("HTTP 503");
290
+ });
291
+
292
+ test("TransientFetchError message includes attempt count", async () => {
293
+ const fetchMock = vi.fn().mockResolvedValue(status(500));
294
+ vi.stubGlobal("fetch", fetchMock);
295
+
296
+ // 0 retries → exactly 1 attempt
297
+ const err = await fetchWithRetry("https://example.test/x", 0, 1).catch((e: unknown) => e);
298
+ expect(err).toBeInstanceOf(TransientFetchError);
299
+ expect((err as TransientFetchError).message).toContain("1 attempt");
300
+ });
220
301
  });
@@ -30,11 +30,38 @@ const RETRYABLE_STATUSES = new Set([408, 425, 429, 500, 502, 503, 504]);
30
30
 
31
31
  const DEFAULT_RETRIES = 4;
32
32
  const DEFAULT_BACKOFF_MS = 1000;
33
+ /** Hard cap on Retry-After delays so a rogue header cannot stall CI indefinitely. */
34
+ const MAX_RETRY_AFTER_MS = 60_000;
33
35
 
34
36
  function sleep(ms: number): Promise<void> {
35
37
  return new Promise((resolve) => setTimeout(resolve, ms));
36
38
  }
37
39
 
40
+ /**
41
+ * Parse the `Retry-After` response header and return a delay in milliseconds.
42
+ *
43
+ * The header value may be an integer number of seconds or an HTTP-date.
44
+ * Returns `null` when the header is absent or unparseable.
45
+ * The returned value is capped at {@link MAX_RETRY_AFTER_MS}.
46
+ */
47
+ function parseRetryAfter(response: Response): number | null {
48
+ const raw = response.headers.get("retry-after");
49
+ if (!raw) return null;
50
+
51
+ const seconds = Number(raw.trim());
52
+ if (!Number.isNaN(seconds) && seconds >= 0) {
53
+ return Math.min(Math.ceil(seconds * 1000), MAX_RETRY_AFTER_MS);
54
+ }
55
+
56
+ // HTTP-date format: "Wed, 01 Jul 2026 12:00:00 GMT"
57
+ const date = new Date(raw);
58
+ if (!Number.isNaN(date.getTime())) {
59
+ return Math.min(Math.max(0, date.getTime() - Date.now()), MAX_RETRY_AFTER_MS);
60
+ }
61
+
62
+ return null;
63
+ }
64
+
38
65
  /**
39
66
  * Fetch a URL, retrying on transient failures (network errors and
40
67
  * retryable HTTP statuses) with exponential backoff.
@@ -42,9 +69,16 @@ function sleep(ms: number): Promise<void> {
42
69
  * Permanent failures (e.g. 404, 403) are not retried — they throw on the
43
70
  * first response. The returned response is guaranteed `ok`.
44
71
  *
72
+ * When a `Retry-After` header is present on a 429 or 503 response the delay
73
+ * from that header is used instead of the exponential back-off for that
74
+ * specific retry, capped at {@link MAX_RETRY_AFTER_MS}.
75
+ *
45
76
  * `init` is passed through to `fetch` on every attempt, so callers that
46
77
  * need request headers (e.g. the GitHub API `Accept` header) or an abort
47
78
  * signal get retries without duplicating the loop.
79
+ *
80
+ * On exhaustion a descriptive {@link TransientFetchError} is thrown rather
81
+ * than the raw last error so the caller can surface a human-readable message.
48
82
  */
49
83
  export async function fetchWithRetry(
50
84
  url: string,
@@ -52,12 +86,17 @@ export async function fetchWithRetry(
52
86
  backoffMs = DEFAULT_BACKOFF_MS,
53
87
  init?: RequestInit,
54
88
  ): Promise<Response> {
89
+ let lastStatus: number | undefined;
55
90
  let lastError: Error | undefined;
91
+ // When a response carries a Retry-After header, the indicated delay replaces
92
+ // the exponential back-off for that specific next retry.
93
+ let retryAfterOverride: number | null = null;
56
94
 
57
95
  for (let attempt = 0; attempt <= retries; attempt++) {
58
96
  if (attempt > 0) {
59
- const delay = backoffMs * 2 ** (attempt - 1);
97
+ const delay = retryAfterOverride ?? backoffMs * 2 ** (attempt - 1);
60
98
  debug(`retrying ${url} (attempt ${attempt}/${retries}) after ${delay}ms: ${lastError?.message}`);
99
+ retryAfterOverride = null;
61
100
  await sleep(delay);
62
101
  }
63
102
 
@@ -67,6 +106,7 @@ export async function fetchWithRetry(
67
106
  } catch (e) {
68
107
  // Network-level failure (DNS, connection reset, timeout). Transient.
69
108
  lastError = e instanceof Error ? e : new Error(String(e));
109
+ lastStatus = undefined;
70
110
  continue;
71
111
  }
72
112
 
@@ -74,10 +114,44 @@ export async function fetchWithRetry(
74
114
 
75
115
  const err = new Error(`Download from ${url} returned ${response.status}`);
76
116
  if (!RETRYABLE_STATUSES.has(response.status)) throw err;
117
+
77
118
  lastError = err;
119
+ lastStatus = response.status;
120
+
121
+ // Capture Retry-After so the next loop iteration uses it instead of backoff
122
+ const retryAfterMs = parseRetryAfter(response);
123
+ if (retryAfterMs !== null) {
124
+ debug(`Retry-After for ${url}: ${retryAfterMs}ms`);
125
+ retryAfterOverride = retryAfterMs;
126
+ }
78
127
  }
79
128
 
80
- throw lastError ?? new Error(`Download from ${url} failed after ${retries} retries`);
129
+ throw new TransientFetchError(url, retries, lastStatus, lastError);
130
+ }
131
+
132
+ /**
133
+ * Thrown when {@link fetchWithRetry} exhausts all retry attempts on a
134
+ * transient failure (429 / 5xx / network error).
135
+ *
136
+ * The message is human-readable and actionable: it names the URL, the number
137
+ * of attempts made, and the last HTTP status (if any) so that an upstream Op
138
+ * can route it to a report/issue without exposing a raw stack trace.
139
+ */
140
+ export class TransientFetchError extends Error {
141
+ constructor(
142
+ public readonly url: string,
143
+ public readonly retries: number,
144
+ public readonly lastStatus: number | undefined,
145
+ public readonly cause?: Error,
146
+ ) {
147
+ const attempts = retries + 1;
148
+ const statusPart = lastStatus !== undefined ? ` (HTTP ${lastStatus})` : "";
149
+ super(
150
+ `Transient fetch failure for ${url}${statusPart} — failed after ${attempts} attempt${attempts === 1 ? "" : "s"}. ` +
151
+ `This is typically a rate-limit or upstream outage. Retry later or check upstream status.`,
152
+ );
153
+ this.name = "TransientFetchError";
154
+ }
81
155
  }
82
156
 
83
157
  // ── Fetch with cache ───────────────────────────────────────────────