@intentius/chant 0.13.1 → 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 (450) 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/proof.d.ts +3 -0
  4. package/dist/audit/proof.d.ts.map +1 -1
  5. package/dist/audit/report-model.d.ts +4 -1
  6. package/dist/audit/report-model.d.ts.map +1 -1
  7. package/dist/audit/report.d.ts +3 -0
  8. package/dist/audit/report.d.ts.map +1 -1
  9. package/dist/audit/rules-doc.d.ts +8 -2
  10. package/dist/audit/rules-doc.d.ts.map +1 -1
  11. package/dist/cli/commands/audit.d.ts.map +1 -1
  12. package/dist/cli/commands/doctor.d.ts.map +1 -1
  13. package/dist/cli/commands/lexicon-rolling-upgrade.d.ts +37 -0
  14. package/dist/cli/commands/lexicon-rolling-upgrade.d.ts.map +1 -0
  15. package/dist/cli/commands/lexicon-surface-diff.d.ts +48 -0
  16. package/dist/cli/commands/lexicon-surface-diff.d.ts.map +1 -0
  17. package/dist/cli/commands/lint.d.ts.map +1 -1
  18. package/dist/cli/commands/pinned-upgrade.d.ts +41 -0
  19. package/dist/cli/commands/pinned-upgrade.d.ts.map +1 -0
  20. package/dist/cli/handlers/build.d.ts.map +1 -1
  21. package/dist/cli/handlers/components.d.ts +39 -0
  22. package/dist/cli/handlers/components.d.ts.map +1 -0
  23. package/dist/cli/handlers/dev.d.ts +3 -0
  24. package/dist/cli/handlers/dev.d.ts.map +1 -1
  25. package/dist/cli/handlers/graph.d.ts +4 -2
  26. package/dist/cli/handlers/graph.d.ts.map +1 -1
  27. package/dist/cli/handlers/misc.d.ts.map +1 -1
  28. package/dist/cli/handlers/run.d.ts +49 -0
  29. package/dist/cli/handlers/run.d.ts.map +1 -1
  30. package/dist/cli/main.d.ts.map +1 -1
  31. package/dist/cli/registry.d.ts +35 -0
  32. package/dist/cli/registry.d.ts.map +1 -1
  33. package/dist/codegen/fetch.d.ts +22 -0
  34. package/dist/codegen/fetch.d.ts.map +1 -1
  35. package/dist/codegen/lexicon-regen.d.ts +110 -0
  36. package/dist/codegen/lexicon-regen.d.ts.map +1 -0
  37. package/dist/codegen/pinned-upgrade.d.ts +153 -0
  38. package/dist/codegen/pinned-upgrade.d.ts.map +1 -0
  39. package/dist/codegen/rolling-upgrade.d.ts +138 -0
  40. package/dist/codegen/rolling-upgrade.d.ts.map +1 -0
  41. package/dist/codegen/surface-snapshot.d.ts +173 -0
  42. package/dist/codegen/surface-snapshot.d.ts.map +1 -0
  43. package/dist/codegen/version-bump.d.ts +21 -0
  44. package/dist/codegen/version-bump.d.ts.map +1 -0
  45. package/dist/components/auto-release.d.ts +93 -0
  46. package/dist/components/auto-release.d.ts.map +1 -0
  47. package/dist/components/builders.d.ts +37 -0
  48. package/dist/components/builders.d.ts.map +1 -0
  49. package/dist/components/capability-plugin-loader.d.ts +111 -0
  50. package/dist/components/capability-plugin-loader.d.ts.map +1 -0
  51. package/dist/components/capability-plugin-schema.d.ts +24 -0
  52. package/dist/components/capability-plugin-schema.d.ts.map +1 -0
  53. package/dist/components/capability-plugin.d.ts +85 -0
  54. package/dist/components/capability-plugin.d.ts.map +1 -0
  55. package/dist/components/capability.d.ts +89 -0
  56. package/dist/components/capability.d.ts.map +1 -0
  57. package/dist/components/cli-support.d.ts +204 -0
  58. package/dist/components/cli-support.d.ts.map +1 -0
  59. package/dist/components/component.d.ts +179 -0
  60. package/dist/components/component.d.ts.map +1 -0
  61. package/dist/components/config-defaults.d.ts +41 -0
  62. package/dist/components/config-defaults.d.ts.map +1 -0
  63. package/dist/components/discover.d.ts +60 -0
  64. package/dist/components/discover.d.ts.map +1 -0
  65. package/dist/components/driver-output.d.ts +20 -0
  66. package/dist/components/driver-output.d.ts.map +1 -0
  67. package/dist/components/driver.d.ts +209 -0
  68. package/dist/components/driver.d.ts.map +1 -0
  69. package/dist/components/index.d.ts +22 -0
  70. package/dist/components/index.d.ts.map +1 -0
  71. package/dist/components/manifest-persistence.d.ts +79 -0
  72. package/dist/components/manifest-persistence.d.ts.map +1 -0
  73. package/dist/components/pilots/alb-ecs.pilot.d.ts +27 -0
  74. package/dist/components/pilots/alb-ecs.pilot.d.ts.map +1 -0
  75. package/dist/components/pilots/dynamodb.pilot.d.ts +22 -0
  76. package/dist/components/pilots/dynamodb.pilot.d.ts.map +1 -0
  77. package/dist/components/pilots/jar-emr.pilot.d.ts +37 -0
  78. package/dist/components/pilots/jar-emr.pilot.d.ts.map +1 -0
  79. package/dist/components/pilots/lambda.pilot.d.ts +35 -0
  80. package/dist/components/pilots/lambda.pilot.d.ts.map +1 -0
  81. package/dist/components/pilots/neo4j-fanout.pilot.d.ts +17 -0
  82. package/dist/components/pilots/neo4j-fanout.pilot.d.ts.map +1 -0
  83. package/dist/components/pilots/project.d.ts +13 -0
  84. package/dist/components/pilots/project.d.ts.map +1 -0
  85. package/dist/components/presets/ecs-fargate.d.ts +59 -0
  86. package/dist/components/presets/ecs-fargate.d.ts.map +1 -0
  87. package/dist/components/presets/index.d.ts +29 -0
  88. package/dist/components/presets/index.d.ts.map +1 -0
  89. package/dist/components/presets/lambda.d.ts +35 -0
  90. package/dist/components/presets/lambda.d.ts.map +1 -0
  91. package/dist/components/presets/single-host-compose.d.ts +43 -0
  92. package/dist/components/presets/single-host-compose.d.ts.map +1 -0
  93. package/dist/components/registry.d.ts +29 -0
  94. package/dist/components/registry.d.ts.map +1 -0
  95. package/dist/components/starter-plugin.d.ts +51 -0
  96. package/dist/components/starter-plugin.d.ts.map +1 -0
  97. package/dist/components/temporal-codegen-loader.d.ts +27 -0
  98. package/dist/components/temporal-codegen-loader.d.ts.map +1 -0
  99. package/dist/components/verbs/__tests__/mock-cloud-executor.d.ts +35 -0
  100. package/dist/components/verbs/__tests__/mock-cloud-executor.d.ts.map +1 -0
  101. package/dist/components/verbs/__tests__/mock-process-runner.d.ts +45 -0
  102. package/dist/components/verbs/__tests__/mock-process-runner.d.ts.map +1 -0
  103. package/dist/components/verbs/__tests__/mock-sbom-generator.d.ts +33 -0
  104. package/dist/components/verbs/__tests__/mock-sbom-generator.d.ts.map +1 -0
  105. package/dist/components/verbs/bom-writer.d.ts +155 -0
  106. package/dist/components/verbs/bom-writer.d.ts.map +1 -0
  107. package/dist/components/verbs/build-archive.d.ts +205 -0
  108. package/dist/components/verbs/build-archive.d.ts.map +1 -0
  109. package/dist/components/verbs/build.d.ts +150 -0
  110. package/dist/components/verbs/build.d.ts.map +1 -0
  111. package/dist/components/verbs/cloud-executor.d.ts +88 -0
  112. package/dist/components/verbs/cloud-executor.d.ts.map +1 -0
  113. package/dist/components/verbs/component-bom.d.ts +103 -0
  114. package/dist/components/verbs/component-bom.d.ts.map +1 -0
  115. package/dist/components/verbs/index.d.ts +49 -0
  116. package/dist/components/verbs/index.d.ts.map +1 -0
  117. package/dist/components/verbs/license-policy.d.ts +56 -0
  118. package/dist/components/verbs/license-policy.d.ts.map +1 -0
  119. package/dist/components/verbs/lockfile-sbom-generator.d.ts +81 -0
  120. package/dist/components/verbs/lockfile-sbom-generator.d.ts.map +1 -0
  121. package/dist/components/verbs/process-runner.d.ts +64 -0
  122. package/dist/components/verbs/process-runner.d.ts.map +1 -0
  123. package/dist/components/verbs/reproducibility.d.ts +80 -0
  124. package/dist/components/verbs/reproducibility.d.ts.map +1 -0
  125. package/dist/components/verbs/sbom-generator.d.ts +157 -0
  126. package/dist/components/verbs/sbom-generator.d.ts.map +1 -0
  127. package/dist/components/verbs/sbom.d.ts +79 -0
  128. package/dist/components/verbs/sbom.d.ts.map +1 -0
  129. package/dist/components/verbs/shell.d.ts +35 -0
  130. package/dist/components/verbs/shell.d.ts.map +1 -0
  131. package/dist/components/verbs/sign.d.ts +222 -0
  132. package/dist/components/verbs/sign.d.ts.map +1 -0
  133. package/dist/components/verbs/stub.d.ts +18 -0
  134. package/dist/components/verbs/stub.d.ts.map +1 -0
  135. package/dist/components/verbs/tool-sbom-generator.d.ts +51 -0
  136. package/dist/components/verbs/tool-sbom-generator.d.ts.map +1 -0
  137. package/dist/components/verbs/verify.d.ts +106 -0
  138. package/dist/components/verbs/verify.d.ts.map +1 -0
  139. package/dist/components/verbs/vex.d.ts +65 -0
  140. package/dist/components/verbs/vex.d.ts.map +1 -0
  141. package/dist/components/verbs/vuln-gate.d.ts +91 -0
  142. package/dist/components/verbs/vuln-gate.d.ts.map +1 -0
  143. package/dist/components/verbs/vuln-scan.d.ts +116 -0
  144. package/dist/components/verbs/vuln-scan.d.ts.map +1 -0
  145. package/dist/components/verbs/wait-verify.d.ts +85 -0
  146. package/dist/components/verbs/wait-verify.d.ts.map +1 -0
  147. package/dist/config.d.ts +205 -0
  148. package/dist/config.d.ts.map +1 -1
  149. package/dist/index.d.ts +4 -0
  150. package/dist/index.d.ts.map +1 -1
  151. package/dist/lexicon.d.ts +78 -0
  152. package/dist/lexicon.d.ts.map +1 -1
  153. package/dist/lifecycle/build-ledger-store.d.ts +121 -0
  154. package/dist/lifecycle/build-ledger-store.d.ts.map +1 -0
  155. package/dist/lifecycle/build-ledger.d.ts +225 -0
  156. package/dist/lifecycle/build-ledger.d.ts.map +1 -0
  157. package/dist/lifecycle/git.d.ts +84 -0
  158. package/dist/lifecycle/git.d.ts.map +1 -1
  159. package/dist/lifecycle/index.d.ts +5 -0
  160. package/dist/lifecycle/index.d.ts.map +1 -1
  161. package/dist/lifecycle/oras-referrer-lookup.d.ts +41 -0
  162. package/dist/lifecycle/oras-referrer-lookup.d.ts.map +1 -0
  163. package/dist/lifecycle/release-ledger.d.ts +122 -0
  164. package/dist/lifecycle/release-ledger.d.ts.map +1 -0
  165. package/dist/lifecycle/status.d.ts +150 -0
  166. package/dist/lifecycle/status.d.ts.map +1 -0
  167. package/dist/lint/component-checks.d.ts +95 -0
  168. package/dist/lint/component-checks.d.ts.map +1 -0
  169. package/dist/lint/engine.d.ts +23 -0
  170. package/dist/lint/engine.d.ts.map +1 -1
  171. package/dist/lint/presets/strict.json +8 -1
  172. package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.d.ts +4 -0
  173. package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.d.ts.map +1 -0
  174. package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.d.ts +4 -0
  175. package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.d.ts.map +1 -0
  176. package/dist/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.d.ts +4 -0
  177. package/dist/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.d.ts.map +1 -0
  178. package/dist/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.d.ts +4 -0
  179. package/dist/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.d.ts.map +1 -0
  180. package/dist/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.d.ts +4 -0
  181. package/dist/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.d.ts.map +1 -0
  182. package/dist/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.d.ts +10 -0
  183. package/dist/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.d.ts.map +1 -0
  184. package/dist/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.d.ts +3 -0
  185. package/dist/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.d.ts.map +1 -0
  186. package/dist/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.d.ts +4 -0
  187. package/dist/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.d.ts.map +1 -0
  188. package/dist/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.d.ts +4 -0
  189. package/dist/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.d.ts.map +1 -0
  190. package/dist/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.d.ts +4 -0
  191. package/dist/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.d.ts.map +1 -0
  192. package/dist/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.d.ts +4 -0
  193. package/dist/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.d.ts.map +1 -0
  194. package/dist/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.d.ts +4 -0
  195. package/dist/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.d.ts.map +1 -0
  196. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts +4 -0
  197. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts.map +1 -0
  198. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts +4 -0
  199. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts.map +1 -0
  200. package/dist/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.d.ts +4 -0
  201. package/dist/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.d.ts.map +1 -0
  202. package/dist/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.d.ts +4 -0
  203. package/dist/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.d.ts.map +1 -0
  204. package/dist/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.d.ts +4 -0
  205. package/dist/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.d.ts.map +1 -0
  206. package/dist/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.d.ts +4 -0
  207. package/dist/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.d.ts.map +1 -0
  208. package/dist/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.d.ts +4 -0
  209. package/dist/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.d.ts.map +1 -0
  210. package/dist/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.d.ts +4 -0
  211. package/dist/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.d.ts.map +1 -0
  212. package/dist/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.d.ts +3 -0
  213. package/dist/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.d.ts.map +1 -0
  214. package/dist/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.d.ts +4 -0
  215. package/dist/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.d.ts.map +1 -0
  216. package/dist/lint/rules/comp/comp001-publishes-never-applies.d.ts +20 -0
  217. package/dist/lint/rules/comp/comp001-publishes-never-applies.d.ts.map +1 -0
  218. package/dist/lint/rules/comp/comp002-dangling-wiring-ref.d.ts +28 -0
  219. package/dist/lint/rules/comp/comp002-dangling-wiring-ref.d.ts.map +1 -0
  220. package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts +39 -0
  221. package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts.map +1 -0
  222. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts +37 -0
  223. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts.map +1 -0
  224. package/dist/lint/rules/comp/comp005-capability-kind-is-noun.d.ts +43 -0
  225. package/dist/lint/rules/comp/comp005-capability-kind-is-noun.d.ts.map +1 -0
  226. package/dist/lint/rules/comp/comp006-shell-needs-reason.d.ts +22 -0
  227. package/dist/lint/rules/comp/comp006-shell-needs-reason.d.ts.map +1 -0
  228. package/dist/lint/rules/comp/comp007-composition-sprawl.d.ts +33 -0
  229. package/dist/lint/rules/comp/comp007-composition-sprawl.d.ts.map +1 -0
  230. package/dist/lint/rules/comp/index.d.ts +18 -0
  231. package/dist/lint/rules/comp/index.d.ts.map +1 -0
  232. package/dist/lint/rules/comp/support.d.ts +71 -0
  233. package/dist/lint/rules/comp/support.d.ts.map +1 -0
  234. package/dist/lsp/lexicon-providers.d.ts.map +1 -1
  235. package/dist/op/activity-registry.d.ts +11 -6
  236. package/dist/op/activity-registry.d.ts.map +1 -1
  237. package/dist/op/activity-runtime.d.ts +24 -0
  238. package/dist/op/activity-runtime.d.ts.map +1 -0
  239. package/dist/op/builders.d.ts +56 -0
  240. package/dist/op/builders.d.ts.map +1 -1
  241. package/dist/op/index.d.ts +2 -1
  242. package/dist/op/index.d.ts.map +1 -1
  243. package/dist/ownership.d.ts +20 -14
  244. package/dist/ownership.d.ts.map +1 -1
  245. package/package.json +14 -1
  246. package/src/audit/catalog.test.ts +74 -22
  247. package/src/audit/catalog.ts +74 -471
  248. package/src/audit/proof.ts +4 -2
  249. package/src/audit/report-html.test.ts +14 -7
  250. package/src/audit/report-model.ts +20 -9
  251. package/src/audit/report.test.ts +36 -13
  252. package/src/audit/report.ts +2 -0
  253. package/src/audit/rules-doc.test.ts +8 -6
  254. package/src/audit/rules-doc.ts +13 -5
  255. package/src/cli/commands/audit.ts +23 -18
  256. package/src/cli/commands/component-lint.test.ts +158 -0
  257. package/src/cli/commands/doctor.test.ts +116 -2
  258. package/src/cli/commands/doctor.ts +46 -0
  259. package/src/cli/commands/lexicon-rolling-upgrade.ts +198 -0
  260. package/src/cli/commands/lexicon-surface-diff.test.ts +104 -0
  261. package/src/cli/commands/lexicon-surface-diff.ts +185 -0
  262. package/src/cli/commands/lint.ts +142 -1
  263. package/src/cli/commands/pinned-upgrade.ts +204 -0
  264. package/src/cli/handlers/build.ts +58 -1
  265. package/src/cli/handlers/components.test.ts +504 -0
  266. package/src/cli/handlers/components.ts +404 -0
  267. package/src/cli/handlers/dev.ts +60 -1
  268. package/src/cli/handlers/graph.ts +47 -2
  269. package/src/cli/handlers/misc.ts +45 -1
  270. package/src/cli/handlers/run.test.ts +967 -3
  271. package/src/cli/handlers/run.ts +618 -11
  272. package/src/cli/main.test.ts +66 -0
  273. package/src/cli/main.ts +105 -2
  274. package/src/cli/registry.ts +35 -0
  275. package/src/codegen/fetch.test.ts +84 -3
  276. package/src/codegen/fetch.ts +76 -2
  277. package/src/codegen/lexicon-regen.test.ts +298 -0
  278. package/src/codegen/lexicon-regen.ts +545 -0
  279. package/src/codegen/pinned-upgrade.test.ts +399 -0
  280. package/src/codegen/pinned-upgrade.ts +423 -0
  281. package/src/codegen/rolling-upgrade.test.ts +333 -0
  282. package/src/codegen/rolling-upgrade.ts +320 -0
  283. package/src/codegen/surface-snapshot.test.ts +461 -0
  284. package/src/codegen/surface-snapshot.ts +546 -0
  285. package/src/codegen/version-bump.test.ts +47 -0
  286. package/src/codegen/version-bump.ts +39 -0
  287. package/src/components/README.md +130 -0
  288. package/src/components/SPRAWL-VALIDATION.md +181 -0
  289. package/src/components/__fixtures__/alb-ecs-service.json +51 -0
  290. package/src/components/__fixtures__/dynamodb-infra.json +32 -0
  291. package/src/components/__fixtures__/emr-job-consumer.json +26 -0
  292. package/src/components/__fixtures__/jar-lib-producer.json +16 -0
  293. package/src/components/__fixtures__/lambda-image-processor.json +28 -0
  294. package/src/components/__fixtures__/neo4j-fanout.json +34 -0
  295. package/src/components/__fixtures__/single-host-compose.json +29 -0
  296. package/src/components/auto-release.test.ts +217 -0
  297. package/src/components/auto-release.ts +216 -0
  298. package/src/components/builders.test.ts +45 -0
  299. package/src/components/builders.ts +71 -0
  300. package/src/components/capability-plugin-loader.ts +218 -0
  301. package/src/components/capability-plugin-schema.ts +56 -0
  302. package/src/components/capability-plugin.test.ts +235 -0
  303. package/src/components/capability-plugin.ts +108 -0
  304. package/src/components/capability.ts +117 -0
  305. package/src/components/cli-support.test.ts +579 -0
  306. package/src/components/cli-support.ts +481 -0
  307. package/src/components/component-schema.test.ts +265 -0
  308. package/src/components/component.schema.json +235 -0
  309. package/src/components/component.test.ts +251 -0
  310. package/src/components/component.ts +255 -0
  311. package/src/components/config-defaults.test.ts +263 -0
  312. package/src/components/config-defaults.ts +145 -0
  313. package/src/components/discover.test.ts +352 -0
  314. package/src/components/discover.ts +167 -0
  315. package/src/components/driver-output.ts +71 -0
  316. package/src/components/driver.test.ts +638 -0
  317. package/src/components/driver.ts +690 -0
  318. package/src/components/index.ts +113 -0
  319. package/src/components/manifest-persistence.test.ts +152 -0
  320. package/src/components/manifest-persistence.ts +164 -0
  321. package/src/components/pilots/README.md +33 -0
  322. package/src/components/pilots/alb-ecs.pilot.ts +63 -0
  323. package/src/components/pilots/dynamodb.pilot.ts +59 -0
  324. package/src/components/pilots/jar-emr.pilot.ts +69 -0
  325. package/src/components/pilots/lambda.pilot.ts +55 -0
  326. package/src/components/pilots/neo4j-fanout.pilot.ts +92 -0
  327. package/src/components/pilots/pilots-lint-clean.test.ts +78 -0
  328. package/src/components/pilots/pilots.test.ts +126 -0
  329. package/src/components/pilots/project.ts +13 -0
  330. package/src/components/presets/comp007-reduction.test.ts +122 -0
  331. package/src/components/presets/ecs-fargate.test.ts +84 -0
  332. package/src/components/presets/ecs-fargate.ts +103 -0
  333. package/src/components/presets/index.ts +32 -0
  334. package/src/components/presets/lambda.test.ts +35 -0
  335. package/src/components/presets/lambda.ts +53 -0
  336. package/src/components/presets/single-host-compose.test.ts +65 -0
  337. package/src/components/presets/single-host-compose.ts +64 -0
  338. package/src/components/registry.test.ts +112 -0
  339. package/src/components/registry.ts +37 -0
  340. package/src/components/starter-plugin.ts +105 -0
  341. package/src/components/temporal-codegen-loader.ts +41 -0
  342. package/src/components/verbs/__fixtures__/package-lock.json +45 -0
  343. package/src/components/verbs/__fixtures__/pom.xml +37 -0
  344. package/src/components/verbs/__fixtures__/schemas/cyclonedx-1.5.schema.json +3800 -0
  345. package/src/components/verbs/__fixtures__/schemas/cyclonedx-spdx-license.schema.json +621 -0
  346. package/src/components/verbs/__fixtures__/schemas/jsf-0.82.schema.json +240 -0
  347. package/src/components/verbs/__fixtures__/schemas/spdx-2.3.schema.json +740 -0
  348. package/src/components/verbs/__tests__/mock-cloud-executor.ts +102 -0
  349. package/src/components/verbs/__tests__/mock-process-runner.ts +79 -0
  350. package/src/components/verbs/__tests__/mock-sbom-generator.ts +85 -0
  351. package/src/components/verbs/bom-writer.test.ts +331 -0
  352. package/src/components/verbs/bom-writer.ts +386 -0
  353. package/src/components/verbs/build-archive.test.ts +251 -0
  354. package/src/components/verbs/build-archive.ts +296 -0
  355. package/src/components/verbs/build.test.ts +222 -0
  356. package/src/components/verbs/build.ts +252 -0
  357. package/src/components/verbs/cloud-executor.ts +181 -0
  358. package/src/components/verbs/component-bom.test.ts +219 -0
  359. package/src/components/verbs/component-bom.ts +189 -0
  360. package/src/components/verbs/index.ts +49 -0
  361. package/src/components/verbs/license-policy.ts +115 -0
  362. package/src/components/verbs/lockfile-sbom-generator.test.ts +195 -0
  363. package/src/components/verbs/lockfile-sbom-generator.ts +236 -0
  364. package/src/components/verbs/process-runner.test.ts +79 -0
  365. package/src/components/verbs/process-runner.ts +113 -0
  366. package/src/components/verbs/reproducibility.test.ts +24 -0
  367. package/src/components/verbs/reproducibility.ts +100 -0
  368. package/src/components/verbs/sbom-generator.test.ts +96 -0
  369. package/src/components/verbs/sbom-generator.ts +209 -0
  370. package/src/components/verbs/sbom.test.ts +184 -0
  371. package/src/components/verbs/sbom.ts +152 -0
  372. package/src/components/verbs/shell.test.ts +36 -0
  373. package/src/components/verbs/shell.ts +51 -0
  374. package/src/components/verbs/sign.test.ts +210 -0
  375. package/src/components/verbs/sign.ts +350 -0
  376. package/src/components/verbs/stub.ts +34 -0
  377. package/src/components/verbs/tool-sbom-generator.test.ts +194 -0
  378. package/src/components/verbs/tool-sbom-generator.ts +192 -0
  379. package/src/components/verbs/verify.test.ts +159 -0
  380. package/src/components/verbs/verify.ts +179 -0
  381. package/src/components/verbs/vex.ts +182 -0
  382. package/src/components/verbs/vuln-gate.test.ts +201 -0
  383. package/src/components/verbs/vuln-gate.ts +170 -0
  384. package/src/components/verbs/vuln-scan.test.ts +145 -0
  385. package/src/components/verbs/vuln-scan.ts +266 -0
  386. package/src/components/verbs/wait-verify.test.ts +111 -0
  387. package/src/components/verbs/wait-verify.ts +171 -0
  388. package/src/config.test.ts +74 -1
  389. package/src/config.ts +219 -0
  390. package/src/index.ts +32 -0
  391. package/src/lexicon.ts +88 -0
  392. package/src/lifecycle/build-ledger-store.test.ts +217 -0
  393. package/src/lifecycle/build-ledger-store.ts +165 -0
  394. package/src/lifecycle/build-ledger.test.ts +349 -0
  395. package/src/lifecycle/build-ledger.ts +305 -0
  396. package/src/lifecycle/git.test.ts +118 -0
  397. package/src/lifecycle/git.ts +149 -14
  398. package/src/lifecycle/index.ts +5 -0
  399. package/src/lifecycle/oras-referrer-lookup.test.ts +116 -0
  400. package/src/lifecycle/oras-referrer-lookup.ts +110 -0
  401. package/src/lifecycle/release-ledger.test.ts +209 -0
  402. package/src/lifecycle/release-ledger.ts +185 -0
  403. package/src/lifecycle/status.test.ts +330 -0
  404. package/src/lifecycle/status.ts +278 -0
  405. package/src/lint/component-checks.ts +148 -0
  406. package/src/lint/config.test.ts +21 -0
  407. package/src/lint/engine.ts +11 -2
  408. package/src/lint/presets/relaxed.json +8 -1
  409. package/src/lint/presets/strict.json +8 -1
  410. package/src/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.ts +9 -0
  411. package/src/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.ts +9 -0
  412. package/src/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.ts +11 -0
  413. package/src/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.ts +13 -0
  414. package/src/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.ts +11 -0
  415. package/src/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.ts +27 -0
  416. package/src/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.ts +10 -0
  417. package/src/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.ts +23 -0
  418. package/src/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.ts +10 -0
  419. package/src/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.ts +19 -0
  420. package/src/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.ts +10 -0
  421. package/src/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.ts +20 -0
  422. package/src/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.ts +23 -0
  423. package/src/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.ts +10 -0
  424. package/src/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.ts +14 -0
  425. package/src/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.ts +18 -0
  426. package/src/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.ts +10 -0
  427. package/src/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.ts +14 -0
  428. package/src/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.ts +13 -0
  429. package/src/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.ts +13 -0
  430. package/src/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.ts +12 -0
  431. package/src/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.ts +18 -0
  432. package/src/lint/rules/comp/comp.test.ts +378 -0
  433. package/src/lint/rules/comp/comp001-publishes-never-applies.ts +78 -0
  434. package/src/lint/rules/comp/comp002-dangling-wiring-ref.ts +94 -0
  435. package/src/lint/rules/comp/comp003-mutating-no-rollback.ts +115 -0
  436. package/src/lint/rules/comp/comp004-gate-needs-temporal.ts +66 -0
  437. package/src/lint/rules/comp/comp005-capability-kind-is-noun.ts +113 -0
  438. package/src/lint/rules/comp/comp006-shell-needs-reason.ts +55 -0
  439. package/src/lint/rules/comp/comp007-composition-sprawl.ts +71 -0
  440. package/src/lint/rules/comp/index.ts +37 -0
  441. package/src/lint/rules/comp/support.ts +173 -0
  442. package/src/lsp/lexicon-providers.test.ts +55 -0
  443. package/src/lsp/lexicon-providers.ts +13 -3
  444. package/src/op/activity-registry.test.ts +38 -0
  445. package/src/op/activity-registry.ts +31 -12
  446. package/src/op/activity-runtime.ts +73 -0
  447. package/src/op/builders.ts +92 -0
  448. package/src/op/index.ts +3 -1
  449. package/src/ownership.test.ts +24 -26
  450. package/src/ownership.ts +26 -38
