@intentius/chant 0.13.1 → 0.15.1

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 +59 -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 +99 -0
  448. package/src/op/index.ts +3 -1
  449. package/src/ownership.test.ts +24 -26
  450. package/src/ownership.ts +26 -38
@@ -0,0 +1,349 @@
1
+ import { describe, test, expect } from "vitest";
2
+ import {
3
+ buildLedgerEntries,
4
+ findReferrer,
5
+ noopReferrerLookup,
6
+ artifactReproducibilitySummary,
7
+ componentBomSummary,
8
+ type Referrer,
9
+ type ReferrerLookup,
10
+ } from "./build-ledger";
11
+ import {
12
+ createBuildArchiveManifest,
13
+ addArchiveEntry,
14
+ findSbomForSubject,
15
+ } from "../components/verbs/build-archive";
16
+
17
+ describe("build-ledger", () => {
18
+ describe("buildLedgerEntries", () => {
19
+ test("returns one entry per image-kind manifest entry", async () => {
20
+ let manifest = createBuildArchiveManifest("search-service", { now: () => new Date("2026-01-01T00:00:00.000Z") });
21
+ manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:image1" });
22
+ manifest = addArchiveEntry(manifest, { kind: "template", path: "search.template.json", digest: "sha256:tmpl1" });
23
+
24
+ const entries = await buildLedgerEntries(manifest);
25
+ expect(entries).toHaveLength(1);
26
+ expect(entries[0]).toMatchObject({
27
+ component: "search-service",
28
+ path: "image.tar",
29
+ digest: "sha256:image1",
30
+ manifestDigest: manifest.manifestDigest,
31
+ });
32
+ });
33
+
34
+ test("noop lookup (default) attaches no referrers", async () => {
35
+ let manifest = createBuildArchiveManifest("svc");
36
+ manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:image1" });
37
+ const entries = await buildLedgerEntries(manifest);
38
+ expect(entries[0].referrers).toEqual([]);
39
+ });
40
+
41
+ test("injected lookup attaches referrers keyed by digest", async () => {
42
+ let manifest = createBuildArchiveManifest("svc");
43
+ manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:image1" });
44
+
45
+ const sbom: Referrer = { kind: "sbom", mediaType: "application/vnd.cyclonedx+json", digest: "sha256:sbom1" };
46
+ const provenance: Referrer = { kind: "provenance", mediaType: "application/vnd.in-toto+json", digest: "sha256:prov1" };
47
+ const lookup: ReferrerLookup = {
48
+ async discover(digest) {
49
+ if (digest !== "sha256:image1") return [];
50
+ return [sbom, provenance];
51
+ },
52
+ };
53
+
54
+ const entries = await buildLedgerEntries(manifest, lookup);
55
+ expect(entries[0].referrers).toEqual([sbom, provenance]);
56
+ });
57
+
58
+ test("multiple image entries each get their own referrer lookup", async () => {
59
+ let manifest = createBuildArchiveManifest("multi-image");
60
+ manifest = addArchiveEntry(manifest, { kind: "image", path: "a.tar", digest: "sha256:a" });
61
+ manifest = addArchiveEntry(manifest, { kind: "image", path: "b.tar", digest: "sha256:b" });
62
+
63
+ const lookup: ReferrerLookup = {
64
+ async discover(digest) {
65
+ return [{ kind: "signature", mediaType: "application/vnd.dev.cosign.simplesigning.v1+json", digest: `${digest}-sig` }];
66
+ },
67
+ };
68
+
69
+ const entries = await buildLedgerEntries(manifest, lookup);
70
+ expect(entries.map((e) => e.path).sort()).toEqual(["a.tar", "b.tar"]);
71
+ const byDigest = new Map(entries.map((e) => [e.digest, e]));
72
+ expect(byDigest.get("sha256:a")?.referrers[0].digest).toBe("sha256:a-sig");
73
+ expect(byDigest.get("sha256:b")?.referrers[0].digest).toBe("sha256:b-sig");
74
+ });
75
+
76
+ test("no image entries -> no build ledger entries", async () => {
77
+ let manifest = createBuildArchiveManifest("infra-only");
78
+ manifest = addArchiveEntry(manifest, { kind: "template", path: "t.json", digest: "sha256:t" });
79
+ const entries = await buildLedgerEntries(manifest);
80
+ expect(entries).toEqual([]);
81
+ });
82
+ });
83
+
84
+ describe("findReferrer", () => {
85
+ test("finds a referrer by kind", () => {
86
+ const referrers: Referrer[] = [
87
+ { kind: "sbom", mediaType: "x", digest: "sha256:1" },
88
+ { kind: "signature", mediaType: "y", digest: "sha256:2" },
89
+ ];
90
+ expect(findReferrer(referrers, "signature")?.digest).toBe("sha256:2");
91
+ expect(findReferrer(referrers, "provenance")).toBeUndefined();
92
+ });
93
+ });
94
+
95
+ describe("noopReferrerLookup", () => {
96
+ test("always returns an empty list", async () => {
97
+ expect(await noopReferrerLookup.discover("sha256:anything")).toEqual([]);
98
+ });
99
+ });
100
+
101
+ describe("sbom summary (#606) — archive-carried vs referrer-projected", () => {
102
+ test("no sbom entry and no sbom referrer -> sbom is undefined (component opted out / no SBOM generated)", async () => {
103
+ let manifest = createBuildArchiveManifest("svc");
104
+ manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:image1" });
105
+ const entries = await buildLedgerEntries(manifest);
106
+ expect(entries[0].sbom).toBeUndefined();
107
+ });
108
+
109
+ test("archive-carried sbom entry is surfaced with format/package count/generator, source 'archive'", async () => {
110
+ let manifest = createBuildArchiveManifest("svc");
111
+ manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:image1" });
112
+ manifest = addArchiveEntry(manifest, {
113
+ kind: "sbom",
114
+ path: "image.tar.sbom.json",
115
+ digest: "sha256:sbomdoc",
116
+ mediaType: "application/spdx+json",
117
+ subjectDigest: "sha256:image1",
118
+ packageCount: 17,
119
+ generator: "syft",
120
+ });
121
+
122
+ const entries = await buildLedgerEntries(manifest);
123
+ expect(entries[0].sbom).toEqual({
124
+ mediaType: "application/spdx+json",
125
+ packageCount: 17,
126
+ generator: "syft",
127
+ source: "archive",
128
+ });
129
+ });
130
+
131
+ test("referrer-projected sbom is surfaced when no archive-carried entry exists, source 'referrer'", async () => {
132
+ let manifest = createBuildArchiveManifest("svc");
133
+ manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:image1" });
134
+
135
+ const lookup: ReferrerLookup = {
136
+ async discover(digest) {
137
+ if (digest !== "sha256:image1") return [];
138
+ return [{ kind: "sbom", mediaType: "application/vnd.cyclonedx+json", digest: "sha256:sbomref" }];
139
+ },
140
+ };
141
+
142
+ const entries = await buildLedgerEntries(manifest, lookup);
143
+ expect(entries[0].sbom).toEqual({
144
+ mediaType: "application/vnd.cyclonedx+json",
145
+ source: "referrer",
146
+ });
147
+ });
148
+
149
+ test("archive-carried sbom is preferred over a referrer-projected one when both exist", async () => {
150
+ let manifest = createBuildArchiveManifest("svc");
151
+ manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:image1" });
152
+ manifest = addArchiveEntry(manifest, {
153
+ kind: "sbom",
154
+ path: "image.tar.sbom.json",
155
+ digest: "sha256:sbomdoc",
156
+ mediaType: "application/spdx+json",
157
+ subjectDigest: "sha256:image1",
158
+ packageCount: 9,
159
+ generator: "buildkit",
160
+ });
161
+
162
+ const lookup: ReferrerLookup = {
163
+ async discover() {
164
+ return [{ kind: "sbom", mediaType: "application/vnd.cyclonedx+json", digest: "sha256:sbomref" }];
165
+ },
166
+ };
167
+
168
+ const entries = await buildLedgerEntries(manifest, lookup);
169
+ expect(entries[0].sbom).toEqual({
170
+ mediaType: "application/spdx+json",
171
+ packageCount: 9,
172
+ generator: "buildkit",
173
+ source: "archive",
174
+ });
175
+ });
176
+
177
+ test("format-agnostic: CycloneDX archive-carried sbom is surfaced the same way SPDX is", async () => {
178
+ let manifest = createBuildArchiveManifest("svc");
179
+ manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:image1" });
180
+ manifest = addArchiveEntry(manifest, {
181
+ kind: "sbom",
182
+ path: "image.tar.sbom.json",
183
+ digest: "sha256:sbomdoc",
184
+ mediaType: "application/vnd.cyclonedx+json",
185
+ subjectDigest: "sha256:image1",
186
+ packageCount: 5,
187
+ generator: "syft",
188
+ });
189
+
190
+ const entries = await buildLedgerEntries(manifest);
191
+ expect(entries[0].sbom?.mediaType).toBe("application/vnd.cyclonedx+json");
192
+ });
193
+
194
+ test("non-image artifact (jar/zip, no image entry) still carries its sbom in the manifest, findable independent of buildLedgerEntries (which is image-scoped)", async () => {
195
+ let manifest = createBuildArchiveManifest("jar-lib");
196
+ manifest = addArchiveEntry(manifest, { kind: "asset", path: "lib.jar", digest: "sha256:jar1" });
197
+ manifest = addArchiveEntry(manifest, {
198
+ kind: "sbom",
199
+ path: "lib.jar.sbom.json",
200
+ digest: "sha256:sbomdoc",
201
+ mediaType: "application/spdx+json",
202
+ subjectDigest: "sha256:jar1",
203
+ });
204
+
205
+ // buildLedgerEntries only enumerates image-kind entries (the build
206
+ // ledger's historical scope, #568) — a jar's SBOM is still readable
207
+ // straight from the archive via findSbomForSubject, the same
208
+ // artifact-type-agnostic accessor ./build-ledger.ts uses internally.
209
+ const entries = await buildLedgerEntries(manifest);
210
+ expect(entries).toEqual([]);
211
+ expect(findSbomForSubject(manifest, "sha256:jar1")).toMatchObject({ mediaType: "application/spdx+json" });
212
+ });
213
+
214
+ test("image entries surface their own reproducibility record (#614)", async () => {
215
+ let manifest = createBuildArchiveManifest("svc");
216
+ manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:image1" });
217
+ const entries = await buildLedgerEntries(manifest);
218
+ expect(entries[0].reproducibility).toEqual({ basis: "best-effort" });
219
+ });
220
+ });
221
+
222
+ describe("artifactReproducibilitySummary (#614)", () => {
223
+ test("reports every image/template/asset entry's own reproducibility, honestly per artifact type", () => {
224
+ let manifest = createBuildArchiveManifest("search-service");
225
+ manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:img1" });
226
+ manifest = addArchiveEntry(manifest, { kind: "template", path: "search.template.json", digest: "sha256:tmpl1" });
227
+ manifest = addArchiveEntry(manifest, { kind: "asset", path: "lib.jar", digest: "sha256:jar1" });
228
+
229
+ const summary = artifactReproducibilitySummary(manifest);
230
+ const byPath = new Map(summary.map((s) => [s.path, s]));
231
+ expect(byPath.get("image.tar")?.reproducibility).toEqual({ basis: "best-effort" });
232
+ expect(byPath.get("search.template.json")?.reproducibility).toEqual({
233
+ basis: "deterministic-synthesis",
234
+ verifyBy: "re-synth",
235
+ });
236
+ expect(byPath.get("lib.jar")?.reproducibility).toEqual({ basis: "best-effort" });
237
+ });
238
+
239
+ test("excludes sbom-kind entries — they describe another artifact, they aren't one themselves", () => {
240
+ let manifest = createBuildArchiveManifest("svc");
241
+ manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:img1" });
242
+ manifest = addArchiveEntry(manifest, {
243
+ kind: "sbom",
244
+ path: "image.tar.sbom.json",
245
+ digest: "sha256:sbom1",
246
+ subjectDigest: "sha256:img1",
247
+ });
248
+ const summary = artifactReproducibilitySummary(manifest);
249
+ expect(summary.map((s) => s.path)).toEqual(["image.tar"]);
250
+ });
251
+
252
+ test("surfaces the recorded provenance sourceRef when present", () => {
253
+ let manifest = createBuildArchiveManifest("svc");
254
+ manifest = addArchiveEntry(manifest, {
255
+ kind: "template",
256
+ path: "t.json",
257
+ digest: "sha256:t1",
258
+ provenance: { sourceRef: "abc123", artifactDigest: "sha256:t1" },
259
+ });
260
+ const summary = artifactReproducibilitySummary(manifest);
261
+ expect(summary[0]!.sourceRef).toBe("abc123");
262
+ });
263
+
264
+ test("no artifact entries -> empty summary", () => {
265
+ const manifest = createBuildArchiveManifest("empty");
266
+ expect(artifactReproducibilitySummary(manifest)).toEqual([]);
267
+ });
268
+ });
269
+
270
+ describe("componentBomSummary (#614)", () => {
271
+ test("undefined when the manifest carries no BOM at all", () => {
272
+ let manifest = createBuildArchiveManifest("svc");
273
+ manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:img1" });
274
+ expect(componentBomSummary(manifest)).toBeUndefined();
275
+ });
276
+
277
+ test("single leaf: isAssembly is false — a 1:1 component BOM", () => {
278
+ let manifest = createBuildArchiveManifest("infra-only");
279
+ manifest = addArchiveEntry(manifest, { kind: "template", path: "t.json", digest: "sha256:t1" });
280
+ manifest = addArchiveEntry(manifest, {
281
+ kind: "sbom",
282
+ bomKind: "config",
283
+ path: "t.json.config-bom.json",
284
+ digest: "sha256:sbom1",
285
+ subjectDigest: "sha256:t1",
286
+ mediaType: "application/spdx+json",
287
+ packageCount: 3,
288
+ generator: "chant-config-bom-extractor",
289
+ });
290
+
291
+ const summary = componentBomSummary(manifest);
292
+ expect(summary?.isAssembly).toBe(false);
293
+ expect(summary?.leaves).toHaveLength(1);
294
+ expect(summary?.leaves[0]).toEqual({
295
+ path: "t.json.config-bom.json",
296
+ bomKind: "config",
297
+ subjectDigest: "sha256:t1",
298
+ mediaType: "application/spdx+json",
299
+ packageCount: 3,
300
+ generator: "chant-config-bom-extractor",
301
+ });
302
+ expect(summary?.totalPackageCount).toBe(3);
303
+ });
304
+
305
+ test("multi leaf (software SBOM + config-BOM): isAssembly is true, totalPackageCount sums both leaves", () => {
306
+ let manifest = createBuildArchiveManifest("search-service");
307
+ manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:img1" });
308
+ manifest = addArchiveEntry(manifest, { kind: "template", path: "search.template.json", digest: "sha256:tmpl1" });
309
+ manifest = addArchiveEntry(manifest, {
310
+ kind: "sbom",
311
+ bomKind: "software",
312
+ path: "image.tar.sbom.json",
313
+ digest: "sha256:sbomsoft",
314
+ subjectDigest: "sha256:img1",
315
+ mediaType: "application/spdx+json",
316
+ packageCount: 17,
317
+ generator: "syft",
318
+ });
319
+ manifest = addArchiveEntry(manifest, {
320
+ kind: "sbom",
321
+ bomKind: "config",
322
+ path: "search.template.json.config-bom.json",
323
+ digest: "sha256:sbomconfig",
324
+ subjectDigest: "sha256:tmpl1",
325
+ mediaType: "application/spdx+json",
326
+ packageCount: 5,
327
+ generator: "chant-config-bom-extractor",
328
+ });
329
+
330
+ const summary = componentBomSummary(manifest);
331
+ expect(summary?.isAssembly).toBe(true);
332
+ expect(summary?.leaves).toHaveLength(2);
333
+ expect(summary?.totalPackageCount).toBe(22);
334
+ expect(summary?.leaves.map((l) => l.bomKind).sort()).toEqual(["config", "software"]);
335
+ });
336
+
337
+ test("bomKind defaults to 'software' when a leaf entry omits it, matching BuildArchiveEntry's own default", () => {
338
+ let manifest = createBuildArchiveManifest("svc");
339
+ manifest = addArchiveEntry(manifest, {
340
+ kind: "sbom",
341
+ path: "a.sbom.json",
342
+ digest: "sha256:s1",
343
+ subjectDigest: "sha256:img1",
344
+ });
345
+ const summary = componentBomSummary(manifest);
346
+ expect(summary?.leaves[0]?.bomKind).toBe("software");
347
+ });
348
+ });
349
+ });
@@ -0,0 +1,305 @@
1
+ /**
2
+ * Build ledger — the "what's been built" half of build & deploy observability
3
+ * (#568, epic #551 "Build & deploy observability" / "4. Build archive +
4
+ * deferred publish"; SBOM consume/surface added in #606).
5
+ *
6
+ * The `BuildArchive` manifest (../components/verbs/build-archive.ts) is
7
+ * itself the build ledger: it is content-addressed by digest and enumerates
8
+ * every image/template/asset/sbom a build produced. What #568 adds on top is
9
+ * discovery of an image's **referrers** — the SBOM, SLSA provenance, and
10
+ * signature attached to the same digest by tooling like `oras discover
11
+ * <digest>` / `cosign tree` (epic #551's phrasing).
12
+ *
13
+ * This module only *surfaces and consumes* SBOM/provenance/signature
14
+ * evidence — generation is ../components/verbs/sbom.ts's `generate-sbom`
15
+ * capability (#606; SLSA provenance + signing remain out of scope, noted as
16
+ * fast-follow siblings). Two sources feed the same `sbom` summary below, and
17
+ * a caller gets *both* without special-casing which one exists:
18
+ *
19
+ * - **archive-carried** — an `sbom`-kind entry in the same
20
+ * `BuildArchiveManifest`, written directly by `generate-sbom`. This is
21
+ * the **universal home**: it works for every artifact type (image, jar,
22
+ * zip) and every publish backend, including the registry-less
23
+ * `load-image-on-host` path, with no registry/network access required to
24
+ * read it back.
25
+ * - **referrer-projected** — an OCI referrer of `kind: "sbom"` discovered
26
+ * via `ReferrerLookup`, the registry-side convenience for image
27
+ * artifacts (`oras discover <digest>`/`cosign tree`) that #568 already
28
+ * modeled. Not every artifact type has this projection (only images
29
+ * published to a registry do), and not every environment can query it
30
+ * (`ReferrerLookup` defaults to `noopReferrerLookup`, no network).
31
+ *
32
+ * `buildLedgerEntries` prefers the archive-carried copy when both exist
33
+ * (same digest either way, since both trace back to the same
34
+ * `generate-sbom` run — the archive is the source of truth; the referrer is
35
+ * a projection of it) and falls back to the referrer when only that is
36
+ * available (e.g. a manifest read that predates #606, or a third-party tool
37
+ * attached an SBOM directly to the registry outside chant's own build). The
38
+ * `ReferrerLookup` interface remains injectable for exactly the reason
39
+ * `CloudExecutor` is injectable elsewhere in ../components/verbs: no test and
40
+ * no default implementation shells out to a real `oras`/`cosign`/registry
41
+ * call.
42
+ *
43
+ * **#614 additions.** Two more things get surfaced per manifest, both
44
+ * derived from data the manifest already carries (no new generation, no
45
+ * re-parsing of BOM bytes):
46
+ *
47
+ * - Each `BuildLedgerEntry` (still one per image, matching this module's
48
+ * existing scope) now carries that image's own `reproducibility` — read
49
+ * straight off its `BuildArchiveEntry`, never inferred from a sibling
50
+ * artifact (see ../components/verbs/reproducibility.ts's module doc for
51
+ * why a blanket flag would misrepresent a multi-artifact component).
52
+ * `artifactReproducibilitySummary` below reports the same per-artifact
53
+ * honesty for *every* artifact entry in a manifest (image, template,
54
+ * asset alike) — the full per-artifact picture #614 asks for, not just
55
+ * the image-scoped slice `buildLedgerEntries` has always covered.
56
+ * - `componentBomSummary` reports the component-level BOM aggregation
57
+ * picture (../components/verbs/component-bom.ts): how many leaf BOMs
58
+ * (software SBOM + IaC config-BOM) the manifest carries, each one's
59
+ * `bomKind`/format/package count, and whether they'd compose into a
60
+ * single-artifact (1:1) or real multi-artifact assembly. This is a
61
+ * summary read off the manifest's existing `sbom`-kind entries — it does
62
+ * not regenerate or re-validate the aggregate document (that stays
63
+ * `aggregateComponentBom`'s job, given each leaf's `BomPackage[]`, which
64
+ * the manifest alone does not carry).
65
+ */
66
+
67
+ import type { BuildArchiveManifest, BuildArchiveEntry } from "../components/verbs/build-archive";
68
+ import { findSbomForSubject, imageEntries, sbomEntries, artifactEntries } from "../components/verbs/build-archive";
69
+ import type { ArtifactReproducibility } from "../components/verbs/reproducibility";
70
+
71
+ /** The kind of referrer artifact attached to an image digest (mirrors the OCI referrers API / `oras discover` output). */
72
+ export type ReferrerKind = "sbom" | "provenance" | "signature";
73
+
74
+ /** One referrer attached to an image digest. */
75
+ export interface Referrer {
76
+ kind: ReferrerKind;
77
+ /** Media type of the referrer artifact itself (e.g. `application/vnd.cyclonedx+json`, `application/vnd.in-toto+json`). */
78
+ mediaType: string;
79
+ /** Content-addressed digest of the referrer artifact (`sha256:...`) — distinct from the subject digest it refers to. */
80
+ digest: string;
81
+ /** Where to fetch the referrer's content, when known (a registry blob ref, a file path, ...). Opaque to this module. */
82
+ location?: string;
83
+ }
84
+
85
+ /**
86
+ * Injectable lookup for an image digest's referrers — the seam a real
87
+ * `oras`/`cosign`/registry-API implementation plugs into. Defaults to
88
+ * `NoopReferrerLookup` (below) so build ledger queries degrade to "no
89
+ * referrers known" rather than requiring network/registry access, matching
90
+ * how ../components/verbs/cloud-executor.ts's real implementations are
91
+ * optional and mockable.
92
+ */
93
+ export interface ReferrerLookup {
94
+ /** List every referrer attached to `digest`, or `[]` if none / lookup unsupported. */
95
+ discover(digest: string): Promise<Referrer[]>;
96
+ }
97
+
98
+ /** Default `ReferrerLookup`: reports no referrers. A real registry/`oras`-backed implementation is out of scope here (belongs with #564's signing follow-up) — this keeps build-ledger queries safe to run with no cloud/network access. */
99
+ export const noopReferrerLookup: ReferrerLookup = {
100
+ async discover() {
101
+ return [];
102
+ },
103
+ };
104
+
105
+ /**
106
+ * Where a build-ledger entry's `sbom` summary was read from — surfaced so
107
+ * `chant components status` can report SBOM *source*, not just presence
108
+ * (#606). "avoid drift": whichever source answers, it traces back to the
109
+ * same `generate-sbom` run for a given digest, since promote-by-digest never
110
+ * rebuilds — see docs/components/build-archive.mdx.
111
+ */
112
+ export type SbomSource = "archive" | "referrer";
113
+
114
+ /**
115
+ * The SBOM summary `chant components status` surfaces for one build-ledger
116
+ * entry (#606): format, package count, and where it was read from — never
117
+ * the raw SBOM bytes (chant's job is to generate/store/surface, not to
118
+ * interpret SBOM content; that is the scan-result/VEX policy-gate
119
+ * fast-follow, out of scope here).
120
+ */
121
+ export interface SbomSummary {
122
+ /** Media type of the SBOM document (`application/spdx+json` or `application/vnd.cyclonedx+json`) — the format-agnostic key, never a hardcoded format. */
123
+ mediaType: string;
124
+ /** Number of packages/components enumerated, when known. */
125
+ packageCount?: number;
126
+ /** Which tool produced it (e.g. "syft", "buildkit"), when known. */
127
+ generator?: string;
128
+ /** Whether this summary was read from the archive-carried entry or a registry-discovered OCI referrer. */
129
+ source: SbomSource;
130
+ }
131
+
132
+ /** One build-ledger entry: an archive's image entry plus whatever referrers are attached to its digest. */
133
+ export interface BuildLedgerEntry {
134
+ /** Component the archive was built for. */
135
+ component: string;
136
+ /** Archive-relative path of the image entry (see `BuildArchiveEntry.path`). */
137
+ path: string;
138
+ /** Content-addressed digest of the image (`sha256:...`) — the join key with the release ledger. */
139
+ digest: string;
140
+ /** When the archive manifest was assembled. */
141
+ createdAt: string;
142
+ /** The manifest's own aggregate digest — traces a promoted image digest back to the exact manifest that produced it. */
143
+ manifestDigest: string;
144
+ /** SBOM/provenance/signature referrers attached to this image's digest, if any were discoverable. */
145
+ referrers: Referrer[];
146
+ /**
147
+ * SBOM summary for this digest, read from whichever source is available —
148
+ * the archive-carried `sbom`-kind manifest entry (preferred; works for
149
+ * every artifact type and every publish backend, registry or not) or a
150
+ * registry-discovered `sbom`-kind referrer (image artifacts only, when
151
+ * `lookup` can reach a registry). `undefined` when neither source has one
152
+ * — including when the component opted out of `generate-sbom` entirely.
153
+ */
154
+ sbom?: SbomSummary;
155
+ /**
156
+ * This image's own honest, per-artifact reproducibility record (#614) —
157
+ * read from its `BuildArchiveEntry.reproducibility`. An `image` entry
158
+ * defaults to `{ basis: "best-effort" }` (see
159
+ * ../components/verbs/reproducibility.ts's `defaultReproducibility`)
160
+ * absent a real reproducible-build attestation; never inferred from a
161
+ * sibling `template`/`asset` entry's basis.
162
+ */
163
+ reproducibility?: ArtifactReproducibility;
164
+ }
165
+
166
+ /**
167
+ * Build the ledger view for one archive manifest: every `image`-kind entry,
168
+ * each paired with its OCI referrers via `lookup` and its SBOM summary (read
169
+ * from the archive-carried entry first, the referrer projection otherwise —
170
+ * see this module's doc comment). "What's built" becomes "what's built, from
171
+ * which commit, by what, when" once a release record (./release-ledger.ts)
172
+ * is joined onto this by `digest` — this function only produces the build
173
+ * side of that join.
174
+ */
175
+ export async function buildLedgerEntries(
176
+ manifest: BuildArchiveManifest,
177
+ lookup: ReferrerLookup = noopReferrerLookup,
178
+ ): Promise<BuildLedgerEntry[]> {
179
+ const images = imageEntries(manifest);
180
+ const entries: BuildLedgerEntry[] = [];
181
+ for (const image of images) {
182
+ const referrers = await lookup.discover(image.digest);
183
+
184
+ const archiveSbom = findSbomForSubject(manifest, image.digest);
185
+ const referrerSbom = findReferrer(referrers, "sbom");
186
+ const sbom: SbomSummary | undefined = archiveSbom
187
+ ? {
188
+ mediaType: archiveSbom.mediaType ?? "application/octet-stream",
189
+ packageCount: archiveSbom.packageCount,
190
+ generator: archiveSbom.generator,
191
+ source: "archive",
192
+ }
193
+ : referrerSbom
194
+ ? { mediaType: referrerSbom.mediaType, source: "referrer" }
195
+ : undefined;
196
+
197
+ entries.push({
198
+ component: manifest.component,
199
+ path: image.path,
200
+ digest: image.digest,
201
+ createdAt: manifest.createdAt,
202
+ manifestDigest: manifest.manifestDigest,
203
+ referrers,
204
+ sbom,
205
+ reproducibility: image.reproducibility,
206
+ });
207
+ }
208
+ return entries;
209
+ }
210
+
211
+ /** Find the referrer of a given kind for a digest, if any (a convenience for "does this build have a signature/SBOM/provenance"). */
212
+ export function findReferrer(referrers: Referrer[], kind: ReferrerKind): Referrer | undefined {
213
+ return referrers.find((r) => r.kind === kind);
214
+ }
215
+
216
+ // ── #614: per-artifact reproducibility + component BOM summary ─────────────
217
+
218
+ /** One artifact's reproducibility, named for display (`chant components status`) rather than just keyed by digest. */
219
+ export interface ArtifactReproducibilitySummary {
220
+ /** Archive-relative path of the artifact (`BuildArchiveEntry.path`). */
221
+ path: string;
222
+ /** Which kind of artifact this is. */
223
+ kind: BuildArchiveEntry["kind"];
224
+ /** Content-addressed digest of the artifact. */
225
+ digest: string;
226
+ /** The artifact's own reproducibility record, when one is recorded (absent only for `sbom`-kind entries, which describe another artifact rather than being one). */
227
+ reproducibility?: ArtifactReproducibility;
228
+ /** Source ref/commit that produced this artifact, when recorded (#614's provenance link). */
229
+ sourceRef?: string;
230
+ }
231
+
232
+ /**
233
+ * Report every artifact entry's (`image`/`template`/`asset`) own
234
+ * reproducibility + provenance, honestly per-artifact — the full picture
235
+ * #614 asks for, wider than `buildLedgerEntries`'s image-only scope. Order
236
+ * matches `artifactEntries`' manifest insertion order; sort by `path`
237
+ * yourself for a stable display order (mirroring
238
+ * `computeManifestDigest`'s own path-sort convention).
239
+ */
240
+ export function artifactReproducibilitySummary(manifest: BuildArchiveManifest): ArtifactReproducibilitySummary[] {
241
+ return artifactEntries(manifest).map((entry) => ({
242
+ path: entry.path,
243
+ kind: entry.kind,
244
+ digest: entry.digest,
245
+ reproducibility: entry.reproducibility,
246
+ sourceRef: entry.provenance?.sourceRef,
247
+ }));
248
+ }
249
+
250
+ /** One leaf BOM in a component's aggregation picture — the summary form `componentBomSummary` reports, without regenerating or re-validating the aggregate document itself. */
251
+ export interface ComponentBomLeafSummary {
252
+ /** Archive-relative path of the leaf BOM document. */
253
+ path: string;
254
+ /** Software SBOM or IaC config-BOM (#613's `bomKind`, defaulting to `"software"` the same way `BuildArchiveEntry.bomKind` does). */
255
+ bomKind: "software" | "config";
256
+ /** Digest of the artifact this leaf describes (`subjectDigest`), when recorded. */
257
+ subjectDigest?: string;
258
+ /** Media type of the leaf document (format-agnostic key, per #613's convention). */
259
+ mediaType: string;
260
+ /** Package/component count this leaf enumerates, when known. */
261
+ packageCount?: number;
262
+ /** Which tool produced this leaf, when known. */
263
+ generator?: string;
264
+ }
265
+
266
+ /**
267
+ * Summary of a component's BOM aggregation picture (#614,
268
+ * ../components/verbs/component-bom.ts): every leaf BOM the manifest
269
+ * carries, whether they compose 1:1 (a single-artifact component — the leaf
270
+ * BOM *is* the component BOM, structurally) or as a real multi-artifact
271
+ * assembly (2+ leaves), and the combined package count across every leaf.
272
+ */
273
+ export interface ComponentBomSummary {
274
+ /** Every leaf BOM (software SBOM + IaC config-BOM) the manifest carries. */
275
+ leaves: ComponentBomLeafSummary[];
276
+ /** Sum of every leaf's `packageCount` (0 for a leaf with an unknown count). */
277
+ totalPackageCount: number;
278
+ /** `false` when there are 0 or 1 leaves (nothing to assemble, or a 1:1 component BOM); `true` when there are 2+ leaves (a real multi-artifact assembly, per #614's "single-artifact = 1:1, multi-artifact = a real assembly"). */
279
+ isAssembly: boolean;
280
+ }
281
+
282
+ /**
283
+ * Build the component BOM summary for one manifest, straight from its
284
+ * existing `sbom`-kind entries — no regeneration, no re-parsing of BOM
285
+ * bytes. `undefined` when the manifest carries no BOM at all (a component
286
+ * that composed neither `generate-sbom` nor `extract-config-bom` — skipping
287
+ * is structural, matching every other BOM consumer's convention in this
288
+ * codebase).
289
+ */
290
+ export function componentBomSummary(manifest: BuildArchiveManifest): ComponentBomSummary | undefined {
291
+ const boms = sbomEntries(manifest);
292
+ if (boms.length === 0) return undefined;
293
+
294
+ const leaves: ComponentBomLeafSummary[] = boms.map((entry) => ({
295
+ path: entry.path,
296
+ bomKind: entry.bomKind ?? "software",
297
+ subjectDigest: entry.subjectDigest,
298
+ mediaType: entry.mediaType ?? "application/octet-stream",
299
+ packageCount: entry.packageCount,
300
+ generator: entry.generator,
301
+ }));
302
+ const totalPackageCount = leaves.reduce((sum, l) => sum + (l.packageCount ?? 0), 0);
303
+
304
+ return { leaves, totalPackageCount, isAssembly: leaves.length > 1 };
305
+ }