@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,66 @@
1
+ /**
2
+ * COMP004: gate-needs-temporal
3
+ *
4
+ * Flags a `gate` step anywhere in a component's composition — a durable,
5
+ * human-approval wait that cannot run on the local in-process executor (see
6
+ * ../../../components/driver.ts's `DriverGateUnsupportedError` and
7
+ * docs/components/orchestration.mdx's "Temporal is optional" aside: "gates ...
8
+ * require the durable (Temporal) execution backend").
9
+ *
10
+ * The component contract has no per-component backend/executor field (the
11
+ * backend is chosen per *run*, via `chant run --temporal`, per epic #551 §8 —
12
+ * intentionally not part of the declaration, so the same component graduates
13
+ * to Temporal without changing its definition). That means lint cannot tell,
14
+ * from the declaration alone, whether a given `gate` will actually run under
15
+ * Temporal when this component is deployed — every `gate` is a standing
16
+ * "this component needs the durable backend" fact worth surfacing, not a
17
+ * mistake to silently allow. Acknowledge it explicitly with a **file-level**
18
+ * disable directive once the durable backend is genuinely intended (see
19
+ * lint-rules/disable-directives.mdx) — a COMP* diagnostic carries no real
20
+ * line/column (it is reported for the whole component; see
21
+ * ../../component-checks.ts), so only the file-level `chant-disable` form is
22
+ * honored for these rules — `-line`/`-next-line` do not suppress a COMP*
23
+ * diagnostic (see ../../../cli/commands/lint.ts's
24
+ * `runComponentCheckDiagnostics`):
25
+ *
26
+ * ```ts
27
+ * // chant-disable COMP004 -- graduates to --temporal for the human approval wait
28
+ * ```
29
+ *
30
+ * Triggers on: any `gate` step in `deploy`/`rollback` with no matching
31
+ * file-level disable directive.
32
+ * OK: a component with no `gate` steps at all, or a `gate` whose file-level
33
+ * disable directive documents the Temporal graduation.
34
+ */
35
+
36
+ import type { ComponentCheck, ComponentCheckContext, ComponentCheckDiagnostic } from "../../component-checks";
37
+ import { walkComponent } from "./support";
38
+
39
+ export const comp004GateNeedsTemporalRule: ComponentCheck = {
40
+ id: "COMP004",
41
+ severity: "error",
42
+ category: "correctness",
43
+ description: "A gate step requires the durable (Temporal) execution backend — it cannot run on the local executor",
44
+ check(ctx: ComponentCheckContext): ComponentCheckDiagnostic[] {
45
+ const diagnostics: ComponentCheckDiagnostic[] = [];
46
+
47
+ for (const [name, { component, filePath }] of ctx.components) {
48
+ const { gates } = walkComponent(component);
49
+ for (const { gate, phaseName } of gates) {
50
+ diagnostics.push({
51
+ checkId: "COMP004",
52
+ severity: "error",
53
+ component: name,
54
+ file: filePath,
55
+ message:
56
+ `Component "${name}": gate "${gate.signalName}" (phase "${phaseName}") requires the durable (Temporal) ` +
57
+ `execution backend — it cannot run on the local in-process executor. If this component is meant to run ` +
58
+ `with "chant run --temporal", suppress with a file-level "// chant-disable COMP004 -- <reason>" comment ` +
59
+ `anywhere in this file to document that intent explicitly.`,
60
+ });
61
+ }
62
+ }
63
+
64
+ return diagnostics;
65
+ },
66
+ };
@@ -0,0 +1,113 @@
1
+ /**
2
+ * COMP005: capability-kind-is-noun
3
+ *
4
+ * Flags a step `kind` that names a component rather than an operation — the
5
+ * "capability-per-component (renamed sprawl)" failure mode from epic #551
6
+ * §"Failure modes ... + guardrails": "Tell: capabilities named after nouns."
7
+ * Every starter-set verb is a verb-phrase describing an operation
8
+ * (`cfn-deploy`, `publish-image`, `wait-steady-state`, ...); a capability
9
+ * named after *this* component (or any other discovered component) is
10
+ * exactly the sprawl the capability model exists to remove — see
11
+ * docs/components/capabilities.mdx#the-discipline ("A capability describes
12
+ * an operation, never a component").
13
+ *
14
+ * Detection is structural, not NLP: a step `kind` is flagged when it equals,
15
+ * or is built from, a discovered component's own kebab-case `name` (e.g. a
16
+ * `search-service` component composing a `{ kind: "deploy-search-service" }`
17
+ * or `{ kind: "search-service" }` step) — the concrete, checkable form of
18
+ * "named after a component" the issue asks for, mirroring how
19
+ * ../../../components/capability.ts's own docstring defines the discipline
20
+ * ("never named after the component that happens to use it").
21
+ *
22
+ * Every registered `kind` is excluded from this check unconditionally,
23
+ * regardless of whether its dash-separated segments happen to overlap a
24
+ * component's name. The allowlist comes from `ctx.knownKinds` — the kinds the
25
+ * project's capability registry actually resolves, which `chant lint` builds
26
+ * from the project's config (core's starter set plus the active lexicons'
27
+ * leaves, e.g. `cfn-deploy`/`wait-for-stack` when `lexicons: ["aws"]`). Without
28
+ * this exclusion, a component literally named "stack", "job", "image", or
29
+ * "host" would falsely flag `wait-for-stack`/`emr-start-job-run`/`publish-image`/
30
+ * `load-image-on-host` — a real capability's verb-ness is an established fact of
31
+ * the registry, not something to re-adjudicate by substring match. When no
32
+ * registry was resolved (a direct unit test), it falls back to core's starter
33
+ * set (`CORE_STARTER_KINDS`).
34
+ *
35
+ * Triggers on: `{ kind: "search-service" }` or `{ kind: "deploy-search-service" }`
36
+ * in any component, when "search-service" is a discovered component's name and
37
+ * neither form is a registered verb.
38
+ * OK: `{ kind: "cfn-deploy" }`, `{ kind: "ecs-update-service" }`,
39
+ * `{ kind: "wait-for-stack" }` — verb-named, and/or a registered verb.
40
+ */
41
+
42
+ import { STARTER_VERB_FAMILIES } from "../../../components/starter-plugin";
43
+ import type { ComponentCheck, ComponentCheckContext, ComponentCheckDiagnostic } from "../../component-checks";
44
+ import { walkComponent } from "./support";
45
+
46
+ /**
47
+ * Fallback allowlist when the check runs without a resolved registry (e.g. a
48
+ * direct unit test): core's own starter verbs. The real `chant lint` run passes
49
+ * `ctx.knownKinds` — every kind the project's registry actually resolves,
50
+ * including the active lexicons' leaves (`cfn-deploy`, `wait-for-stack`, …) —
51
+ * so a real project never depends on this fallback for its cloud verbs.
52
+ */
53
+ const CORE_STARTER_KINDS: ReadonlySet<string> = new Set(Object.values(STARTER_VERB_FAMILIES).flat());
54
+
55
+ /** True if `kind` is exactly a component name, or that name embedded with `-`-joined affixes (e.g. "deploy-search-service", "search-service-apply"). */
56
+ function kindNamesComponent(kind: string, componentName: string): boolean {
57
+ if (kind === componentName) return true;
58
+ const parts = kind.split("-");
59
+ const nameParts = componentName.split("-");
60
+ if (nameParts.length === 0) return false;
61
+ for (let i = 0; i + nameParts.length <= parts.length; i++) {
62
+ if (nameParts.every((p, j) => parts[i + j] === p)) return true;
63
+ }
64
+ return false;
65
+ }
66
+
67
+ export const comp005CapabilityKindIsNounRule: ComponentCheck = {
68
+ id: "COMP005",
69
+ severity: "error",
70
+ category: "style",
71
+ description: "A capability kind is named after a component (a noun) rather than an operation (a verb)",
72
+ check(ctx: ComponentCheckContext): ComponentCheckDiagnostic[] {
73
+ const diagnostics: ComponentCheckDiagnostic[] = [];
74
+ const componentNames = [...ctx.components.keys()];
75
+ if (componentNames.length === 0) return diagnostics;
76
+
77
+ // Every known verb — the project's registered kinds when `chant lint`
78
+ // resolved a registry (includes active lexicons' leaves), else core's
79
+ // starter set. A known verb is never a "noun named after the component",
80
+ // regardless of any substring overlap with a component's name.
81
+ const knownKinds = ctx.knownKinds ?? CORE_STARTER_KINDS;
82
+
83
+ for (const [name, { component, filePath }] of ctx.components) {
84
+ const { steps } = walkComponent(component);
85
+ const reported = new Set<string>();
86
+
87
+ for (const { step, phaseName } of steps) {
88
+ if (step.kind === "gate") continue;
89
+ if (knownKinds.has(step.kind)) continue;
90
+ const matchedComponent = componentNames.find((cn) => kindNamesComponent(step.kind, cn));
91
+ if (!matchedComponent) continue;
92
+
93
+ const dedupeKey = `${step.kind}:${phaseName}`;
94
+ if (reported.has(dedupeKey)) continue;
95
+ reported.add(dedupeKey);
96
+
97
+ diagnostics.push({
98
+ checkId: "COMP005",
99
+ severity: "error",
100
+ component: name,
101
+ file: filePath,
102
+ message:
103
+ `Component "${name}": step kind "${step.kind}" (phase "${phaseName}") is named after component ` +
104
+ `"${matchedComponent}" rather than an operation. A capability kind must be a verb ("cfn-deploy", ` +
105
+ `"publish-image", "wait-steady-state", ...), never a noun naming the component that uses it — see ` +
106
+ `docs/components/capabilities.mdx#the-discipline.`,
107
+ });
108
+ }
109
+ }
110
+
111
+ return diagnostics;
112
+ },
113
+ };
@@ -0,0 +1,55 @@
1
+ /**
2
+ * COMP006: shell-needs-reason
3
+ *
4
+ * Flags a `shell` step with no (or empty/blank) `reason` — the escape-hatch
5
+ * discipline from epic #551 §7 and §"What chant adds": "raw `shell` without a
6
+ * declared reason" gets flagged. `shell` is the deliberate escape hatch
7
+ * (../../../components/verbs/shell.ts) for reaching past the capability set;
8
+ * `ShellInput.reason` is already a required field on the typed
9
+ * `createShellCapability` input, but a hand-authored/JSON-only component (the
10
+ * contract's whole point — see component-contract.mdx's "portable, not
11
+ * chant-only" framing) is not type-checked against that interface, so the raw
12
+ * JSON contract's `Step` (open `additionalProperties`) does not itself
13
+ * enforce it. This rule enforces the same discipline at the composition
14
+ * level, for every authoring form.
15
+ *
16
+ * Triggers on: `{ kind: "shell", cmd: "..." }` with no `reason`, or
17
+ * `reason: ""` / `reason: " "`.
18
+ * OK: `{ kind: "shell", cmd: "...", reason: "no capability covers <x> yet" }`.
19
+ */
20
+
21
+ import type { ComponentCheck, ComponentCheckContext, ComponentCheckDiagnostic } from "../../component-checks";
22
+ import { walkComponent } from "./support";
23
+
24
+ export const comp006ShellNeedsReasonRule: ComponentCheck = {
25
+ id: "COMP006",
26
+ severity: "error",
27
+ category: "correctness",
28
+ description: 'A raw "shell" escape-hatch step has no declared reason',
29
+ check(ctx: ComponentCheckContext): ComponentCheckDiagnostic[] {
30
+ const diagnostics: ComponentCheckDiagnostic[] = [];
31
+
32
+ for (const [name, { component, filePath }] of ctx.components) {
33
+ const { steps } = walkComponent(component);
34
+
35
+ for (const { step, phaseName } of steps) {
36
+ if (step.kind !== "shell") continue;
37
+ const reason = (step as { reason?: unknown }).reason;
38
+ const hasReason = typeof reason === "string" && reason.trim().length > 0;
39
+ if (hasReason) continue;
40
+
41
+ diagnostics.push({
42
+ checkId: "COMP006",
43
+ severity: "error",
44
+ component: name,
45
+ file: filePath,
46
+ message:
47
+ `Component "${name}": "shell" step (phase "${phaseName}") is the escape hatch and must declare why no ` +
48
+ `capability covers this case — add a non-empty "reason" property (e.g. "reason: \\"no capability for X yet\\"").`,
49
+ });
50
+ }
51
+ }
52
+
53
+ return diagnostics;
54
+ },
55
+ };
@@ -0,0 +1,71 @@
1
+ /**
2
+ * COMP007: composition-sprawl
3
+ *
4
+ * Flags two or more components whose `deploy` composition is structurally
5
+ * identical (same phases, same `parallel`/nesting shape, same step kinds in
6
+ * the same order) — the "composition copy-paste (declaration sprawl)"
7
+ * failure mode from epic #551 §"Failure modes ... + guardrails": "Guard:
8
+ * presets (Level 2) + a lint hint when an identical composition repeats
9
+ * across components." This is a *hint*, not a hard error (severity:
10
+ * "warning") — the epic frames it as "reach for a preset"
11
+ * (docs/components/capabilities.mdx#presets--reuse-without-a-closed-set),
12
+ * not a correctness defect.
13
+ *
14
+ * The fingerprint (../comp/support.ts's `compositionFingerprint`) compares
15
+ * phase names, `parallel` flags, nesting, and step *kinds* only — never
16
+ * `name`/`dependsOn`/literal wiring values/params, so two components that
17
+ * both do "build -> publish -> apply -> verify" with the same capability
18
+ * kinds but different templates/clusters/images still match (that is exactly
19
+ * the "own destiny is in the params, not the shape" case a preset should
20
+ * absorb).
21
+ *
22
+ * Triggers on: three components (arbitrary example) that all compose
23
+ * `docker-build` -> `publish-image` -> `cfn-deploy` + `ecs-update-service` ->
24
+ * `wait-steady-state` + `health-gate` with no meaningful structural
25
+ * difference.
26
+ * OK: components whose phase/step-kind shape differs (a different archetype,
27
+ * a different apply family, fan-out vs. no fan-out, etc.) — like the four
28
+ * real pilots, which were deliberately chosen to be structurally distinct
29
+ * (see ../../../components/SPRAWL-VALIDATION.md).
30
+ */
31
+
32
+ import type { ComponentCheck, ComponentCheckContext, ComponentCheckDiagnostic } from "../../component-checks";
33
+ import { compositionFingerprint } from "./support";
34
+
35
+ export const comp007CompositionSprawlRule: ComponentCheck = {
36
+ id: "COMP007",
37
+ severity: "warning",
38
+ category: "style",
39
+ description: "An identical composition shape is repeated across components — a declaration-sprawl hint to reach for a preset",
40
+ check(ctx: ComponentCheckContext): ComponentCheckDiagnostic[] {
41
+ const diagnostics: ComponentCheckDiagnostic[] = [];
42
+
43
+ const byFingerprint = new Map<string, string[]>();
44
+ for (const [name, { component }] of ctx.components) {
45
+ const fp = compositionFingerprint(component);
46
+ byFingerprint.set(fp, [...(byFingerprint.get(fp) ?? []), name]);
47
+ }
48
+
49
+ for (const names of byFingerprint.values()) {
50
+ if (names.length < 2) continue;
51
+ const sorted = [...names].sort();
52
+
53
+ for (const name of sorted) {
54
+ const others = sorted.filter((n) => n !== name);
55
+ const { filePath } = ctx.components.get(name)!;
56
+ diagnostics.push({
57
+ checkId: "COMP007",
58
+ severity: "warning",
59
+ component: name,
60
+ file: filePath,
61
+ message:
62
+ `Component "${name}" composes an identical phase/step shape to ${others.map((n) => `"${n}"`).join(", ")} ` +
63
+ `— consider extracting a shared preset (docs/components/capabilities.mdx#presets) instead of repeating ` +
64
+ `the composition per component.`,
65
+ });
66
+ }
67
+ }
68
+
69
+ return diagnostics;
70
+ },
71
+ };
@@ -0,0 +1,37 @@
1
+ /**
2
+ * COMP* composition lint rules (#562, epic #551) — the long-term entropy
3
+ * guards over the discovered `Component` graph. See ../../component-checks.ts
4
+ * for why these are `ComponentCheck`s (whole-project, post-discovery) rather
5
+ * than `LintRule`s (single-file AST), and docs/lint-rules/composition.mdx for
6
+ * the user-facing reference.
7
+ */
8
+
9
+ import type { ComponentCheck } from "../../component-checks";
10
+ import { comp001PublishesNeverAppliesRule } from "./comp001-publishes-never-applies";
11
+ import { comp002DanglingWiringRefRule } from "./comp002-dangling-wiring-ref";
12
+ import { comp003MutatingNoRollbackRule } from "./comp003-mutating-no-rollback";
13
+ import { comp004GateNeedsTemporalRule } from "./comp004-gate-needs-temporal";
14
+ import { comp005CapabilityKindIsNounRule } from "./comp005-capability-kind-is-noun";
15
+ import { comp006ShellNeedsReasonRule } from "./comp006-shell-needs-reason";
16
+ import { comp007CompositionSprawlRule } from "./comp007-composition-sprawl";
17
+
18
+ export { comp001PublishesNeverAppliesRule } from "./comp001-publishes-never-applies";
19
+ export { comp002DanglingWiringRefRule } from "./comp002-dangling-wiring-ref";
20
+ export { comp003MutatingNoRollbackRule } from "./comp003-mutating-no-rollback";
21
+ export { comp004GateNeedsTemporalRule } from "./comp004-gate-needs-temporal";
22
+ export { comp005CapabilityKindIsNounRule } from "./comp005-capability-kind-is-noun";
23
+ export { comp006ShellNeedsReasonRule } from "./comp006-shell-needs-reason";
24
+ export { comp007CompositionSprawlRule } from "./comp007-composition-sprawl";
25
+
26
+ /** All seven COMP* checks, sorted by id. */
27
+ export function loadComponentChecks(): ComponentCheck[] {
28
+ return [
29
+ comp001PublishesNeverAppliesRule,
30
+ comp002DanglingWiringRefRule,
31
+ comp003MutatingNoRollbackRule,
32
+ comp004GateNeedsTemporalRule,
33
+ comp005CapabilityKindIsNounRule,
34
+ comp006ShellNeedsReasonRule,
35
+ comp007CompositionSprawlRule,
36
+ ];
37
+ }
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Shared walking/wiring helpers for the COMP* composition rules (#562, epic
3
+ * #551). Every COMP rule operates over the real, discovered `Component`
4
+ * graph (see ../../component-checks.ts), not raw TypeScript source — these
5
+ * helpers flatten a component's `deploy`/`rollback` phases into steps and
6
+ * parse the wiring reference forms `../../../components/driver.ts` already
7
+ * resolves at runtime, so lint can check the same references before
8
+ * anything runs (per docs/components/orchestration.mdx).
9
+ */
10
+
11
+ import type { Component, Gate, Phase, Step } from "../../../components/component";
12
+
13
+ /** True if a step/gate/nested-phase entry is a `Gate` (`kind: "gate"`). */
14
+ export function isGateEntry(entry: Step | Gate | Phase): entry is Gate {
15
+ return (entry as { kind?: unknown }).kind === "gate";
16
+ }
17
+
18
+ /** True if a step/gate/nested-phase entry is itself a nested `Phase` (a fan-out unit) — see `component.ts`'s `isNestedPhase`. */
19
+ export function isPhaseEntry(entry: Step | Gate | Phase): entry is Phase {
20
+ return typeof (entry as Phase).phase === "string" && Array.isArray((entry as Phase).steps);
21
+ }
22
+
23
+ /** True if a step/gate/nested-phase entry is a plain capability `Step` (neither a gate nor a nested phase). */
24
+ export function isStepEntry(entry: Step | Gate | Phase): entry is Step {
25
+ return !isGateEntry(entry) && !isPhaseEntry(entry);
26
+ }
27
+
28
+ /** One step found while walking a component, together with the (possibly nested) phase it lives directly under. */
29
+ export interface WalkedStep {
30
+ step: Step;
31
+ phaseName: string;
32
+ /**
33
+ * The exact `Phase` object this step lives directly under — object
34
+ * identity, not just `phaseName`, since two different nested fan-out
35
+ * phases (or a nested phase and its parent) can share the same display
36
+ * name. Rules that need "another step in this same phase" (e.g. COMP003's
37
+ * compensation-sibling check) should group by this reference, not by name.
38
+ */
39
+ phase: Phase;
40
+ }
41
+
42
+ /** One gate found while walking a component, together with the phase it lives directly under. */
43
+ export interface WalkedGate {
44
+ gate: Gate;
45
+ phaseName: string;
46
+ phase: Phase;
47
+ }
48
+
49
+ /**
50
+ * Flatten every step and gate across a list of phases (including nested
51
+ * fan-out phases and `onFailure` compensation phases), matching
52
+ * `component.ts`'s own `allSteps` walk but keeping the immediate phase
53
+ * alongside each entry (needed for `@Phase.field` reference checks and for
54
+ * "same phase" sibling checks) and separating gates out explicitly.
55
+ */
56
+ export function walkPhases(phases: Phase[] | undefined): { steps: WalkedStep[]; gates: WalkedGate[] } {
57
+ const steps: WalkedStep[] = [];
58
+ const gates: WalkedGate[] = [];
59
+
60
+ const walk = (ps: Phase[]) => {
61
+ for (const p of ps) {
62
+ for (const entry of p.steps) {
63
+ if (isPhaseEntry(entry)) {
64
+ walk([entry]);
65
+ } else if (isGateEntry(entry)) {
66
+ gates.push({ gate: entry, phaseName: p.phase, phase: p });
67
+ } else {
68
+ steps.push({ step: entry, phaseName: p.phase, phase: p });
69
+ }
70
+ }
71
+ if (p.onFailure) walk(p.onFailure);
72
+ }
73
+ };
74
+ walk(phases ?? []);
75
+
76
+ return { steps, gates };
77
+ }
78
+
79
+ /** Every step and gate across a component's `deploy` plus its top-level `rollback` phases. */
80
+ export function walkComponent(component: Component): { steps: WalkedStep[]; gates: WalkedGate[] } {
81
+ const deployWalk = walkPhases(component.deploy);
82
+ const rollbackWalk = walkPhases(component.rollback);
83
+ return {
84
+ steps: [...deployWalk.steps, ...rollbackWalk.steps],
85
+ gates: [...deployWalk.gates, ...rollbackWalk.gates],
86
+ };
87
+ }
88
+
89
+ /** Every distinct phase name declared directly in a component's `deploy` (top-level only, not nested fan-out phase names — those are addressed by the fan-out's own phase name, never the parent's). */
90
+ export function topLevelPhaseNames(component: Component): Set<string> {
91
+ return new Set((component.deploy ?? []).map((p) => p.phase));
92
+ }
93
+
94
+ // ── Wiring reference parsing (mirrors ../../../components/driver.ts) ────────
95
+
96
+ /** `@Phase.field[...]` — a prior step's output within the same component, keyed by phase name. */
97
+ const PRIOR_STEP_REF = /^@([A-Za-z0-9_ ]+)\.([A-Za-z0-9_.]+)$/;
98
+ /** `@<component>.publish.uri|digest|key` — a cross-component artifact reference. */
99
+ const COMPONENT_ARTIFACT_REF = /^@([a-z0-9]+(?:-[a-z0-9]+)*)\.publish\.(uri|digest|key)$/;
100
+
101
+ export interface PriorStepRef {
102
+ kind: "prior-step";
103
+ phaseName: string;
104
+ field: string;
105
+ }
106
+
107
+ export interface ComponentArtifactRef {
108
+ kind: "component-artifact";
109
+ componentName: string;
110
+ field: "uri" | "digest" | "key";
111
+ }
112
+
113
+ export type WiringRef = PriorStepRef | ComponentArtifactRef;
114
+
115
+ /** Parse a single string value as one of the two name-bearing wiring reference forms (`@Phase.field`, `@component.publish.*`). Returns `undefined` for a plain literal, an `$env.*` reference, or a malformed `@` reference (schema validation, not lint, is responsible for rejecting malformed references). */
116
+ export function parseWiringRef(value: unknown): WiringRef | undefined {
117
+ if (typeof value !== "string") return undefined;
118
+
119
+ const artifactMatch = value.match(COMPONENT_ARTIFACT_REF);
120
+ if (artifactMatch) {
121
+ return { kind: "component-artifact", componentName: artifactMatch[1], field: artifactMatch[2] as "uri" | "digest" | "key" };
122
+ }
123
+
124
+ const priorStepMatch = value.match(PRIOR_STEP_REF);
125
+ if (priorStepMatch) {
126
+ return { kind: "prior-step", phaseName: priorStepMatch[1], field: priorStepMatch[2] };
127
+ }
128
+
129
+ return undefined;
130
+ }
131
+
132
+ /** Recursively collect every wiring-reference-shaped string found anywhere in a step's fields (imageRef, jar, revision, inputs, and any other open capability-specific property), matching how ../../../components/driver.ts's `resolveStepInput` walks a step's input. */
133
+ export function collectWiringRefs(step: Step): WiringRef[] {
134
+ const refs: WiringRef[] = [];
135
+ const walk = (value: unknown): void => {
136
+ if (typeof value === "string") {
137
+ const ref = parseWiringRef(value);
138
+ if (ref) refs.push(ref);
139
+ return;
140
+ }
141
+ if (Array.isArray(value)) {
142
+ for (const v of value) walk(v);
143
+ return;
144
+ }
145
+ if (value && typeof value === "object") {
146
+ // `{ stackOutput: { stack, name } }` is a cross-stack reference, resolved
147
+ // by the graph, not a same-project component/phase reference — out of
148
+ // scope for this reference check (see component-contract.mdx).
149
+ if ("stackOutput" in value) return;
150
+ for (const v of Object.values(value as Record<string, unknown>)) walk(v);
151
+ }
152
+ };
153
+ const { kind: _kind, ...rest } = step;
154
+ walk(rest);
155
+ return refs;
156
+ }
157
+
158
+ /** A structural fingerprint of a component's composition shape, ignoring `name`/`dependsOn`/wiring literal values — two components with the same fingerprint compose the exact same phase/step-kind shape (see COMP007, "identical composition repeated across components"). */
159
+ export function compositionFingerprint(component: Component): string {
160
+ const shape = (component.deploy ?? []).map((p) => fingerprintPhase(p));
161
+ return JSON.stringify(shape);
162
+ }
163
+
164
+ function fingerprintPhase(phase: Phase): unknown {
165
+ return {
166
+ parallel: phase.parallel ?? false,
167
+ steps: phase.steps.map((entry) => {
168
+ if (isPhaseEntry(entry)) return fingerprintPhase(entry);
169
+ if (isGateEntry(entry)) return { kind: "gate" };
170
+ return { kind: entry.kind };
171
+ }),
172
+ };
173
+ }
@@ -0,0 +1,55 @@
1
+ import { describe, test, expect } from "vitest";
2
+ import { LexiconIndex, lexiconCompletions, type LexiconEntry } from "./lexicon-providers";
3
+ import type { CompletionContext } from "./types";
4
+
5
+ function resourceEntry(resourceType: string): LexiconEntry {
6
+ return { resourceType, kind: "resource", lexicon: "test" };
7
+ }
8
+
9
+ /**
10
+ * Index with 80 "Sample*" resources plus a common "StorageBucket" inserted
11
+ * LAST — so in raw index order it lands well past the old 50-item cap. This is
12
+ * the shape that made gcp v1.152 drop StorageBucket from a "new S" completion.
13
+ */
14
+ function largeSPrefixIndex(): LexiconIndex {
15
+ const entries: Record<string, LexiconEntry> = {};
16
+ for (let i = 0; i < 80; i++) {
17
+ entries[`Sample${String(i).padStart(3, "0")}`] = resourceEntry(`Test::Sample::N${i}`);
18
+ }
19
+ entries["StorageBucket"] = resourceEntry("Test::Storage::Bucket");
20
+ return new LexiconIndex(entries);
21
+ }
22
+
23
+ const newCtx = (linePrefix: string, wordAtCursor: string): CompletionContext =>
24
+ ({
25
+ uri: "file:///t.ts",
26
+ content: linePrefix,
27
+ linePrefix,
28
+ wordAtCursor,
29
+ position: { line: 0, character: linePrefix.length },
30
+ }) as unknown as CompletionContext;
31
+
32
+ describe("lexiconCompletions resource ranking (#600)", () => {
33
+ test("a prefix returns every match — a common resource isn't truncated by a cap", () => {
34
+ const items = lexiconCompletions(newCtx("const x = new S", "S"), largeSPrefixIndex(), "Test resource");
35
+ const labels = items.map((i) => i.label);
36
+ // 81 resources start with "S"; StorageBucket (inserted 81st, past the old
37
+ // slice(0,50)) must still be present.
38
+ expect(labels).toContain("StorageBucket");
39
+ expect(labels.length).toBe(81);
40
+ });
41
+
42
+ test("results are sorted deterministically (alphabetical)", () => {
43
+ const labels = lexiconCompletions(newCtx("const x = new S", "S"), largeSPrefixIndex(), "Test resource").map(
44
+ (i) => i.label,
45
+ );
46
+ expect(labels).toEqual([...labels].sort((a, b) => a.localeCompare(b)));
47
+ });
48
+
49
+ test("the unfiltered `new ` dump stays capped at 100", () => {
50
+ const entries: Record<string, LexiconEntry> = {};
51
+ for (let i = 0; i < 200; i++) entries[`R${String(i).padStart(3, "0")}`] = resourceEntry(`T::R::${i}`);
52
+ const items = lexiconCompletions(newCtx("const x = new ", ""), new LexiconIndex(entries), "Test resource");
53
+ expect(items.length).toBe(100);
54
+ });
55
+ });
@@ -94,11 +94,21 @@ export function lexiconCompletions(
94
94
  // After `new ` — suggest resource class names
95
95
  if (/\bnew\s+\w*$/.test(linePrefix)) {
96
96
  const resources = index.getResourceNames();
97
- const filtered = wordAtCursor
98
- ? resources.filter((r) => r.className.toLowerCase().startsWith(wordAtCursor.toLowerCase()))
97
+ const prefix = wordAtCursor?.toLowerCase();
98
+ const filtered = prefix
99
+ ? resources.filter((r) => r.className.toLowerCase().startsWith(prefix))
99
100
  : resources;
100
101
 
101
- return filtered.slice(0, 50).map((r) => ({
102
+ // Deterministic ordering (the raw index order is arbitrary).
103
+ const sorted = [...filtered].sort((a, b) => a.className.localeCompare(b.className));
104
+
105
+ // When the user has typed a prefix, the set is already narrowed — return
106
+ // every match so a common resource never gets truncated out of a short
107
+ // prefix on a large lexicon (e.g. StorageBucket for "S" on gcp/aws, #600).
108
+ // Only cap the unfiltered `new ` dump, to bound the payload.
109
+ const items = prefix ? sorted : sorted.slice(0, 100);
110
+
111
+ return items.map((r) => ({
102
112
  label: r.className,
103
113
  insertText: r.className,
104
114
  kind: "resource" as const,
@@ -57,3 +57,41 @@ describe("loadActivities — heartbeat-shim safety", () => {
57
57
  expect(activities.has("waitForStack")).toBe(true);
58
58
  });
59
59
  });
60
+
61
+ // Cloud appliers were relocated out of the temporal lexicon into their own
62
+ // lexicons (#706); the loader pulls them in per the project's configured
63
+ // `lexicons`. Proves the relocation resolves end-to-end.
64
+ describe("loadActivities — multi-lexicon (#706)", () => {
65
+ test("base (no lexicons): temporal activities present, cloud appliers absent", async () => {
66
+ const a = await loadActivities();
67
+ expect(a.has("kubectlApply")).toBe(true); // temporal base
68
+ expect(a.has("k3dUp")).toBe(true); // cloud-agnostic, stays in temporal
69
+ expect(a.has("gcpApply")).toBe(false); // relocated to gcp
70
+ expect(a.has("flociUp")).toBe(false); // relocated to aws
71
+ expect(a.has("azGroupEnsure")).toBe(false); // relocated to azure
72
+ });
73
+
74
+ test("gcp lexicon contributes the GCP applier", async () => {
75
+ const a = await loadActivities(["gcp"]);
76
+ expect(a.has("gcpApply")).toBe(true);
77
+ expect(a.has("kubectlApply")).toBe(true); // base still loaded alongside
78
+ });
79
+
80
+ test("aws lexicon contributes the Floci lifecycle", async () => {
81
+ const a = await loadActivities(["aws"]);
82
+ expect(a.has("flociUp")).toBe(true);
83
+ expect(a.has("flociDown")).toBe(true);
84
+ });
85
+
86
+ test("azure lexicon contributes the resource-group lifecycle + ARM applier", async () => {
87
+ const a = await loadActivities(["azure"]);
88
+ expect(a.has("azGroupEnsure")).toBe(true);
89
+ expect(a.has("azGroupDelete")).toBe(true);
90
+ expect(a.has("azApply")).toBe(true);
91
+ });
92
+
93
+ test("unknown lexicon is skipped without throwing", async () => {
94
+ const a = await loadActivities(["definitely-not-a-lexicon"]);
95
+ expect(a.has("kubectlApply")).toBe(true);
96
+ });
97
+ });