package/src/lexicon.ts CHANGED
@@ -7,6 +7,8 @@ import type { TypeScriptGenerator } from "./import/generator";
7
7
  import type { ArtifactIntegrity } from "./lexicon-integrity";
8
8
  import type { CompletionContext, CompletionItem, HoverContext, HoverInfo, CodeActionContext, CodeAction } from "./lsp/types";
9
9
  import type { McpToolContribution, McpResourceContribution } from "./mcp/types";
10
+ import type { DriverComponent } from "./components/driver";
11
+ import type { RuleMeta } from "./audit/catalog";
10
12
 
11
13
  /**
12
14
  * Manifest for a packaged lexicon — metadata embedded in the tarball.
@@ -161,6 +163,68 @@ export interface InitTemplateSet {
161
163
  * Required lifecycle methods enforce consistency: every lexicon must support
162
164
  * generate, validate, coverage, and package operations.
163
165
  */
166
+ /**
167
+ * Where a lexicon pins an upstream schema/spec version, and where to look for a
168
+ * newer one — the lexicon's own declaration for the self-upgrade tooling
169
+ * (`chant dev pinned-upgrade`), so core never hard-codes per-lexicon upstream
170
+ * repos or version-constant locations. See ./codegen/pinned-upgrade.ts.
171
+ */
172
+ export interface UpstreamPin {
173
+ /** Source file (relative to the lexicon package root) holding the pinned version constant. */
174
+ readonly file: string;
175
+ /** Regex whose first capture group is the current pinned version in `file`. */
176
+ readonly pattern: RegExp;
177
+ /** Rebuild a line that matched `pattern`, substituting the new version. */
178
+ replace(newVersion: string, line: string): string;
179
+ /** Where to query for the latest stable upstream tag. */
180
+ readonly upstream: {
181
+ readonly owner: string;
182
+ readonly repo: string;
183
+ /** `releases` = published releases; `tags` = every git tag. */
184
+ readonly kind: "releases" | "tags";
185
+ /** Only consider tags ending with this suffix (e.g. "-ee" for GitLab). */
186
+ readonly tagSuffix?: string;
187
+ };
188
+ }
189
+
190
+ /** One CI job in a generated pipeline (generate mode) — a thin trigger for one component in one wave. */
191
+ export interface ComponentPipelineJob {
192
+ /** CI job name (safe as a YAML key). */
193
+ jobName: string;
194
+ /** The component this job triggers. */
195
+ component: string;
196
+ /** The stage/wave this job runs in. */
197
+ stage: string;
198
+ /** Direct dependency job names this job waits on (mirrors the component's `dependsOn`). */
199
+ needs: string[];
200
+ }
201
+
202
+ /** Generic knobs for generate mode, applied once across every generated job (never per component). */
203
+ export interface ComponentPipelineOptions {
204
+ /** Target environment threaded into the trigger command. */
205
+ env?: string;
206
+ /** The trigger command per component, argv with `{name}` substituted. */
207
+ runCommand?: string[];
208
+ /** Extra script lines appended to every job. */
209
+ extraScript?: string[];
210
+ /** Script lines run before the trigger command in every job. */
211
+ beforeScript?: string[];
212
+ /** Container image every job runs in. */
213
+ image?: string;
214
+ /** Top-level CI `variables:` block. */
215
+ variables?: Record<string, string>;
216
+ }
217
+
218
+ /** The synthesized CI pipeline for a component graph (generate mode). */
219
+ export interface ComponentPipelineResult {
220
+ /** The synthesized CI YAML (e.g. `.gitlab-ci.yml` content). */
221
+ yaml: string;
222
+ /** Wave-ordered stage names (matches the driver's graph waves 1:1). */
223
+ stages: string[];
224
+ /** Every generated job, in emit order. */
225
+ jobs: ComponentPipelineJob[];
226
+ }
227
+
164
228
  export interface LexiconPlugin {
165
229
  // ── Required ──────────────────────────────────────────────
166
230
  /** Human-readable name (e.g. "aws", "gcp") */
@@ -191,6 +255,15 @@ export interface LexiconPlugin {
191
255
  /** Return post-synthesis checks for build validation */
192
256
  postSynthChecks?(): PostSynthCheck[];
193
257
 
258
+ /**
259
+ * Audit catalog metadata (title/tier/fix/authority/category) for this
260
+ * lexicon's `postSynthChecks`, keyed by check id — the per-provider half of
261
+ * `chant audit`'s rule catalog (#687, epic #350). Core aggregates these over
262
+ * its static catalog via `resolveAuditCatalog` (./audit/catalog.ts). Omit
263
+ * for lexicons whose rule metadata still lives in core's static catalog.
264
+ */
265
+ auditCatalog?(): Record<string, RuleMeta>;
266
+
194
267
  /** Return intrinsic function definitions */
195
268
  intrinsics?(): IntrinsicDef[];
196
269
 
@@ -219,6 +292,21 @@ export interface LexiconPlugin {
219
292
  /** Optional initialization hook */
220
293
  init?(): void | Promise<void>;
221
294
 
295
+ /** How this lexicon pins its upstream schema version + where to check for a newer one (self-upgrade tooling — see ./codegen/pinned-upgrade.ts). Omit for lexicons with no pinned upstream. */
296
+ readonly upstreamPin?: UpstreamPin;
297
+
298
+ /**
299
+ * Generate a CI pipeline from the discovered component graph — the
300
+ * `chant build --components --generate <name>` seam. Only CI-provider
301
+ * lexicons (gitlab, github, forgejo) implement this; core owns the generic
302
+ * driver (discovery + graph waves) and dispatches here. Omit for lexicons
303
+ * that are not CI providers.
304
+ */
305
+ generateComponentPipeline?(
306
+ components: DriverComponent[],
307
+ options?: ComponentPipelineOptions,
308
+ ): ComponentPipelineResult;
309
+
222
310
  // LSP
223
311
  /** Provide completions for LSP */
224
312
  completionProvider?(ctx: CompletionContext): CompletionItem[];
@@ -0,0 +1,217 @@
1
+ import { describe, test, expect } from "vitest";
2
+ import { withTestDir } from "@intentius/chant-test-utils";
3
+ import { spawnSync } from "node:child_process";
4
+ import { writeFileSync } from "node:fs";
5
+ import { join } from "node:path";
6
+ import {
7
+ persistBuildManifest,
8
+ readBuildManifest,
9
+ listBuildManifestDigests,
10
+ readAllBuildManifests,
11
+ findBuildManifestByArtifactDigest,
12
+ } from "./build-ledger-store";
13
+ import {
14
+ createBuildArchiveManifest,
15
+ addArchiveEntry,
16
+ type BuildArchiveManifest,
17
+ } from "../components/verbs/build-archive";
18
+
19
+ function git(args: string[], cwd: string): { stdout: string; exitCode: number } {
20
+ const r = spawnSync("git", args, { cwd, encoding: "utf-8" });
21
+ return { stdout: r.stdout ?? "", exitCode: r.status ?? -1 };
22
+ }
23
+
24
+ async function initRepo(dir: string): Promise<void> {
25
+ git(["init", "-q", "-b", "main"], dir);
26
+ git(["config", "user.email", "test@chant.dev"], dir);
27
+ git(["config", "user.name", "Test"], dir);
28
+ writeFileSync(join(dir, "README.md"), "fixture\n");
29
+ git(["add", "README.md"], dir);
30
+ git(["commit", "-q", "-m", "init"], dir);
31
+ }
32
+
33
+ function makeManifest(component: string, imageDigest: string): BuildArchiveManifest {
34
+ let manifest = createBuildArchiveManifest(component, { now: () => new Date("2026-01-01T00:00:00.000Z") });
35
+ manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: imageDigest });
36
+ manifest = addArchiveEntry(manifest, {
37
+ kind: "sbom",
38
+ path: "image.tar.sbom.json",
39
+ digest: "sha256:sbomdoc",
40
+ mediaType: "application/spdx+json",
41
+ subjectDigest: imageDigest,
42
+ packageCount: 12,
43
+ generator: "syft",
44
+ });
45
+ return manifest;
46
+ }
47
+
48
+ describe("lifecycle/build-ledger-store", () => {
49
+ describe("persistBuildManifest / readBuildManifest", () => {
50
+ test("round-trips a manifest by its own manifestDigest", async () => {
51
+ await withTestDir(async (dir) => {
52
+ await initRepo(dir);
53
+ const manifest = makeManifest("search-service", "sha256:image1");
54
+
55
+ const { commit } = await persistBuildManifest(manifest, { cwd: dir });
56
+ expect(commit).toMatch(/^[0-9a-f]{40}$/);
57
+
58
+ const readBack = await readBuildManifest(manifest.manifestDigest, { cwd: dir });
59
+ expect(readBack).not.toBeNull();
60
+ expect(readBack).toEqual(manifest);
61
+ });
62
+ });
63
+
64
+ test("preserves BOM entries and reproducibility/provenance across the round-trip", async () => {
65
+ await withTestDir(async (dir) => {
66
+ await initRepo(dir);
67
+ let manifest = createBuildArchiveManifest("svc");
68
+ manifest = addArchiveEntry(manifest, {
69
+ kind: "image",
70
+ path: "image.tar",
71
+ digest: "sha256:image1",
72
+ provenance: { sourceRef: "deadbeef", artifactDigest: "sha256:image1" },
73
+ });
74
+ manifest = addArchiveEntry(manifest, {
75
+ kind: "sbom",
76
+ path: "image.tar.sbom.json",
77
+ digest: "sha256:sbomdoc",
78
+ mediaType: "application/spdx+json",
79
+ subjectDigest: "sha256:image1",
80
+ bomKind: "software",
81
+ packageCount: 42,
82
+ generator: "syft",
83
+ });
84
+
85
+ await persistBuildManifest(manifest, { cwd: dir });
86
+ const readBack = await readBuildManifest(manifest.manifestDigest, { cwd: dir });
87
+
88
+ expect(readBack?.contents.find((e) => e.kind === "image")?.reproducibility).toEqual({ basis: "best-effort" });
89
+ expect(readBack?.contents.find((e) => e.kind === "image")?.provenance).toEqual({
90
+ sourceRef: "deadbeef",
91
+ artifactDigest: "sha256:image1",
92
+ });
93
+ const sbomEntry = readBack?.contents.find((e) => e.kind === "sbom");
94
+ expect(sbomEntry?.packageCount).toBe(42);
95
+ expect(sbomEntry?.generator).toBe("syft");
96
+ expect(sbomEntry?.bomKind).toBe("software");
97
+ });
98
+ });
99
+
100
+ test("readBuildManifest returns null for an unknown digest", async () => {
101
+ await withTestDir(async (dir) => {
102
+ await initRepo(dir);
103
+ expect(await readBuildManifest("sha256:doesnotexist", { cwd: dir })).toBeNull();
104
+ });
105
+ });
106
+
107
+ test("persisting the same manifest twice overwrites rather than duplicating", async () => {
108
+ await withTestDir(async (dir) => {
109
+ await initRepo(dir);
110
+ const manifest = makeManifest("svc", "sha256:image1");
111
+ await persistBuildManifest(manifest, { cwd: dir });
112
+ await persistBuildManifest(manifest, { cwd: dir });
113
+
114
+ const digests = await listBuildManifestDigests({ cwd: dir });
115
+ expect(digests).toHaveLength(1);
116
+ });
117
+ });
118
+
119
+ test("release ledger and build manifest store coexist on the same orphan branch", async () => {
120
+ const { appendReleaseRecord, readReleaseLedger } = await import("./release-ledger");
121
+ await withTestDir(async (dir) => {
122
+ await initRepo(dir);
123
+ const manifest = makeManifest("svc", "sha256:image1");
124
+ await persistBuildManifest(manifest, { cwd: dir });
125
+ await appendReleaseRecord(
126
+ {
127
+ component: "svc",
128
+ env: "prod",
129
+ digest: "sha256:image1",
130
+ gitSha: "deadbeef",
131
+ runId: "run-1",
132
+ timestamp: "2026-01-01T00:00:00.000Z",
133
+ actor: "ci-bot",
134
+ },
135
+ { cwd: dir },
136
+ );
137
+
138
+ expect(await readBuildManifest(manifest.manifestDigest, { cwd: dir })).not.toBeNull();
139
+ const { records } = await readReleaseLedger("prod", { cwd: dir });
140
+ expect(records).toHaveLength(1);
141
+ });
142
+ });
143
+ });
144
+
145
+ describe("listBuildManifestDigests / readAllBuildManifests", () => {
146
+ test("returns empty when no manifest has ever been persisted", async () => {
147
+ await withTestDir(async (dir) => {
148
+ await initRepo(dir);
149
+ expect(await listBuildManifestDigests({ cwd: dir })).toEqual([]);
150
+ expect(await readAllBuildManifests({ cwd: dir })).toEqual([]);
151
+ });
152
+ });
153
+
154
+ test("lists every persisted manifest's own digest", async () => {
155
+ await withTestDir(async (dir) => {
156
+ await initRepo(dir);
157
+ const a = makeManifest("svc-a", "sha256:a1");
158
+ const b = makeManifest("svc-b", "sha256:b1");
159
+ await persistBuildManifest(a, { cwd: dir });
160
+ await persistBuildManifest(b, { cwd: dir });
161
+
162
+ const digests = await listBuildManifestDigests({ cwd: dir });
163
+ expect(digests.sort()).toEqual([a.manifestDigest, b.manifestDigest].sort());
164
+ });
165
+ });
166
+
167
+ test("readAllBuildManifests reads every persisted manifest back", async () => {
168
+ await withTestDir(async (dir) => {
169
+ await initRepo(dir);
170
+ const a = makeManifest("svc-a", "sha256:a1");
171
+ const b = makeManifest("svc-b", "sha256:b1");
172
+ await persistBuildManifest(a, { cwd: dir });
173
+ await persistBuildManifest(b, { cwd: dir });
174
+
175
+ const manifests = await readAllBuildManifests({ cwd: dir });
176
+ expect(manifests.map((m) => m.component).sort()).toEqual(["svc-a", "svc-b"]);
177
+ });
178
+ });
179
+ });
180
+
181
+ describe("findBuildManifestByArtifactDigest", () => {
182
+ test("finds the manifest carrying a matching image-entry digest", async () => {
183
+ await withTestDir(async (dir) => {
184
+ await initRepo(dir);
185
+ const manifest = makeManifest("search-service", "sha256:image1");
186
+ await persistBuildManifest(manifest, { cwd: dir });
187
+
188
+ const found = await findBuildManifestByArtifactDigest("sha256:image1", { cwd: dir });
189
+ expect(found?.manifestDigest).toBe(manifest.manifestDigest);
190
+ expect(found?.component).toBe("search-service");
191
+ });
192
+ });
193
+
194
+ test("returns undefined when no persisted manifest carries the digest", async () => {
195
+ await withTestDir(async (dir) => {
196
+ await initRepo(dir);
197
+ const manifest = makeManifest("svc", "sha256:image1");
198
+ await persistBuildManifest(manifest, { cwd: dir });
199
+
200
+ expect(await findBuildManifestByArtifactDigest("sha256:unknown", { cwd: dir })).toBeUndefined();
201
+ });
202
+ });
203
+
204
+ test("distinguishes between multiple persisted manifests by digest", async () => {
205
+ await withTestDir(async (dir) => {
206
+ await initRepo(dir);
207
+ const a = makeManifest("svc-a", "sha256:a1");
208
+ const b = makeManifest("svc-b", "sha256:b1");
209
+ await persistBuildManifest(a, { cwd: dir });
210
+ await persistBuildManifest(b, { cwd: dir });
211
+
212
+ expect((await findBuildManifestByArtifactDigest("sha256:a1", { cwd: dir }))?.component).toBe("svc-a");
213
+ expect((await findBuildManifestByArtifactDigest("sha256:b1", { cwd: dir }))?.component).toBe("svc-b");
214
+ });
215
+ });
216
+ });
217
+ });
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Build-archive manifest store (#609, epic #551 follow-up to #564/#568/#606/
3
+ * #613/#614): persists a `BuildArchiveManifest`
4
+ * (../components/verbs/build-archive.ts) to a durable, content-addressed
5
+ * location so it survives past the in-process build/run that assembled it.
6
+ *
7
+ * **The gap this closes.** `docker-build` (../components/verbs/build.ts) and
8
+ * friends assemble a real `BuildArchiveManifest` in memory — complete with
9
+ * `sbom`/config-BOM entries (#606/#613) and per-artifact
10
+ * `reproducibility`/`provenance` (#614) — but nothing ever wrote it anywhere
11
+ * durable. `build-ledger.ts`'s `buildLedgerEntries`/`componentBomSummary` are
12
+ * unit-tested against a manifest handed to them directly, but
13
+ * `chant components status` (../cli/handlers/components.ts) had no manifest
14
+ * to hand them at all: `componentBom` and `build.reproducibility` were always
15
+ * `null` in that CLI path. This module is the missing durable layer — write
16
+ * once at build time, read back by digest at status time.
17
+ *
18
+ * **Storage choice: the `chant/lifecycle` orphan branch, a sibling namespace
19
+ * to the release ledger.** Reuses the exact git-plumbing
20
+ * `writeBlobToPath`/`readBlobFromPath`/`listFilesInDir` (./git.ts) that
21
+ * `writeSnapshot`/`appendReleaseRecordLine` already use — same hash-object →
22
+ * mktree → commit-tree → update-ref pipeline, same `--force-with-lease` push
23
+ * discipline (`pushLifecycle`), same "no checkout, no working tree changes"
24
+ * property. Rejected alternatives:
25
+ * - **A new on-disk directory in the working tree** — would need its own
26
+ * persistence/sync story (commit it? gitignore it? sync across CI
27
+ * runners?) that the orphan branch already solves for the release ledger;
28
+ * introducing a second mechanism for a manifest that is conceptually the
29
+ * same kind of durable, git-native record would be needless divergence.
30
+ * - **A path under `<env>/`** — a build archive is *not* env-scoped. The
31
+ * whole point of promote-by-digest (epic #551 "4. Build archive +
32
+ * deferred publish") is that one build's manifest is referenced from
33
+ * every environment it gets promoted to; writing it under a single env
34
+ * directory would either duplicate it per env (defeating "byte-identical
35
+ * across envs") or arbitrarily pick one env as "the" owner. Manifests
36
+ * instead live under a fixed top-level `_builds/` directory on the same
37
+ * orphan branch, keyed by `manifestDigest` — a peer of the per-env
38
+ * directories `writeSnapshot`/the release ledger already create there,
39
+ * never nested inside one.
40
+ *
41
+ * **Content-addressed, keyed by `manifestDigest`.** One file per manifest,
42
+ * `_builds/<manifestDigest-with-":"->"_">.json`, so a rebuild of
43
+ * byte-identical inputs (same `manifestDigest`, per
44
+ * ../components/verbs/build-archive.ts's module doc) overwrites its own
45
+ * entry with identical content rather than accumulating duplicates —
46
+ * `writeBlobToPath` already replaces-by-path, so this falls out for free.
47
+ * `findBuildManifestByArtifactDigest` additionally resolves "the manifest
48
+ * that produced artifact digest X" (the join key `chant components status`
49
+ * actually has on hand — a `ReleaseRecord.digest` is an *artifact* digest,
50
+ * not a manifest digest) by scanning every stored manifest's `contents` for
51
+ * a matching entry. This is a linear scan over however many manifests have
52
+ * been persisted; fine for the ledger's expected scale (one manifest per
53
+ * build), and avoids maintaining a second reverse-index file that could get
54
+ * out of sync with the manifests themselves.
55
+ */
56
+
57
+ import { sortedJsonReplacer } from "../utils";
58
+ import { writeBlobToPath, readBlobFromPath, listFilesInDir } from "./git";
59
+ import type { BuildArchiveManifest } from "../components/verbs/build-archive";
60
+
61
+ /** Fixed top-level directory on the `chant/lifecycle` orphan branch that holds every persisted build manifest — a peer of the per-env directories, never nested inside one (see module doc for why). */
62
+ const BUILDS_DIR = "_builds";
63
+
64
+ /** Turn a `sha256:...`-style digest into a filesystem/git-tree-safe filename stem (`:` is not usable in a git tree entry name the way this plumbing constructs it). */
65
+ function digestToFilenameStem(digest: string): string {
66
+ return digest.replace(/:/g, "_");
67
+ }
68
+
69
+ function manifestFilename(manifestDigest: string): string {
70
+ return `${digestToFilenameStem(manifestDigest)}.json`;
71
+ }
72
+
73
+ /**
74
+ * Persist a `BuildArchiveManifest` to the orphan branch, keyed by its own
75
+ * `manifestDigest`. Does not push to the remote — call `pushLifecycle`
76
+ * (./git.ts) afterward, the same two-step (`write` then `push`) shape
77
+ * `appendReleaseRecord`/`takeSnapshot` use, so a caller persisting several
78
+ * manifests (or a manifest plus a release record) in one run can batch the
79
+ * push into one network round-trip.
80
+ *
81
+ * Idempotent: persisting the same manifest twice (identical `manifestDigest`)
82
+ * overwrites the same path with identical bytes — safe to call more than
83
+ * once for the same build.
84
+ */
85
+ export async function persistBuildManifest(
86
+ manifest: BuildArchiveManifest,
87
+ opts?: { cwd?: string },
88
+ ): Promise<{ commit: string }> {
89
+ const json = JSON.stringify(manifest, sortedJsonReplacer);
90
+ const commit = await writeBlobToPath(BUILDS_DIR, manifestFilename(manifest.manifestDigest), json, "Build manifest", opts);
91
+ return { commit };
92
+ }
93
+
94
+ /**
95
+ * Read a persisted `BuildArchiveManifest` back by its own `manifestDigest`.
96
+ * Returns `null` when no manifest was ever persisted under that digest
97
+ * (never throws — a status query for a digest with no recorded build is a
98
+ * normal, expected state, matching `readSnapshot`'s null-on-missing
99
+ * convention).
100
+ */
101
+ export async function readBuildManifest(
102
+ manifestDigest: string,
103
+ opts?: { cwd?: string },
104
+ ): Promise<BuildArchiveManifest | null> {
105
+ const content = await readBlobFromPath(BUILDS_DIR, manifestFilename(manifestDigest), opts);
106
+ if (!content) return null;
107
+ try {
108
+ return JSON.parse(content) as BuildArchiveManifest;
109
+ } catch {
110
+ return null;
111
+ }
112
+ }
113
+
114
+ /** List the `manifestDigest` of every build manifest persisted on the orphan branch. */
115
+ export async function listBuildManifestDigests(opts?: { cwd?: string }): Promise<string[]> {
116
+ const files = await listFilesInDir(BUILDS_DIR, opts);
117
+ return files
118
+ .filter((f) => f.endsWith(".json"))
119
+ .map((f) => f.replace(/\.json$/, "").replace(/_/g, ":"));
120
+ }
121
+
122
+ /**
123
+ * Read every persisted build manifest. Best-effort: a manifest file that
124
+ * fails to parse is skipped rather than failing the whole read (mirrors
125
+ * `readReleaseLedger`'s "skip malformed lines" degrade-gracefully
126
+ * convention) — the caller only gets the count implicitly via array length,
127
+ * since a corrupted manifest blob is a much rarer/more surprising event than
128
+ * a hand-edited ledger line and not expected to need the same "surfaced
129
+ * count" treatment.
130
+ */
131
+ export async function readAllBuildManifests(opts?: { cwd?: string }): Promise<BuildArchiveManifest[]> {
132
+ const digests = await listBuildManifestDigests(opts);
133
+ const manifests: BuildArchiveManifest[] = [];
134
+ for (const digest of digests) {
135
+ const manifest = await readBuildManifest(digest, opts);
136
+ if (manifest) manifests.push(manifest);
137
+ }
138
+ return manifests;
139
+ }
140
+
141
+ /**
142
+ * Find the persisted manifest that carries an entry (any kind — image,
143
+ * template, asset, or sbom) whose `digest` equals `artifactDigest`. This is
144
+ * the lookup `chant components status` actually needs: a `ReleaseRecord`
145
+ * (../lifecycle/release-ledger.ts) records the *artifact* digest that was
146
+ * promoted/deployed, not the manifest's own aggregate `manifestDigest` — so
147
+ * resolving "the build behind this deployed digest" means searching
148
+ * manifest contents, not a direct keyed lookup. Returns `undefined` when no
149
+ * persisted manifest contains a matching entry (no build was ever persisted
150
+ * for that artifact digest — e.g. it predates #609, or was recorded via the
151
+ * standalone `chant components release` command with no corresponding
152
+ * `chant build`/`run` in this checkout).
153
+ *
154
+ * A `ReleaseRecord.manifestDigest`, when present (an optional field a caller
155
+ * may have supplied at record time), lets a caller skip this scan entirely
156
+ * via `readBuildManifest` directly — this function is the fallback for the
157
+ * common case where only the artifact digest is known.
158
+ */
159
+ export async function findBuildManifestByArtifactDigest(
160
+ artifactDigest: string,
161
+ opts?: { cwd?: string },
162
+ ): Promise<BuildArchiveManifest | undefined> {
163
+ const manifests = await readAllBuildManifests(opts);
164
+ return manifests.find((m) => m.contents.some((entry) => entry.digest === artifactDigest));
165
+ }