@intentius/chant 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (453) hide show
  1. package/dist/audit/catalog.d.ts +45 -5
  2. package/dist/audit/catalog.d.ts.map +1 -1
  3. package/dist/audit/fetch.d.ts.map +1 -1
  4. package/dist/audit/proof.d.ts +3 -0
  5. package/dist/audit/proof.d.ts.map +1 -1
  6. package/dist/audit/report-model.d.ts +4 -1
  7. package/dist/audit/report-model.d.ts.map +1 -1
  8. package/dist/audit/report.d.ts +3 -0
  9. package/dist/audit/report.d.ts.map +1 -1
  10. package/dist/audit/rules-doc.d.ts +8 -2
  11. package/dist/audit/rules-doc.d.ts.map +1 -1
  12. package/dist/cli/commands/audit.d.ts.map +1 -1
  13. package/dist/cli/commands/doctor.d.ts.map +1 -1
  14. package/dist/cli/commands/lexicon-rolling-upgrade.d.ts +37 -0
  15. package/dist/cli/commands/lexicon-rolling-upgrade.d.ts.map +1 -0
  16. package/dist/cli/commands/lexicon-surface-diff.d.ts +48 -0
  17. package/dist/cli/commands/lexicon-surface-diff.d.ts.map +1 -0
  18. package/dist/cli/commands/lint.d.ts.map +1 -1
  19. package/dist/cli/commands/pinned-upgrade.d.ts +41 -0
  20. package/dist/cli/commands/pinned-upgrade.d.ts.map +1 -0
  21. package/dist/cli/handlers/build.d.ts.map +1 -1
  22. package/dist/cli/handlers/components.d.ts +39 -0
  23. package/dist/cli/handlers/components.d.ts.map +1 -0
  24. package/dist/cli/handlers/dev.d.ts +3 -0
  25. package/dist/cli/handlers/dev.d.ts.map +1 -1
  26. package/dist/cli/handlers/graph.d.ts +4 -2
  27. package/dist/cli/handlers/graph.d.ts.map +1 -1
  28. package/dist/cli/handlers/misc.d.ts.map +1 -1
  29. package/dist/cli/handlers/run.d.ts +49 -0
  30. package/dist/cli/handlers/run.d.ts.map +1 -1
  31. package/dist/cli/main.d.ts.map +1 -1
  32. package/dist/cli/registry.d.ts +35 -0
  33. package/dist/cli/registry.d.ts.map +1 -1
  34. package/dist/codegen/fetch.d.ts +22 -0
  35. package/dist/codegen/fetch.d.ts.map +1 -1
  36. package/dist/codegen/lexicon-regen.d.ts +110 -0
  37. package/dist/codegen/lexicon-regen.d.ts.map +1 -0
  38. package/dist/codegen/pinned-upgrade.d.ts +153 -0
  39. package/dist/codegen/pinned-upgrade.d.ts.map +1 -0
  40. package/dist/codegen/rolling-upgrade.d.ts +138 -0
  41. package/dist/codegen/rolling-upgrade.d.ts.map +1 -0
  42. package/dist/codegen/surface-snapshot.d.ts +173 -0
  43. package/dist/codegen/surface-snapshot.d.ts.map +1 -0
  44. package/dist/codegen/version-bump.d.ts +21 -0
  45. package/dist/codegen/version-bump.d.ts.map +1 -0
  46. package/dist/components/auto-release.d.ts +93 -0
  47. package/dist/components/auto-release.d.ts.map +1 -0
  48. package/dist/components/builders.d.ts +37 -0
  49. package/dist/components/builders.d.ts.map +1 -0
  50. package/dist/components/capability-plugin-loader.d.ts +111 -0
  51. package/dist/components/capability-plugin-loader.d.ts.map +1 -0
  52. package/dist/components/capability-plugin-schema.d.ts +24 -0
  53. package/dist/components/capability-plugin-schema.d.ts.map +1 -0
  54. package/dist/components/capability-plugin.d.ts +85 -0
  55. package/dist/components/capability-plugin.d.ts.map +1 -0
  56. package/dist/components/capability.d.ts +89 -0
  57. package/dist/components/capability.d.ts.map +1 -0
  58. package/dist/components/cli-support.d.ts +204 -0
  59. package/dist/components/cli-support.d.ts.map +1 -0
  60. package/dist/components/component.d.ts +179 -0
  61. package/dist/components/component.d.ts.map +1 -0
  62. package/dist/components/config-defaults.d.ts +41 -0
  63. package/dist/components/config-defaults.d.ts.map +1 -0
  64. package/dist/components/discover.d.ts +60 -0
  65. package/dist/components/discover.d.ts.map +1 -0
  66. package/dist/components/driver-output.d.ts +20 -0
  67. package/dist/components/driver-output.d.ts.map +1 -0
  68. package/dist/components/driver.d.ts +209 -0
  69. package/dist/components/driver.d.ts.map +1 -0
  70. package/dist/components/index.d.ts +22 -0
  71. package/dist/components/index.d.ts.map +1 -0
  72. package/dist/components/manifest-persistence.d.ts +79 -0
  73. package/dist/components/manifest-persistence.d.ts.map +1 -0
  74. package/dist/components/pilots/alb-ecs.pilot.d.ts +27 -0
  75. package/dist/components/pilots/alb-ecs.pilot.d.ts.map +1 -0
  76. package/dist/components/pilots/dynamodb.pilot.d.ts +22 -0
  77. package/dist/components/pilots/dynamodb.pilot.d.ts.map +1 -0
  78. package/dist/components/pilots/jar-emr.pilot.d.ts +37 -0
  79. package/dist/components/pilots/jar-emr.pilot.d.ts.map +1 -0
  80. package/dist/components/pilots/lambda.pilot.d.ts +35 -0
  81. package/dist/components/pilots/lambda.pilot.d.ts.map +1 -0
  82. package/dist/components/pilots/neo4j-fanout.pilot.d.ts +17 -0
  83. package/dist/components/pilots/neo4j-fanout.pilot.d.ts.map +1 -0
  84. package/dist/components/pilots/project.d.ts +13 -0
  85. package/dist/components/pilots/project.d.ts.map +1 -0
  86. package/dist/components/presets/ecs-fargate.d.ts +59 -0
  87. package/dist/components/presets/ecs-fargate.d.ts.map +1 -0
  88. package/dist/components/presets/index.d.ts +29 -0
  89. package/dist/components/presets/index.d.ts.map +1 -0
  90. package/dist/components/presets/lambda.d.ts +35 -0
  91. package/dist/components/presets/lambda.d.ts.map +1 -0
  92. package/dist/components/presets/single-host-compose.d.ts +43 -0
  93. package/dist/components/presets/single-host-compose.d.ts.map +1 -0
  94. package/dist/components/registry.d.ts +29 -0
  95. package/dist/components/registry.d.ts.map +1 -0
  96. package/dist/components/starter-plugin.d.ts +51 -0
  97. package/dist/components/starter-plugin.d.ts.map +1 -0
  98. package/dist/components/temporal-codegen-loader.d.ts +27 -0
  99. package/dist/components/temporal-codegen-loader.d.ts.map +1 -0
  100. package/dist/components/verbs/__tests__/mock-cloud-executor.d.ts +35 -0
  101. package/dist/components/verbs/__tests__/mock-cloud-executor.d.ts.map +1 -0
  102. package/dist/components/verbs/__tests__/mock-process-runner.d.ts +45 -0
  103. package/dist/components/verbs/__tests__/mock-process-runner.d.ts.map +1 -0
  104. package/dist/components/verbs/__tests__/mock-sbom-generator.d.ts +33 -0
  105. package/dist/components/verbs/__tests__/mock-sbom-generator.d.ts.map +1 -0
  106. package/dist/components/verbs/bom-writer.d.ts +155 -0
  107. package/dist/components/verbs/bom-writer.d.ts.map +1 -0
  108. package/dist/components/verbs/build-archive.d.ts +205 -0
  109. package/dist/components/verbs/build-archive.d.ts.map +1 -0
  110. package/dist/components/verbs/build.d.ts +150 -0
  111. package/dist/components/verbs/build.d.ts.map +1 -0
  112. package/dist/components/verbs/cloud-executor.d.ts +88 -0
  113. package/dist/components/verbs/cloud-executor.d.ts.map +1 -0
  114. package/dist/components/verbs/component-bom.d.ts +103 -0
  115. package/dist/components/verbs/component-bom.d.ts.map +1 -0
  116. package/dist/components/verbs/index.d.ts +49 -0
  117. package/dist/components/verbs/index.d.ts.map +1 -0
  118. package/dist/components/verbs/license-policy.d.ts +56 -0
  119. package/dist/components/verbs/license-policy.d.ts.map +1 -0
  120. package/dist/components/verbs/lockfile-sbom-generator.d.ts +81 -0
  121. package/dist/components/verbs/lockfile-sbom-generator.d.ts.map +1 -0
  122. package/dist/components/verbs/process-runner.d.ts +64 -0
  123. package/dist/components/verbs/process-runner.d.ts.map +1 -0
  124. package/dist/components/verbs/reproducibility.d.ts +80 -0
  125. package/dist/components/verbs/reproducibility.d.ts.map +1 -0
  126. package/dist/components/verbs/sbom-generator.d.ts +157 -0
  127. package/dist/components/verbs/sbom-generator.d.ts.map +1 -0
  128. package/dist/components/verbs/sbom.d.ts +79 -0
  129. package/dist/components/verbs/sbom.d.ts.map +1 -0
  130. package/dist/components/verbs/shell.d.ts +35 -0
  131. package/dist/components/verbs/shell.d.ts.map +1 -0
  132. package/dist/components/verbs/sign.d.ts +222 -0
  133. package/dist/components/verbs/sign.d.ts.map +1 -0
  134. package/dist/components/verbs/stub.d.ts +18 -0
  135. package/dist/components/verbs/stub.d.ts.map +1 -0
  136. package/dist/components/verbs/tool-sbom-generator.d.ts +51 -0
  137. package/dist/components/verbs/tool-sbom-generator.d.ts.map +1 -0
  138. package/dist/components/verbs/verify.d.ts +106 -0
  139. package/dist/components/verbs/verify.d.ts.map +1 -0
  140. package/dist/components/verbs/vex.d.ts +65 -0
  141. package/dist/components/verbs/vex.d.ts.map +1 -0
  142. package/dist/components/verbs/vuln-gate.d.ts +91 -0
  143. package/dist/components/verbs/vuln-gate.d.ts.map +1 -0
  144. package/dist/components/verbs/vuln-scan.d.ts +116 -0
  145. package/dist/components/verbs/vuln-scan.d.ts.map +1 -0
  146. package/dist/components/verbs/wait-verify.d.ts +85 -0
  147. package/dist/components/verbs/wait-verify.d.ts.map +1 -0
  148. package/dist/config.d.ts +205 -0
  149. package/dist/config.d.ts.map +1 -1
  150. package/dist/index.d.ts +4 -0
  151. package/dist/index.d.ts.map +1 -1
  152. package/dist/lexicon.d.ts +78 -0
  153. package/dist/lexicon.d.ts.map +1 -1
  154. package/dist/lifecycle/build-ledger-store.d.ts +121 -0
  155. package/dist/lifecycle/build-ledger-store.d.ts.map +1 -0
  156. package/dist/lifecycle/build-ledger.d.ts +225 -0
  157. package/dist/lifecycle/build-ledger.d.ts.map +1 -0
  158. package/dist/lifecycle/git.d.ts +84 -0
  159. package/dist/lifecycle/git.d.ts.map +1 -1
  160. package/dist/lifecycle/index.d.ts +5 -0
  161. package/dist/lifecycle/index.d.ts.map +1 -1
  162. package/dist/lifecycle/oras-referrer-lookup.d.ts +41 -0
  163. package/dist/lifecycle/oras-referrer-lookup.d.ts.map +1 -0
  164. package/dist/lifecycle/release-ledger.d.ts +122 -0
  165. package/dist/lifecycle/release-ledger.d.ts.map +1 -0
  166. package/dist/lifecycle/status.d.ts +150 -0
  167. package/dist/lifecycle/status.d.ts.map +1 -0
  168. package/dist/lint/component-checks.d.ts +95 -0
  169. package/dist/lint/component-checks.d.ts.map +1 -0
  170. package/dist/lint/engine.d.ts +23 -0
  171. package/dist/lint/engine.d.ts.map +1 -1
  172. package/dist/lint/presets/strict.json +8 -1
  173. package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.d.ts +4 -0
  174. package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.d.ts.map +1 -0
  175. package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.d.ts +4 -0
  176. package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.d.ts.map +1 -0
  177. package/dist/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.d.ts +4 -0
  178. package/dist/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.d.ts.map +1 -0
  179. package/dist/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.d.ts +4 -0
  180. package/dist/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.d.ts.map +1 -0
  181. package/dist/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.d.ts +4 -0
  182. package/dist/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.d.ts.map +1 -0
  183. package/dist/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.d.ts +10 -0
  184. package/dist/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.d.ts.map +1 -0
  185. package/dist/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.d.ts +3 -0
  186. package/dist/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.d.ts.map +1 -0
  187. package/dist/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.d.ts +4 -0
  188. package/dist/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.d.ts.map +1 -0
  189. package/dist/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.d.ts +4 -0
  190. package/dist/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.d.ts.map +1 -0
  191. package/dist/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.d.ts +4 -0
  192. package/dist/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.d.ts.map +1 -0
  193. package/dist/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.d.ts +4 -0
  194. package/dist/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.d.ts.map +1 -0
  195. package/dist/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.d.ts +4 -0
  196. package/dist/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.d.ts.map +1 -0
  197. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts +4 -0
  198. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts.map +1 -0
  199. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts +4 -0
  200. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts.map +1 -0
  201. package/dist/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.d.ts +4 -0
  202. package/dist/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.d.ts.map +1 -0
  203. package/dist/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.d.ts +4 -0
  204. package/dist/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.d.ts.map +1 -0
  205. package/dist/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.d.ts +4 -0
  206. package/dist/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.d.ts.map +1 -0
  207. package/dist/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.d.ts +4 -0
  208. package/dist/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.d.ts.map +1 -0
  209. package/dist/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.d.ts +4 -0
  210. package/dist/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.d.ts.map +1 -0
  211. package/dist/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.d.ts +4 -0
  212. package/dist/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.d.ts.map +1 -0
  213. package/dist/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.d.ts +3 -0
  214. package/dist/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.d.ts.map +1 -0
  215. package/dist/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.d.ts +4 -0
  216. package/dist/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.d.ts.map +1 -0
  217. package/dist/lint/rules/comp/comp001-publishes-never-applies.d.ts +20 -0
  218. package/dist/lint/rules/comp/comp001-publishes-never-applies.d.ts.map +1 -0
  219. package/dist/lint/rules/comp/comp002-dangling-wiring-ref.d.ts +28 -0
  220. package/dist/lint/rules/comp/comp002-dangling-wiring-ref.d.ts.map +1 -0
  221. package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts +39 -0
  222. package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts.map +1 -0
  223. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts +37 -0
  224. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts.map +1 -0
  225. package/dist/lint/rules/comp/comp005-capability-kind-is-noun.d.ts +43 -0
  226. package/dist/lint/rules/comp/comp005-capability-kind-is-noun.d.ts.map +1 -0
  227. package/dist/lint/rules/comp/comp006-shell-needs-reason.d.ts +22 -0
  228. package/dist/lint/rules/comp/comp006-shell-needs-reason.d.ts.map +1 -0
  229. package/dist/lint/rules/comp/comp007-composition-sprawl.d.ts +33 -0
  230. package/dist/lint/rules/comp/comp007-composition-sprawl.d.ts.map +1 -0
  231. package/dist/lint/rules/comp/index.d.ts +18 -0
  232. package/dist/lint/rules/comp/index.d.ts.map +1 -0
  233. package/dist/lint/rules/comp/support.d.ts +71 -0
  234. package/dist/lint/rules/comp/support.d.ts.map +1 -0
  235. package/dist/lsp/lexicon-providers.d.ts.map +1 -1
  236. package/dist/op/activity-registry.d.ts +11 -6
  237. package/dist/op/activity-registry.d.ts.map +1 -1
  238. package/dist/op/activity-runtime.d.ts +24 -0
  239. package/dist/op/activity-runtime.d.ts.map +1 -0
  240. package/dist/op/builders.d.ts +56 -0
  241. package/dist/op/builders.d.ts.map +1 -1
  242. package/dist/op/index.d.ts +2 -1
  243. package/dist/op/index.d.ts.map +1 -1
  244. package/dist/ownership.d.ts +20 -14
  245. package/dist/ownership.d.ts.map +1 -1
  246. package/package.json +14 -1
  247. package/src/audit/catalog.test.ts +74 -22
  248. package/src/audit/catalog.ts +74 -471
  249. package/src/audit/fetch.test.ts +112 -8
  250. package/src/audit/fetch.ts +53 -8
  251. package/src/audit/proof.ts +4 -2
  252. package/src/audit/report-html.test.ts +14 -7
  253. package/src/audit/report-model.ts +20 -9
  254. package/src/audit/report.test.ts +36 -13
  255. package/src/audit/report.ts +2 -0
  256. package/src/audit/rules-doc.test.ts +8 -6
  257. package/src/audit/rules-doc.ts +13 -5
  258. package/src/cli/commands/audit.ts +23 -18
  259. package/src/cli/commands/component-lint.test.ts +158 -0
  260. package/src/cli/commands/doctor.test.ts +116 -2
  261. package/src/cli/commands/doctor.ts +46 -0
  262. package/src/cli/commands/lexicon-rolling-upgrade.ts +198 -0
  263. package/src/cli/commands/lexicon-surface-diff.test.ts +104 -0
  264. package/src/cli/commands/lexicon-surface-diff.ts +185 -0
  265. package/src/cli/commands/lint.ts +142 -1
  266. package/src/cli/commands/pinned-upgrade.ts +204 -0
  267. package/src/cli/handlers/build.ts +58 -1
  268. package/src/cli/handlers/components.test.ts +504 -0
  269. package/src/cli/handlers/components.ts +404 -0
  270. package/src/cli/handlers/dev.ts +60 -1
  271. package/src/cli/handlers/graph.ts +47 -2
  272. package/src/cli/handlers/misc.ts +45 -1
  273. package/src/cli/handlers/run.test.ts +967 -3
  274. package/src/cli/handlers/run.ts +618 -11
  275. package/src/cli/main.test.ts +66 -0
  276. package/src/cli/main.ts +105 -2
  277. package/src/cli/registry.ts +35 -0
  278. package/src/codegen/fetch.test.ts +84 -3
  279. package/src/codegen/fetch.ts +76 -2
  280. package/src/codegen/lexicon-regen.test.ts +298 -0
  281. package/src/codegen/lexicon-regen.ts +545 -0
  282. package/src/codegen/pinned-upgrade.test.ts +399 -0
  283. package/src/codegen/pinned-upgrade.ts +423 -0
  284. package/src/codegen/rolling-upgrade.test.ts +333 -0
  285. package/src/codegen/rolling-upgrade.ts +320 -0
  286. package/src/codegen/surface-snapshot.test.ts +461 -0
  287. package/src/codegen/surface-snapshot.ts +546 -0
  288. package/src/codegen/version-bump.test.ts +47 -0
  289. package/src/codegen/version-bump.ts +39 -0
  290. package/src/components/README.md +130 -0
  291. package/src/components/SPRAWL-VALIDATION.md +181 -0
  292. package/src/components/__fixtures__/alb-ecs-service.json +51 -0
  293. package/src/components/__fixtures__/dynamodb-infra.json +32 -0
  294. package/src/components/__fixtures__/emr-job-consumer.json +26 -0
  295. package/src/components/__fixtures__/jar-lib-producer.json +16 -0
  296. package/src/components/__fixtures__/lambda-image-processor.json +28 -0
  297. package/src/components/__fixtures__/neo4j-fanout.json +34 -0
  298. package/src/components/__fixtures__/single-host-compose.json +29 -0
  299. package/src/components/auto-release.test.ts +217 -0
  300. package/src/components/auto-release.ts +216 -0
  301. package/src/components/builders.test.ts +45 -0
  302. package/src/components/builders.ts +71 -0
  303. package/src/components/capability-plugin-loader.ts +218 -0
  304. package/src/components/capability-plugin-schema.ts +56 -0
  305. package/src/components/capability-plugin.test.ts +235 -0
  306. package/src/components/capability-plugin.ts +108 -0
  307. package/src/components/capability.ts +117 -0
  308. package/src/components/cli-support.test.ts +579 -0
  309. package/src/components/cli-support.ts +481 -0
  310. package/src/components/component-schema.test.ts +265 -0
  311. package/src/components/component.schema.json +235 -0
  312. package/src/components/component.test.ts +251 -0
  313. package/src/components/component.ts +255 -0
  314. package/src/components/config-defaults.test.ts +263 -0
  315. package/src/components/config-defaults.ts +145 -0
  316. package/src/components/discover.test.ts +352 -0
  317. package/src/components/discover.ts +167 -0
  318. package/src/components/driver-output.ts +71 -0
  319. package/src/components/driver.test.ts +638 -0
  320. package/src/components/driver.ts +690 -0
  321. package/src/components/index.ts +113 -0
  322. package/src/components/manifest-persistence.test.ts +152 -0
  323. package/src/components/manifest-persistence.ts +164 -0
  324. package/src/components/pilots/README.md +33 -0
  325. package/src/components/pilots/alb-ecs.pilot.ts +63 -0
  326. package/src/components/pilots/dynamodb.pilot.ts +59 -0
  327. package/src/components/pilots/jar-emr.pilot.ts +69 -0
  328. package/src/components/pilots/lambda.pilot.ts +55 -0
  329. package/src/components/pilots/neo4j-fanout.pilot.ts +92 -0
  330. package/src/components/pilots/pilots-lint-clean.test.ts +78 -0
  331. package/src/components/pilots/pilots.test.ts +126 -0
  332. package/src/components/pilots/project.ts +13 -0
  333. package/src/components/presets/comp007-reduction.test.ts +122 -0
  334. package/src/components/presets/ecs-fargate.test.ts +84 -0
  335. package/src/components/presets/ecs-fargate.ts +103 -0
  336. package/src/components/presets/index.ts +32 -0
  337. package/src/components/presets/lambda.test.ts +35 -0
  338. package/src/components/presets/lambda.ts +53 -0
  339. package/src/components/presets/single-host-compose.test.ts +65 -0
  340. package/src/components/presets/single-host-compose.ts +64 -0
  341. package/src/components/registry.test.ts +112 -0
  342. package/src/components/registry.ts +37 -0
  343. package/src/components/starter-plugin.ts +105 -0
  344. package/src/components/temporal-codegen-loader.ts +41 -0
  345. package/src/components/verbs/__fixtures__/package-lock.json +45 -0
  346. package/src/components/verbs/__fixtures__/pom.xml +37 -0
  347. package/src/components/verbs/__fixtures__/schemas/cyclonedx-1.5.schema.json +3800 -0
  348. package/src/components/verbs/__fixtures__/schemas/cyclonedx-spdx-license.schema.json +621 -0
  349. package/src/components/verbs/__fixtures__/schemas/jsf-0.82.schema.json +240 -0
  350. package/src/components/verbs/__fixtures__/schemas/spdx-2.3.schema.json +740 -0
  351. package/src/components/verbs/__tests__/mock-cloud-executor.ts +102 -0
  352. package/src/components/verbs/__tests__/mock-process-runner.ts +79 -0
  353. package/src/components/verbs/__tests__/mock-sbom-generator.ts +85 -0
  354. package/src/components/verbs/bom-writer.test.ts +331 -0
  355. package/src/components/verbs/bom-writer.ts +386 -0
  356. package/src/components/verbs/build-archive.test.ts +251 -0
  357. package/src/components/verbs/build-archive.ts +296 -0
  358. package/src/components/verbs/build.test.ts +222 -0
  359. package/src/components/verbs/build.ts +252 -0
  360. package/src/components/verbs/cloud-executor.ts +181 -0
  361. package/src/components/verbs/component-bom.test.ts +219 -0
  362. package/src/components/verbs/component-bom.ts +189 -0
  363. package/src/components/verbs/index.ts +49 -0
  364. package/src/components/verbs/license-policy.ts +115 -0
  365. package/src/components/verbs/lockfile-sbom-generator.test.ts +195 -0
  366. package/src/components/verbs/lockfile-sbom-generator.ts +236 -0
  367. package/src/components/verbs/process-runner.test.ts +79 -0
  368. package/src/components/verbs/process-runner.ts +113 -0
  369. package/src/components/verbs/reproducibility.test.ts +24 -0
  370. package/src/components/verbs/reproducibility.ts +100 -0
  371. package/src/components/verbs/sbom-generator.test.ts +96 -0
  372. package/src/components/verbs/sbom-generator.ts +209 -0
  373. package/src/components/verbs/sbom.test.ts +184 -0
  374. package/src/components/verbs/sbom.ts +152 -0
  375. package/src/components/verbs/shell.test.ts +36 -0
  376. package/src/components/verbs/shell.ts +51 -0
  377. package/src/components/verbs/sign.test.ts +210 -0
  378. package/src/components/verbs/sign.ts +350 -0
  379. package/src/components/verbs/stub.ts +34 -0
  380. package/src/components/verbs/tool-sbom-generator.test.ts +194 -0
  381. package/src/components/verbs/tool-sbom-generator.ts +192 -0
  382. package/src/components/verbs/verify.test.ts +159 -0
  383. package/src/components/verbs/verify.ts +179 -0
  384. package/src/components/verbs/vex.ts +182 -0
  385. package/src/components/verbs/vuln-gate.test.ts +201 -0
  386. package/src/components/verbs/vuln-gate.ts +170 -0
  387. package/src/components/verbs/vuln-scan.test.ts +145 -0
  388. package/src/components/verbs/vuln-scan.ts +266 -0
  389. package/src/components/verbs/wait-verify.test.ts +111 -0
  390. package/src/components/verbs/wait-verify.ts +171 -0
  391. package/src/config.test.ts +74 -1
  392. package/src/config.ts +219 -0
  393. package/src/index.ts +32 -0
  394. package/src/lexicon.ts +88 -0
  395. package/src/lifecycle/build-ledger-store.test.ts +217 -0
  396. package/src/lifecycle/build-ledger-store.ts +165 -0
  397. package/src/lifecycle/build-ledger.test.ts +349 -0
  398. package/src/lifecycle/build-ledger.ts +305 -0
  399. package/src/lifecycle/git.test.ts +118 -0
  400. package/src/lifecycle/git.ts +149 -14
  401. package/src/lifecycle/index.ts +5 -0
  402. package/src/lifecycle/oras-referrer-lookup.test.ts +116 -0
  403. package/src/lifecycle/oras-referrer-lookup.ts +110 -0
  404. package/src/lifecycle/release-ledger.test.ts +209 -0
  405. package/src/lifecycle/release-ledger.ts +185 -0
  406. package/src/lifecycle/status.test.ts +330 -0
  407. package/src/lifecycle/status.ts +278 -0
  408. package/src/lint/component-checks.ts +148 -0
  409. package/src/lint/config.test.ts +21 -0
  410. package/src/lint/engine.ts +11 -2
  411. package/src/lint/presets/relaxed.json +8 -1
  412. package/src/lint/presets/strict.json +8 -1
  413. package/src/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.ts +9 -0
  414. package/src/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.ts +9 -0
  415. package/src/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.ts +11 -0
  416. package/src/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.ts +13 -0
  417. package/src/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.ts +11 -0
  418. package/src/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.ts +27 -0
  419. package/src/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.ts +10 -0
  420. package/src/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.ts +23 -0
  421. package/src/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.ts +10 -0
  422. package/src/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.ts +19 -0
  423. package/src/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.ts +10 -0
  424. package/src/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.ts +20 -0
  425. package/src/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.ts +23 -0
  426. package/src/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.ts +10 -0
  427. package/src/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.ts +14 -0
  428. package/src/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.ts +18 -0
  429. package/src/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.ts +10 -0
  430. package/src/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.ts +14 -0
  431. package/src/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.ts +13 -0
  432. package/src/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.ts +13 -0
  433. package/src/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.ts +12 -0
  434. package/src/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.ts +18 -0
  435. package/src/lint/rules/comp/comp.test.ts +378 -0
  436. package/src/lint/rules/comp/comp001-publishes-never-applies.ts +78 -0
  437. package/src/lint/rules/comp/comp002-dangling-wiring-ref.ts +94 -0
  438. package/src/lint/rules/comp/comp003-mutating-no-rollback.ts +115 -0
  439. package/src/lint/rules/comp/comp004-gate-needs-temporal.ts +66 -0
  440. package/src/lint/rules/comp/comp005-capability-kind-is-noun.ts +113 -0
  441. package/src/lint/rules/comp/comp006-shell-needs-reason.ts +55 -0
  442. package/src/lint/rules/comp/comp007-composition-sprawl.ts +71 -0
  443. package/src/lint/rules/comp/index.ts +37 -0
  444. package/src/lint/rules/comp/support.ts +173 -0
  445. package/src/lsp/lexicon-providers.test.ts +55 -0
  446. package/src/lsp/lexicon-providers.ts +13 -3
  447. package/src/op/activity-registry.test.ts +38 -0
  448. package/src/op/activity-registry.ts +31 -12
  449. package/src/op/activity-runtime.ts +73 -0
  450. package/src/op/builders.ts +92 -0
  451. package/src/op/index.ts +3 -1
  452. package/src/ownership.test.ts +24 -26
  453. package/src/ownership.ts +26 -38
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Pilot: image-processor Lambda (#558, epic #551).
3
+ *
4
+ * The fourth, deliberately different component #558 asks for, added to prove
5
+ * the sprawl metric holds beyond the original three pilots (#555): a
6
+ * container-image AWS Lambda function. `service` archetype, same as ALB/ECS,
7
+ * but a genuinely different shape on two axes at once:
8
+ *
9
+ * - **apply target**: no CloudFormation stack at all — `lambda-deploy`
10
+ * updates a function's code directly (`UpdateFunctionCode` +
11
+ * `PublishVersion` + `UpdateAlias`), never a `cfn-deploy` changeset. Every
12
+ * prior pilot's apply step was `cfn-deploy`; this is the first pilot whose
13
+ * Apply phase contains none.
14
+ * - **verify shape**: a single `wait-for-stack`-free health check
15
+ * (`health-gate` against the function's alias URL) rather than an ECS
16
+ * steady-state poll or a Neo4j quorum probe.
17
+ *
18
+ * Reuses `docker-build` + `publish-image` unchanged from the ALB/ECS pilot's
19
+ * axis (build + promote-by-digest is identical for a container-image Lambda:
20
+ * https://docs.aws.amazon.com/lambda/latest/dg/images-create.html) — the
21
+ * `imageRef`/`codeRef` wiring (`@Publish.digest` / `@Publish.uri`) is the same
22
+ * `@Phase.field` prior-step reference form every pilot already uses. The only
23
+ * capability this component needed that didn't already exist is `lambda-deploy`
24
+ * (../verbs/apply.ts) — one new capability, exactly the sprawl metric's bound
25
+ * for "genuinely novel"; see ../SPRAWL-VALIDATION.md for the full accounting.
26
+ *
27
+ * The JSON projection of this pilot is authoritative at
28
+ * ../__fixtures__/lambda-image-processor.json — this module is the real
29
+ * typed `Component` authoring form (#560, ../component.ts) that composes to
30
+ * that same document; see ./pilots.test.ts, which asserts the two never
31
+ * diverge.
32
+ */
33
+
34
+ import type { Component } from "../component";
35
+ import { phase } from "../component";
36
+
37
+ export const imageProcessor: Component = {
38
+ name: "image-processor-lambda",
39
+ archetype: "service",
40
+ dependsOn: [],
41
+ build: { kind: "docker-build", context: ".", into: "archive" },
42
+ deploy: [
43
+ phase("Publish", [{ kind: "publish-image", from: "archive", to: "$env.registry" }]),
44
+ phase("Apply", [
45
+ { kind: "lambda-deploy", functionName: "image-processor", codeRef: "@Publish.uri", alias: "live" },
46
+ ]),
47
+ phase("Verify", [{ kind: "health-gate", path: "@Apply.functionArn" }]),
48
+ ],
49
+ // No native rollback on lambda-deploy for an alias already repointed at a bad
50
+ // version — same "no automatic capability rollback" story as the ALB/ECS
51
+ // pilot's ecs-update-service, except here the capability itself supplies the
52
+ // best-effort restore (see createLambdaDeployCapability's rollback in
53
+ // ../verbs/apply.ts) rather than the component declaring its own phase, so
54
+ // no component-level `rollback` field is needed here.
55
+ };
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Pilot: Neo4j per-instance fan-out cluster (#555, epic #551).
3
+ *
4
+ * Exercises the fan-out axis: one component composing N per-instance
5
+ * mini-compositions (`cfn-deploy` + `code-deploy` + a health wait), seeded
6
+ * first, then rolled through the remaining instances one at a time behind a
7
+ * `wait-cluster-healthy` gate. See composition-and-wiring.mdx#fan-out-is-composition-not-orchestrator-knowledge.
8
+ *
9
+ * The JSON projection of this pilot is authoritative at
10
+ * ../__fixtures__/neo4j-fanout.json (already schema-validated by
11
+ * component-schema.test.ts) — this module is the real typed `Component`
12
+ * authoring form (#560, ../component.ts) that composes to that same
13
+ * document; see ./pilots.test.ts, which asserts the two never diverge.
14
+ */
15
+
16
+ import type { Component, Phase } from "../component";
17
+ import { gate, phase } from "../component";
18
+
19
+ /** One Neo4j instance's mini-composition: provision, code-deploy the build, then wait for cluster health. */
20
+ interface Neo4jInstanceConfig {
21
+ /** Cluster-relative instance index (0 = seed). */
22
+ index: number;
23
+ /** Phase display name. */
24
+ label: string;
25
+ /** Availability-zone-qualified CloudFormation template for this instance. */
26
+ template: string;
27
+ /**
28
+ * Health condition to satisfy before moving to the next instance. The seed
29
+ * instance only needs itself up (`size: 1`); every subsequent instance
30
+ * requires quorum across whatever is live so far.
31
+ */
32
+ health: { size: number } | { quorum: true };
33
+ /** Optional human gate placed before this instance's steps (rolling instances only, never the seed). */
34
+ approval?: { signalName: string; description: string; timeout: string };
35
+ }
36
+
37
+ const SEED: Neo4jInstanceConfig = {
38
+ index: 0,
39
+ label: "Seed",
40
+ template: "archive:neo4j-az0-0.template.json",
41
+ health: { size: 1 },
42
+ };
43
+
44
+ /**
45
+ * Followers roll one at a time after the seed is healthy. Node 1 carries an
46
+ * explicit approval gate — composed the same way any other step is, never as
47
+ * driver knowledge — so a human confirms the seed before the cluster starts
48
+ * fanning out further; later nodes roll unattended once that first rollout is
49
+ * trusted.
50
+ */
51
+ const FOLLOWERS: Neo4jInstanceConfig[] = [
52
+ {
53
+ index: 1,
54
+ label: "Node 1",
55
+ template: "archive:neo4j-1.template.json",
56
+ health: { quorum: true },
57
+ approval: {
58
+ signalName: "approve-neo4j-node-1",
59
+ description: "Confirm the seed node is healthy before rolling to node 1",
60
+ timeout: "24h",
61
+ },
62
+ },
63
+ {
64
+ index: 2,
65
+ label: "Node 2",
66
+ template: "archive:neo4j-2.template.json",
67
+ health: { quorum: true },
68
+ },
69
+ ];
70
+
71
+ /** Build one instance's mini-composition phase — the unit that repeats N times for an N-node cluster. */
72
+ function instancePhase(instance: Neo4jInstanceConfig): Phase {
73
+ const steps = [
74
+ ...(instance.approval ? [gate(instance.approval.signalName, instance.approval)] : []),
75
+ { kind: "cfn-deploy", template: instance.template },
76
+ { kind: "code-deploy", instance: instance.index, revision: "@Seed.templateUri" },
77
+ { kind: "wait-cluster-healthy", ...instance.health },
78
+ ];
79
+ return phase(instance.label, steps);
80
+ }
81
+
82
+ export const neo4jCluster: Component = {
83
+ name: "neo4j-cluster",
84
+ archetype: "infra",
85
+ dependsOn: [],
86
+ deploy: [
87
+ instancePhase(SEED),
88
+ // Array.map over instance configs — the fan-out is authored here, in the
89
+ // composition, never known by the orchestrator (composition-and-wiring.mdx).
90
+ ...FOLLOWERS.map(instancePhase),
91
+ ],
92
+ };
@@ -0,0 +1,78 @@
1
+ /**
2
+ * The real pilots must lint clean under the COMP* composition rules (#562,
3
+ * epic #551's acceptance criteria: "Ensure the four real pilots lint CLEAN
4
+ * (they are valid compositions)"). #561 added a fifth/sixth component (the
5
+ * jar-lib producer / emr-job consumer pair) after this issue was filed;
6
+ * included here too since it now lives alongside the original four as real,
7
+ * non-stub composition.
8
+ *
9
+ * Pilots are authored as `*.pilot.ts` (see ../discover.ts's docstring: only
10
+ * `*.component.ts` is the discovery convention `chant lint`/`chant build`
11
+ * actually scan), so this test builds a `ComponentCheckContext` directly from
12
+ * the imported `Component` values — the same objects `./pilots.test.ts`
13
+ * schema-validates — rather than depending on file-suffix discovery, and runs
14
+ * every COMP* check against it exactly as `../../lint/component-checks.ts`'s
15
+ * `runComponentChecks` would over a real discovered project.
16
+ */
17
+
18
+ import { describe, expect, it } from "vitest";
19
+ import type { ComponentCheckContext, ComponentCheckEntry } from "../../lint/component-checks";
20
+ import { loadComponentChecks } from "../../lint/rules/comp/index";
21
+ import { searchService } from "./alb-ecs.pilot";
22
+ import { ordersTable } from "./dynamodb.pilot";
23
+ import { neo4jCluster } from "./neo4j-fanout.pilot";
24
+ import { imageProcessor } from "./lambda.pilot";
25
+ import { jarLib, emrJob } from "./jar-emr.pilot";
26
+ import type { Component } from "../component";
27
+
28
+ const checks = loadComponentChecks();
29
+
30
+ function contextFor(components: Component[]): ComponentCheckContext {
31
+ const map = new Map<string, ComponentCheckEntry>();
32
+ for (const component of components) {
33
+ map.set(component.name, { component, filePath: `${component.name}.pilot.ts` });
34
+ }
35
+ return { components: map };
36
+ }
37
+
38
+ /** Every check's diagnostics against one context, tagged with the producing check id. */
39
+ function runAll(ctx: ComponentCheckContext) {
40
+ return checks.flatMap((check) => check.check(ctx));
41
+ }
42
+
43
+ describe("Real pilots lint clean under COMP* (#562 acceptance criteria)", () => {
44
+ it("ALB/ECS, DynamoDB, and the jar-lib/emr-job pair produce zero COMP* diagnostics", () => {
45
+ const ctx = contextFor([searchService, ordersTable, jarLib, emrJob]);
46
+ const diagnostics = runAll(ctx);
47
+ expect(diagnostics).toEqual([]);
48
+ });
49
+
50
+ it("the image-processor Lambda pilot (#558's fourth component) produces zero COMP* diagnostics", () => {
51
+ const ctx = contextFor([imageProcessor]);
52
+ expect(runAll(ctx)).toEqual([]);
53
+ });
54
+
55
+ it("the Neo4j fan-out pilot produces zero COMP* diagnostics other than the expected COMP004 (gate requires Temporal)", () => {
56
+ // The Neo4j pilot's Node-1 approval gate is a deliberate design choice
57
+ // (see neo4j-fanout.pilot.ts's own docstring and
58
+ // ../driver.ts's DriverGateUnsupportedError / docs/components/
59
+ // orchestration.mdx's "Temporal is optional" aside) — it is the one real
60
+ // pilot that genuinely needs the durable backend, so COMP004 firing here
61
+ // is the *correct*, expected signal, not a defect. A real project would
62
+ // acknowledge it with `// chant-disable-next-line COMP004 -- <reason>`
63
+ // once authored as an actual `*.component.ts` file (see
64
+ // ../../lint/rules/comp/comp004-gate-needs-temporal.ts and the
65
+ // comp004/pass fixture demonstrating that opt-out end to end).
66
+ const ctx = contextFor([neo4jCluster]);
67
+ const diagnostics = runAll(ctx);
68
+ expect(diagnostics.every((d) => d.checkId === "COMP004")).toBe(true);
69
+ expect(diagnostics.filter((d) => d.checkId === "COMP004")).toHaveLength(1);
70
+ });
71
+
72
+ it("all six real components together (the full pilot set) produce zero COMP* diagnostics except the one documented Neo4j gate", () => {
73
+ const ctx = contextFor([searchService, ordersTable, neo4jCluster, imageProcessor, jarLib, emrJob]);
74
+ const diagnostics = runAll(ctx);
75
+ const unexpected = diagnostics.filter((d) => !(d.checkId === "COMP004" && d.component === "neo4j-cluster"));
76
+ expect(unexpected).toEqual([]);
77
+ });
78
+ });
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Validates the pilot components (#555, #558, epic #551) two ways:
3
+ *
4
+ * 1. Each pilot's TypeScript authoring form projects to JSON that validates
5
+ * against component.schema.json (draft 2020-12, via the same ajv 2020
6
+ * setup as ../component-schema.test.ts).
7
+ * 2. That projection is byte-identical (as data) to the corresponding
8
+ * existing fixture under ../__fixtures__/ — so the fixture stays the one
9
+ * authoritative JSON projection per pilot rather than a second, divergent
10
+ * copy. If a pilot's composition changes, the fixture is the file to
11
+ * update; this test catches the two drifting apart.
12
+ *
13
+ * Includes the original three pilots (#555 — Neo4j fan-out, DynamoDB sticky
14
+ * apply, ALB/ECS cross-stack) plus the fourth validation component #558 added
15
+ * (image-processor-lambda) to prove the sprawl metric holds beyond the
16
+ * original three; see ../SPRAWL-VALIDATION.md. Also includes the #561
17
+ * JAR-producer / EMR-consumer pair (../pilots/jar-emr.pilot.ts) — the epic's
18
+ * worked cross-component artifact output example.
19
+ */
20
+
21
+ import { readFileSync } from "node:fs";
22
+ import { join } from "node:path";
23
+ import { describe, expect, it } from "vitest";
24
+ import Ajv2020 from "ajv/dist/2020";
25
+ import componentSchema from "../component.schema.json";
26
+ import { neo4jCluster } from "./neo4j-fanout.pilot";
27
+ import { ordersTable } from "./dynamodb.pilot";
28
+ import { searchService } from "./alb-ecs.pilot";
29
+ import { imageProcessor } from "./lambda.pilot";
30
+ import { jarLib, emrJob } from "./jar-emr.pilot";
31
+ import { projectToJson } from "./project";
32
+ import type { Component } from "../component";
33
+
34
+ const FIXTURES_DIR = join(import.meta.dirname, "..", "__fixtures__");
35
+
36
+ function loadFixture(name: string): unknown {
37
+ return JSON.parse(readFileSync(join(FIXTURES_DIR, name), "utf-8"));
38
+ }
39
+
40
+ const ajv = new Ajv2020({ strict: true, allErrors: true });
41
+ const validate = ajv.compile(componentSchema);
42
+
43
+ const pilots: Array<{ name: string; component: Component; fixture: string }> = [
44
+ { name: "neo4j per-instance fan-out", component: neo4jCluster, fixture: "neo4j-fanout.json" },
45
+ { name: "DynamoDB sticky apply", component: ordersTable, fixture: "dynamodb-infra.json" },
46
+ { name: "ALB/ECS target", component: searchService, fixture: "alb-ecs-service.json" },
47
+ { name: "image-processor Lambda (#558 fourth component)", component: imageProcessor, fixture: "lambda-image-processor.json" },
48
+ { name: "jar-lib producer (#561 cross-component pair)", component: jarLib, fixture: "jar-lib-producer.json" },
49
+ { name: "emr-job consumer (#561 cross-component pair)", component: emrJob, fixture: "emr-job-consumer.json" },
50
+ ];
51
+
52
+ describe("Pilot component definitions", () => {
53
+ it.each(pilots.map((p) => [p.name, p] as const))(
54
+ "%s: TypeScript authoring form projects to valid JSON",
55
+ (_name, pilot) => {
56
+ const projected = projectToJson(pilot.component);
57
+ const valid = validate(projected);
58
+ if (!valid) {
59
+ throw new Error(`${pilot.name} failed schema validation: ${ajv.errorsText(validate.errors)}`);
60
+ }
61
+ expect(valid).toBe(true);
62
+ },
63
+ );
64
+
65
+ it.each(pilots.map((p) => [p.name, p] as const))(
66
+ "%s: projection matches the authoritative __fixtures__ JSON (no divergent copy)",
67
+ (_name, pilot) => {
68
+ const projected = projectToJson(pilot.component);
69
+ const fixture = loadFixture(pilot.fixture);
70
+ // The fixture carries $schema/contractVersion metadata the illustrative
71
+ // authoring form doesn't reproduce (those are document-level
72
+ // provenance fields, not composition content); strip them from the
73
+ // fixture side before comparing so this asserts the two never diverge
74
+ // on the parts that matter — name/archetype/dependsOn/build/deploy/rollback.
75
+ const { $schema: _s, contractVersion: _v, ...fixtureContent } = fixture as Record<string, unknown>;
76
+ expect(projected).toEqual(fixtureContent);
77
+ },
78
+ );
79
+
80
+ it("each pilot exercises its intended epic axis (build presence / archetype / fan-out shape)", () => {
81
+ // Neo4j: fan-out — more than one instance phase, nested no build (infra).
82
+ expect(neo4jCluster.archetype).toBe("infra");
83
+ expect(neo4jCluster.build).toBeUndefined();
84
+ expect(neo4jCluster.deploy.length).toBeGreaterThan(1);
85
+
86
+ // DynamoDB: sticky apply — no build, cfn-deploy carries onReplace/stageGsi.
87
+ expect(ordersTable.archetype).toBe("infra");
88
+ expect(ordersTable.build).toBeUndefined();
89
+ const applyStep = ordersTable.deploy[0]!.steps[0] as { onReplace?: string; stageGsi?: boolean };
90
+ expect(applyStep.onReplace).toBe("block");
91
+ expect(applyStep.stageGsi).toBe(true);
92
+
93
+ // ALB/ECS: build present, cross-stack wiring present, service archetype.
94
+ expect(searchService.archetype).toBe("service");
95
+ expect(searchService.build).toBeDefined();
96
+ expect(searchService.dependsOn).toContain("shared-alb");
97
+ const applyPhase = searchService.deploy.find((p) => p.phase === "Apply")!;
98
+ const cfnStep = applyPhase.steps[0] as { inputs?: Record<string, unknown> };
99
+ expect(cfnStep.inputs?.listenerArn).toEqual({ stackOutput: { stack: "shared-alb", name: "ListenerArn" } });
100
+ });
101
+
102
+ it("image-processor Lambda (#558): build present, service archetype, apply has no cfn-deploy at all — the fourth component's distinguishing shape", () => {
103
+ expect(imageProcessor.archetype).toBe("service");
104
+ expect(imageProcessor.build).toBeDefined();
105
+ const applyPhase = imageProcessor.deploy.find((p) => p.phase === "Apply")!;
106
+ expect(applyPhase.steps.map((s) => (s as { kind: string }).kind)).toEqual(["lambda-deploy"]);
107
+ expect(applyPhase.steps.some((s) => (s as { kind: string }).kind === "cfn-deploy")).toBe(false);
108
+ });
109
+
110
+ it("jar-lib (#561): producer-library archetype, publish-only deploy — build -> publish, no apply phase at all", () => {
111
+ expect(jarLib.archetype).toBe("producer-library");
112
+ expect(jarLib.build).toBeDefined();
113
+ expect(jarLib.deploy).toHaveLength(1);
114
+ expect(jarLib.deploy[0]!.phase).toBe("Publish");
115
+ expect(jarLib.deploy[0]!.steps.map((s) => (s as { kind: string }).kind)).toEqual(["publish-artifact"]);
116
+ });
117
+
118
+ it("emr-job (#561): infra archetype, depends on jar-lib, and references the producer's published artifact by @<component>.publish.uri — the cross-component wiring this issue adds", () => {
119
+ expect(emrJob.archetype).toBe("infra");
120
+ expect(emrJob.dependsOn).toEqual(["jar-lib"]);
121
+ const submitPhase = emrJob.deploy.find((p) => p.phase === "Submit")!;
122
+ const submitStep = submitPhase.steps[0] as { kind: string; jar: string };
123
+ expect(submitStep.kind).toBe("emr-start-job-run");
124
+ expect(submitStep.jar).toBe("@jar-lib.publish.uri");
125
+ });
126
+ });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Re-exports the real `projectToJson` (#560, ../component.ts) under the
3
+ * pilots' original import path. Prior to #560, this module held its own
4
+ * "intentionally dumb" projection over the stopgap `authoring-shape.ts`
5
+ * (#555); now that the real typed `Component` authoring form exists, its
6
+ * `projectToJson` (which also fills in `archetype` via `inferArchetype` when
7
+ * a pilot doesn't set one explicitly) is the single implementation. Kept as a
8
+ * re-export, rather than inlining `../component`'s import into every pilot
9
+ * test, so `./pilots.test.ts`/`./pilots-e2e.test.ts`/`../driver.test.ts`
10
+ * don't need their import paths touched by this migration.
11
+ */
12
+
13
+ export { projectToJson } from "../component";
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Presets reduce identical-composition duplication (#566's third acceptance
3
+ * criterion, tied to the COMP007 copy-paste hint per epic #551's failure-mode
4
+ * guardrail #3: "Composition copy-paste (declaration sprawl) ... Guard:
5
+ * presets (Level 2) + a lint hint when an identical composition repeats
6
+ * across components").
7
+ *
8
+ * This demonstrates the concrete before/after COMP007 exists for:
9
+ * - "before" — two components hand-authored with an identical phase/step-kind
10
+ * shape (the same scenario ../../lint/rules/comp/__fixtures__/comp007/fail/
11
+ * covers) trigger COMP007's declaration-sprawl warning, and require
12
+ * hand-copying the full phase/step composition a second time.
13
+ * - "after" — expressing both as two `EcsFargateComponent({...})` calls
14
+ * collapses each declaration to its own params (name/service/healthPath/
15
+ * stack), with the shared composition shape lifted into the preset once.
16
+ * COMP007 still fires on the *expanded* shape (its fingerprint is
17
+ * structural, over the projected composition, by design — see
18
+ * ../../lint/rules/comp/comp007-composition-sprawl.ts's docstring: "own
19
+ * destiny is in the params, not the shape" is exactly the case a preset
20
+ * should absorb) — the reduction is in what the *author* had to write and
21
+ * maintain, not in whether the lint hint still recognizes the shared shape.
22
+ */
23
+
24
+ import { describe, expect, it } from "vitest";
25
+ import type { Component } from "../component";
26
+ import { phase } from "../component";
27
+ import { loadComponentChecks } from "../../lint/rules/comp/index";
28
+ import type { ComponentCheckContext, ComponentCheckEntry } from "../../lint/component-checks";
29
+ import { EcsFargateComponent } from "./ecs-fargate";
30
+
31
+ const checks = loadComponentChecks();
32
+
33
+ function contextFor(components: Component[]): ComponentCheckContext {
34
+ const map = new Map<string, ComponentCheckEntry>();
35
+ for (const component of components) {
36
+ map.set(component.name, { component, filePath: `${component.name}.component.ts` });
37
+ }
38
+ return { components: map };
39
+ }
40
+
41
+ function comp007Diagnostics(components: Component[]) {
42
+ const ctx = contextFor(components);
43
+ return checks.flatMap((check) => check.check(ctx)).filter((d) => d.checkId === "COMP007");
44
+ }
45
+
46
+ describe("Presets reduce composition copy-paste (#566, tied to COMP007)", () => {
47
+ it("before: two hand-authored components with an identical build->publish->apply->verify shape trigger COMP007", () => {
48
+ // Two unrelated services, each requiring the full phase/step composition
49
+ // to be hand-typed and kept in sync by hand — exactly the declaration
50
+ // sprawl epic #551's failure mode #3 describes.
51
+ const catalogService: Component = {
52
+ name: "catalog-service",
53
+ archetype: "service",
54
+ dependsOn: ["shared-alb"],
55
+ build: { kind: "docker-build", context: ".", into: "archive" },
56
+ deploy: [
57
+ phase("Publish", [{ kind: "publish-image", from: "archive", to: "$env.registry" }]),
58
+ phase("Apply", [
59
+ { kind: "cfn-deploy", template: "archive:catalog.template.json", imageRef: "@Publish.digest" },
60
+ { kind: "ecs-update-service", cluster: "$env.cluster", service: "catalog" },
61
+ ]),
62
+ phase("Verify", [
63
+ { kind: "wait-steady-state", service: "catalog" },
64
+ { kind: "health-gate", path: "/healthz" },
65
+ ]),
66
+ ],
67
+ };
68
+ const billingService: Component = {
69
+ name: "billing-service",
70
+ archetype: "service",
71
+ dependsOn: ["shared-alb"],
72
+ build: { kind: "docker-build", context: ".", into: "archive" },
73
+ deploy: [
74
+ phase("Publish", [{ kind: "publish-image", from: "archive", to: "$env.registry" }]),
75
+ phase("Apply", [
76
+ { kind: "cfn-deploy", template: "archive:billing.template.json", imageRef: "@Publish.digest" },
77
+ { kind: "ecs-update-service", cluster: "$env.cluster", service: "billing" },
78
+ ]),
79
+ phase("Verify", [
80
+ { kind: "wait-steady-state", service: "billing" },
81
+ { kind: "health-gate", path: "/healthz" },
82
+ ]),
83
+ ],
84
+ };
85
+
86
+ const diagnostics = comp007Diagnostics([catalogService, billingService]);
87
+ expect(diagnostics).toHaveLength(2);
88
+ expect(diagnostics.map((d) => d.component).sort()).toEqual(["billing-service", "catalog-service"]);
89
+ expect(diagnostics[0]!.message).toContain("consider extracting a shared preset");
90
+ });
91
+
92
+ it("after: the same two services expressed via EcsFargateComponent collapse to one line of params each, instead of a hand-copied phase/step composition", () => {
93
+ const catalogService = EcsFargateComponent({
94
+ name: "catalog-service",
95
+ healthPath: "/healthz",
96
+ sharedAlbStack: "shared-alb",
97
+ });
98
+ const billingService = EcsFargateComponent({
99
+ name: "billing-service",
100
+ healthPath: "/healthz",
101
+ sharedAlbStack: "shared-alb",
102
+ });
103
+
104
+ // The two declarations are now just their own distinguishing params; the
105
+ // shared composition shape is defined exactly once, in ./ecs-fargate.ts,
106
+ // not copy-pasted per component. COMP007's fingerprint is structural
107
+ // (over the *expanded* composition), so it still — correctly — reports
108
+ // that these two share a shape; the point of the preset was never to
109
+ // silence the lint hint, it was to make the shared shape a single
110
+ // maintained definition instead of N hand-copied ones.
111
+ const diagnostics = comp007Diagnostics([catalogService, billingService]);
112
+ expect(diagnostics).toHaveLength(2);
113
+
114
+ // The expanded contract is identical to the hand-authored "before" case
115
+ // above (same phases, same step kinds/order) — the preset changed nothing
116
+ // about the resulting composition, only how many times its shape had to
117
+ // be typed out by hand (once, in the preset, instead of once per component).
118
+ expect(catalogService.deploy.map((p) => ({ phase: p.phase, kinds: p.steps.map((s) => (s as { kind: string }).kind) }))).toEqual(
119
+ billingService.deploy.map((p) => ({ phase: p.phase, kinds: p.steps.map((s) => (s as { kind: string }).kind) })),
120
+ );
121
+ });
122
+ });
@@ -0,0 +1,84 @@
1
+ /**
2
+ * `EcsFargateComponent` (#566): schema validity, and equivalence with the
3
+ * hand-composed `alb-ecs.pilot.ts` reference component — the "preset-based
4
+ * and hand-composed component produce equivalent contracts" acceptance
5
+ * criterion from #566.
6
+ */
7
+
8
+ import { describe, expect, it } from "vitest";
9
+ import Ajv2020 from "ajv/dist/2020";
10
+ import componentSchema from "../component.schema.json";
11
+ import { projectToJson } from "../component";
12
+ import { searchService } from "../pilots/alb-ecs.pilot";
13
+ import { EcsFargateComponent } from "./ecs-fargate";
14
+
15
+ const ajv = new Ajv2020({ strict: true, allErrors: true });
16
+ const validate = ajv.compile(componentSchema);
17
+
18
+ describe("EcsFargateComponent preset", () => {
19
+ it("expands to a component that projects to schema-valid JSON", () => {
20
+ const component = EcsFargateComponent({
21
+ name: "search-service",
22
+ healthPath: "/healthz",
23
+ sharedAlbStack: "shared-alb",
24
+ });
25
+ const projected = projectToJson(component);
26
+ const valid = validate(projected);
27
+ if (!valid) throw new Error(ajv.errorsText(validate.errors));
28
+ expect(valid).toBe(true);
29
+ });
30
+
31
+ it("re-derives alb-ecs.pilot.ts's hand-composed searchService component byte-for-byte (as data)", () => {
32
+ const fromPreset = EcsFargateComponent({
33
+ name: "search-service",
34
+ service: "search",
35
+ // The pilot's template is derived from the service name ("search"), not
36
+ // the component name ("search-service") this preset defaults from —
37
+ // pass it explicitly to match the pre-existing hand-composed pilot exactly.
38
+ template: "archive:search.template.json",
39
+ healthPath: "/healthz",
40
+ sharedAlbStack: "shared-alb",
41
+ });
42
+
43
+ // Same contract, whether authored via the preset or hand-composed from
44
+ // raw capabilities — the preset is Level 2 convenience over the same
45
+ // Level 1 primitives, never a different orchestrator behavior.
46
+ expect(projectToJson(fromPreset)).toEqual(projectToJson(searchService));
47
+ });
48
+
49
+ it("supports dropping to a customized shape by spreading the preset's output — the 'starts from it and drops to raw capabilities where special' path #566 requires", () => {
50
+ const base = EcsFargateComponent({
51
+ name: "search-service-canary",
52
+ healthPath: "/healthz",
53
+ sharedAlbStack: "shared-alb",
54
+ });
55
+
56
+ // A component that is 90% the standard shape but needs one extra Verify
57
+ // step (e.g. a canary-specific smoke test) starts from the preset and
58
+ // edits the composition directly, rather than the preset needing a new
59
+ // config knob for every possible variation.
60
+ const customized = {
61
+ ...base,
62
+ deploy: base.deploy.map((p) =>
63
+ p.phase === "Verify" ? { ...p, steps: [...p.steps, { kind: "shell", command: "./smoke-test.sh" }] } : p,
64
+ ),
65
+ };
66
+
67
+ const projected = projectToJson(customized);
68
+ const valid = validate(projected);
69
+ if (!valid) throw new Error(ajv.errorsText(validate.errors));
70
+ expect(valid).toBe(true);
71
+ const verifyPhase = (projected as { deploy: Array<{ phase: string; steps: Array<{ kind: string }> }> }).deploy.find(
72
+ (p) => p.phase === "Verify",
73
+ )!;
74
+ expect(verifyPhase.steps.map((s) => s.kind)).toEqual(["wait-steady-state", "health-gate", "shell"]);
75
+ });
76
+
77
+ it("omits shared-ALB cross-stack wiring and dependsOn when sharedAlbStack is not given", () => {
78
+ const standalone = EcsFargateComponent({ name: "standalone-service", healthPath: "/healthz" });
79
+ expect(standalone.dependsOn).toEqual([]);
80
+ const applyPhase = standalone.deploy.find((p) => p.phase === "Apply")!;
81
+ const cfnStep = applyPhase.steps[0] as { inputs?: unknown };
82
+ expect(cfnStep.inputs).toBeUndefined();
83
+ });
84
+ });