@intentius/chant 0.56.0 → 0.58.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 (515) hide show
  1. package/dist/audit/catalog.d.ts +51 -1
  2. package/dist/audit/catalog.d.ts.map +1 -1
  3. package/dist/audit/core.d.ts +20 -1
  4. package/dist/audit/core.d.ts.map +1 -1
  5. package/dist/audit/discover.d.ts +14 -8
  6. package/dist/audit/discover.d.ts.map +1 -1
  7. package/dist/audit/lineage.d.ts +10 -0
  8. package/dist/audit/lineage.d.ts.map +1 -0
  9. package/dist/audit/prior-art.d.ts +385 -0
  10. package/dist/audit/prior-art.d.ts.map +1 -0
  11. package/dist/audit/proof.d.ts +16 -0
  12. package/dist/audit/proof.d.ts.map +1 -1
  13. package/dist/audit/report-html.d.ts.map +1 -1
  14. package/dist/audit/report-model.d.ts +8 -1
  15. package/dist/audit/report-model.d.ts.map +1 -1
  16. package/dist/audit/report.d.ts.map +1 -1
  17. package/dist/audit/rules-doc.d.ts +3 -0
  18. package/dist/audit/rules-doc.d.ts.map +1 -1
  19. package/dist/audit/secrets.d.ts +15 -0
  20. package/dist/audit/secrets.d.ts.map +1 -1
  21. package/dist/audit/terraform-state.d.ts +46 -0
  22. package/dist/audit/terraform-state.d.ts.map +1 -0
  23. package/dist/build.d.ts +17 -1
  24. package/dist/build.d.ts.map +1 -1
  25. package/dist/claimed-fields.d.ts +161 -0
  26. package/dist/claimed-fields.d.ts.map +1 -0
  27. package/dist/cli/build-params-cli.d.ts +2 -2
  28. package/dist/cli/command-group.d.ts +14 -0
  29. package/dist/cli/command-group.d.ts.map +1 -1
  30. package/dist/cli/commands/audit.d.ts +17 -1
  31. package/dist/cli/commands/audit.d.ts.map +1 -1
  32. package/dist/cli/commands/build.d.ts.map +1 -1
  33. package/dist/cli/commands/check-lexicon-docs.d.ts +1 -1
  34. package/dist/cli/commands/check-lexicon-examples.d.ts +16 -6
  35. package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
  36. package/dist/cli/commands/import.d.ts.map +1 -1
  37. package/dist/cli/commands/init-lexicon/templates/codegen.d.ts +1 -0
  38. package/dist/cli/commands/init-lexicon/templates/codegen.d.ts.map +1 -1
  39. package/dist/cli/commands/init-lexicon/templates/project.d.ts +7 -0
  40. package/dist/cli/commands/init-lexicon/templates/project.d.ts.map +1 -1
  41. package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
  42. package/dist/cli/commands/init.d.ts.map +1 -1
  43. package/dist/cli/commands/lint.d.ts.map +1 -1
  44. package/dist/cli/commands/onboard.d.ts +2 -0
  45. package/dist/cli/commands/onboard.d.ts.map +1 -1
  46. package/dist/cli/handlers/build.d.ts.map +1 -1
  47. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  48. package/dist/cli/handlers/operator.d.ts +82 -16
  49. package/dist/cli/handlers/operator.d.ts.map +1 -1
  50. package/dist/cli/handlers/run.d.ts +49 -42
  51. package/dist/cli/handlers/run.d.ts.map +1 -1
  52. package/dist/cli/main.d.ts +28 -1
  53. package/dist/cli/main.d.ts.map +1 -1
  54. package/dist/cli/mcp/op-tools.d.ts +16 -1
  55. package/dist/cli/mcp/op-tools.d.ts.map +1 -1
  56. package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
  57. package/dist/cli/mcp/resources/context.d.ts.map +1 -1
  58. package/dist/cli/registry.d.ts +26 -9
  59. package/dist/cli/registry.d.ts.map +1 -1
  60. package/dist/codegen/generate-post-synth-barrel.d.ts +1 -1
  61. package/dist/components/auto-release.d.ts +10 -20
  62. package/dist/components/auto-release.d.ts.map +1 -1
  63. package/dist/components/capability.d.ts +4 -4
  64. package/dist/components/cli-support.d.ts +20 -21
  65. package/dist/components/cli-support.d.ts.map +1 -1
  66. package/dist/components/component.d.ts +3 -3
  67. package/dist/components/component.d.ts.map +1 -1
  68. package/dist/components/driver-output.d.ts.map +1 -1
  69. package/dist/components/driver.d.ts +66 -36
  70. package/dist/components/driver.d.ts.map +1 -1
  71. package/dist/components/index.d.ts +1 -2
  72. package/dist/components/index.d.ts.map +1 -1
  73. package/dist/components/manifest-persistence.d.ts +1 -9
  74. package/dist/components/manifest-persistence.d.ts.map +1 -1
  75. package/dist/components/run-progress.d.ts +6 -6
  76. package/dist/components/run-progress.d.ts.map +1 -1
  77. package/dist/components/verbs/run-agent.d.ts +9 -15
  78. package/dist/components/verbs/run-agent.d.ts.map +1 -1
  79. package/dist/components/verbs/verify.d.ts +2 -2
  80. package/dist/deep-observation.d.ts +17 -0
  81. package/dist/deep-observation.d.ts.map +1 -1
  82. package/dist/discovery/files.d.ts +4 -4
  83. package/dist/discovery/fold-import.d.ts.map +1 -1
  84. package/dist/discovery/param-deps.d.ts +33 -0
  85. package/dist/discovery/param-deps.d.ts.map +1 -1
  86. package/dist/discovery/sandbox/bundle.d.ts.map +1 -1
  87. package/dist/discovery/sandbox/config-wire.d.ts +2 -2
  88. package/dist/fold/foldable-helpers.d.ts +5 -1
  89. package/dist/fold/foldable-helpers.d.ts.map +1 -1
  90. package/dist/fold-provenance.d.ts +236 -0
  91. package/dist/fold-provenance.d.ts.map +1 -0
  92. package/dist/graph-ops.d.ts +1 -1
  93. package/dist/held-elsewhere.d.ts +85 -0
  94. package/dist/held-elsewhere.d.ts.map +1 -0
  95. package/dist/import/ir-utils.d.ts +1 -1
  96. package/dist/import/ir-utils.d.ts.map +1 -1
  97. package/dist/import/parser.d.ts +33 -0
  98. package/dist/import/parser.d.ts.map +1 -1
  99. package/dist/index.d.ts +3 -0
  100. package/dist/index.d.ts.map +1 -1
  101. package/dist/kubectl-context.d.ts +3 -3
  102. package/dist/lexicon-config.d.ts +4 -4
  103. package/dist/lexicon-plugin-helpers.d.ts +1 -1
  104. package/dist/lexicon.d.ts +104 -14
  105. package/dist/lexicon.d.ts.map +1 -1
  106. package/dist/lifecycle/change-set.d.ts +13 -0
  107. package/dist/lifecycle/change-set.d.ts.map +1 -1
  108. package/dist/lifecycle/deep-diff.d.ts +133 -6
  109. package/dist/lifecycle/deep-diff.d.ts.map +1 -1
  110. package/dist/lifecycle/deep-observe.d.ts.map +1 -1
  111. package/dist/lifecycle/gate-ledger.d.ts +71 -3
  112. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  113. package/dist/lifecycle/index.d.ts +1 -0
  114. package/dist/lifecycle/index.d.ts.map +1 -1
  115. package/dist/lifecycle/release-ledger.d.ts +2 -2
  116. package/dist/lifecycle/run-ledger.d.ts +53 -0
  117. package/dist/lifecycle/run-ledger.d.ts.map +1 -0
  118. package/dist/lifecycle/symptoms.d.ts +3 -3
  119. package/dist/lifecycle/symptoms.d.ts.map +1 -1
  120. package/dist/lint/config.d.ts +81 -2
  121. package/dist/lint/config.d.ts.map +1 -1
  122. package/dist/lint/post-synth.d.ts +55 -2
  123. package/dist/lint/post-synth.d.ts.map +1 -1
  124. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts +1 -1
  125. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts.map +1 -1
  126. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts +1 -1
  127. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts.map +1 -1
  128. package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts +44 -0
  129. package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts.map +1 -0
  130. package/dist/lint/rules/comp/index.d.ts +1 -1
  131. package/dist/lint/rules/comp/index.d.ts.map +1 -1
  132. package/dist/lint/rules/op/index.d.ts +16 -0
  133. package/dist/lint/rules/op/index.d.ts.map +1 -0
  134. package/dist/lint/rules/op/ops012-activity-contract.d.ts +27 -0
  135. package/dist/lint/rules/op/ops012-activity-contract.d.ts.map +1 -0
  136. package/dist/lint/rules/op/ops013-step-output-ref.d.ts +61 -0
  137. package/dist/lint/rules/op/ops013-step-output-ref.d.ts.map +1 -0
  138. package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts +55 -0
  139. package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts.map +1 -0
  140. package/dist/lint/rules/op/support.d.ts +12 -0
  141. package/dist/lint/rules/op/support.d.ts.map +1 -0
  142. package/dist/lint/suppressions.d.ts +108 -0
  143. package/dist/lint/suppressions.d.ts.map +1 -0
  144. package/dist/live-endpoint.d.ts +1 -1
  145. package/dist/op/activities/activity-contracts.d.ts +54 -0
  146. package/dist/op/activities/activity-contracts.d.ts.map +1 -0
  147. package/dist/op/activities/apply.d.ts +262 -0
  148. package/dist/op/activities/apply.d.ts.map +1 -0
  149. package/dist/op/activities/build.d.ts +13 -0
  150. package/dist/op/activities/build.d.ts.map +1 -0
  151. package/dist/op/activities/converge.d.ts +110 -0
  152. package/dist/op/activities/converge.d.ts.map +1 -0
  153. package/dist/op/activities/env-teardown.d.ts +76 -0
  154. package/dist/op/activities/env-teardown.d.ts.map +1 -0
  155. package/dist/op/activities/guard-validate.d.ts +64 -0
  156. package/dist/op/activities/guard-validate.d.ts.map +1 -0
  157. package/dist/op/activities/http-check.d.ts +34 -0
  158. package/dist/op/activities/http-check.d.ts.map +1 -0
  159. package/dist/op/activities/index.d.ts +31 -0
  160. package/dist/op/activities/index.d.ts.map +1 -0
  161. package/dist/op/activities/lexicon-upgrade.d.ts +182 -0
  162. package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -0
  163. package/dist/op/activities/lifecycle.d.ts +42 -0
  164. package/dist/op/activities/lifecycle.d.ts.map +1 -0
  165. package/dist/op/activities/pipeline-audit.d.ts +84 -0
  166. package/dist/op/activities/pipeline-audit.d.ts.map +1 -0
  167. package/dist/op/activities/policy.d.ts +17 -0
  168. package/dist/op/activities/policy.d.ts.map +1 -0
  169. package/dist/op/activities/reconcile.d.ts +89 -0
  170. package/dist/op/activities/reconcile.d.ts.map +1 -0
  171. package/dist/op/activities/shell.d.ts +13 -0
  172. package/dist/op/activities/shell.d.ts.map +1 -0
  173. package/dist/op/activities/teardown.d.ts +10 -0
  174. package/dist/op/activities/teardown.d.ts.map +1 -0
  175. package/dist/op/activities/util.d.ts +7 -0
  176. package/dist/op/activities/util.d.ts.map +1 -0
  177. package/dist/op/activities/wait.d.ts +28 -0
  178. package/dist/op/activities/wait.d.ts.map +1 -0
  179. package/dist/op/activities/workflow-audit.d.ts +92 -0
  180. package/dist/op/activities/workflow-audit.d.ts.map +1 -0
  181. package/dist/op/activity-contract-registry.d.ts +76 -0
  182. package/dist/op/activity-contract-registry.d.ts.map +1 -0
  183. package/dist/op/activity-contract.d.ts +25 -18
  184. package/dist/op/activity-contract.d.ts.map +1 -1
  185. package/dist/op/activity-failure.d.ts +28 -0
  186. package/dist/op/activity-failure.d.ts.map +1 -0
  187. package/dist/op/activity-profiles.d.ts +118 -0
  188. package/dist/op/activity-profiles.d.ts.map +1 -0
  189. package/dist/op/activity-registry.d.ts +22 -38
  190. package/dist/op/activity-registry.d.ts.map +1 -1
  191. package/dist/op/activity-runtime.d.ts +3 -14
  192. package/dist/op/activity-runtime.d.ts.map +1 -1
  193. package/dist/op/builders.d.ts +84 -29
  194. package/dist/op/builders.d.ts.map +1 -1
  195. package/dist/op/composites/apply-op.d.ts +113 -0
  196. package/dist/op/composites/apply-op.d.ts.map +1 -0
  197. package/dist/op/composites/converge-op.d.ts +120 -0
  198. package/dist/op/composites/converge-op.d.ts.map +1 -0
  199. package/dist/op/composites/index.d.ts +26 -0
  200. package/dist/op/composites/index.d.ts.map +1 -0
  201. package/dist/op/composites/lexicon-upgrade-op.d.ts +78 -0
  202. package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -0
  203. package/dist/op/composites/pipeline-audit-op.d.ts +50 -0
  204. package/dist/op/composites/pipeline-audit-op.d.ts.map +1 -0
  205. package/dist/op/composites/reconcile-op.d.ts +57 -0
  206. package/dist/op/composites/reconcile-op.d.ts.map +1 -0
  207. package/dist/op/composites/watch-op.d.ts +60 -0
  208. package/dist/op/composites/watch-op.d.ts.map +1 -0
  209. package/dist/op/composites/workflow-audit-op.d.ts +57 -0
  210. package/dist/op/composites/workflow-audit-op.d.ts.map +1 -0
  211. package/dist/op/converge-rule.d.ts +12 -10
  212. package/dist/op/converge-rule.d.ts.map +1 -1
  213. package/dist/op/cron.d.ts +51 -0
  214. package/dist/op/cron.d.ts.map +1 -0
  215. package/dist/op/discover.d.ts +9 -0
  216. package/dist/op/discover.d.ts.map +1 -1
  217. package/dist/op/duration.d.ts +11 -0
  218. package/dist/op/duration.d.ts.map +1 -0
  219. package/dist/op/emulator-lifecycle.d.ts.map +1 -1
  220. package/dist/op/gate.d.ts +90 -0
  221. package/dist/op/gate.d.ts.map +1 -0
  222. package/dist/op/generate-pipeline.d.ts +9 -0
  223. package/dist/op/generate-pipeline.d.ts.map +1 -1
  224. package/dist/op/index.d.ts +23 -7
  225. package/dist/op/index.d.ts.map +1 -1
  226. package/dist/op/local-executor.d.ts +114 -28
  227. package/dist/op/local-executor.d.ts.map +1 -1
  228. package/dist/op/local-output.d.ts +11 -1
  229. package/dist/op/local-output.d.ts.map +1 -1
  230. package/dist/op/op-ir.d.ts +184 -0
  231. package/dist/op/op-ir.d.ts.map +1 -0
  232. package/dist/op/op-verb-class.d.ts +10 -2
  233. package/dist/op/op-verb-class.d.ts.map +1 -1
  234. package/dist/op/operator.d.ts +30 -5
  235. package/dist/op/operator.d.ts.map +1 -1
  236. package/dist/op/receipt-store.d.ts +1 -1
  237. package/dist/op/receipt-store.d.ts.map +1 -1
  238. package/dist/op/resource.d.ts +15 -1
  239. package/dist/op/resource.d.ts.map +1 -1
  240. package/dist/op/runtime.d.ts +200 -0
  241. package/dist/op/runtime.d.ts.map +1 -0
  242. package/dist/op/runtimes/local.d.ts +29 -0
  243. package/dist/op/runtimes/local.d.ts.map +1 -0
  244. package/dist/op/step-output-ref.d.ts +15 -13
  245. package/dist/op/step-output-ref.d.ts.map +1 -1
  246. package/dist/op/types.d.ts +79 -27
  247. package/dist/op/types.d.ts.map +1 -1
  248. package/dist/ownership.d.ts +1 -1
  249. package/dist/provenance.d.ts +21 -0
  250. package/dist/provenance.d.ts.map +1 -1
  251. package/dist/serializer-walker.d.ts.map +1 -1
  252. package/dist/stack-output.d.ts +5 -0
  253. package/dist/stack-output.d.ts.map +1 -1
  254. package/package.json +6 -1
  255. package/src/audit/catalog.test.ts +79 -3
  256. package/src/audit/catalog.ts +111 -13
  257. package/src/audit/core.test.ts +26 -0
  258. package/src/audit/core.ts +61 -17
  259. package/src/audit/discover.test.ts +39 -0
  260. package/src/audit/discover.ts +91 -15
  261. package/src/audit/lineage.ts +100 -0
  262. package/src/audit/prior-art.ts +153 -0
  263. package/src/audit/proof.test.ts +67 -0
  264. package/src/audit/proof.ts +70 -0
  265. package/src/audit/report-html.ts +9 -4
  266. package/src/audit/report-model.ts +16 -2
  267. package/src/audit/report.test.ts +33 -0
  268. package/src/audit/report.ts +5 -3
  269. package/src/audit/rules-doc.test.ts +34 -2
  270. package/src/audit/rules-doc.ts +82 -9
  271. package/src/audit/secrets.ts +15 -3
  272. package/src/audit/terraform-state.test.ts +144 -0
  273. package/src/audit/terraform-state.ts +131 -0
  274. package/src/build.ts +31 -1
  275. package/src/claimed-fields.test.ts +145 -0
  276. package/src/claimed-fields.ts +208 -0
  277. package/src/cli/build-params-cli.ts +2 -2
  278. package/src/cli/command-group.ts +14 -0
  279. package/src/cli/commands/__fixtures__/audit-terraform/infra/.terraform/providers/lock.txt +1 -0
  280. package/src/cli/commands/__fixtures__/audit-terraform/infra/main.tf +1 -0
  281. package/src/cli/commands/__fixtures__/audit-terraform/infra/modules/foo/main.tf +1 -0
  282. package/src/cli/commands/__fixtures__/audit-terraform/infra/variables.tf +3 -0
  283. package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +13 -4
  284. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/docs-cli.ts +4 -0
  285. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/package.ts +1 -0
  286. package/src/cli/commands/__fixtures__/init-lexicon-output/src/package-cli.ts +21 -0
  287. package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +1 -0
  288. package/src/cli/commands/audit.test.ts +189 -11
  289. package/src/cli/commands/audit.ts +73 -27
  290. package/src/cli/commands/build.test.ts +292 -9
  291. package/src/cli/commands/build.ts +156 -24
  292. package/src/cli/commands/check-lexicon-docs.ts +1 -1
  293. package/src/cli/commands/check-lexicon-examples.test.ts +29 -1
  294. package/src/cli/commands/check-lexicon-examples.ts +42 -10
  295. package/src/cli/commands/import.ts +6 -0
  296. package/src/cli/commands/init-lexicon/templates/codegen.ts +9 -0
  297. package/src/cli/commands/init-lexicon/templates/project.ts +44 -4
  298. package/src/cli/commands/init-lexicon.test.ts +97 -2
  299. package/src/cli/commands/init-lexicon.ts +4 -2
  300. package/src/cli/commands/init.test.ts +35 -1
  301. package/src/cli/commands/init.ts +14 -3
  302. package/src/cli/commands/lint.test.ts +48 -0
  303. package/src/cli/commands/lint.ts +123 -0
  304. package/src/cli/commands/onboard.test.ts +75 -0
  305. package/src/cli/commands/onboard.ts +54 -23
  306. package/src/cli/handlers/build.test.ts +81 -1
  307. package/src/cli/handlers/build.ts +15 -1
  308. package/src/cli/handlers/graph.test.ts +3 -3
  309. package/src/cli/handlers/lifecycle.test.ts +195 -12
  310. package/src/cli/handlers/lifecycle.ts +123 -6
  311. package/src/cli/handlers/misc.ts +1 -1
  312. package/src/cli/handlers/operator.test.ts +183 -11
  313. package/src/cli/handlers/operator.ts +260 -55
  314. package/src/cli/handlers/run.test.ts +431 -1287
  315. package/src/cli/handlers/run.ts +365 -915
  316. package/src/cli/main.test.ts +23 -15
  317. package/src/cli/main.ts +88 -33
  318. package/src/cli/mcp/op-tools.ts +147 -88
  319. package/src/cli/mcp/resource-handlers.ts +10 -37
  320. package/src/cli/mcp/resources/context.ts +11 -12
  321. package/src/cli/mcp/server.test.ts +25 -27
  322. package/src/cli/mcp/server.ts +2 -2
  323. package/src/cli/registry.ts +26 -9
  324. package/src/codegen/docs.ts +1 -1
  325. package/src/codegen/generate-post-synth-barrel.test.ts +1 -1
  326. package/src/codegen/generate-post-synth-barrel.ts +1 -1
  327. package/src/components/README.md +3 -2
  328. package/src/components/auto-release.test.ts +4 -20
  329. package/src/components/auto-release.ts +17 -42
  330. package/src/components/capability.ts +4 -4
  331. package/src/components/cli-support.test.ts +10 -5
  332. package/src/components/cli-support.ts +52 -44
  333. package/src/components/component.schema.json +2 -2
  334. package/src/components/component.ts +3 -3
  335. package/src/components/driver-output.ts +14 -2
  336. package/src/components/driver.test.ts +92 -22
  337. package/src/components/driver.ts +193 -83
  338. package/src/components/index.ts +1 -6
  339. package/src/components/manifest-persistence.test.ts +2 -20
  340. package/src/components/manifest-persistence.ts +4 -24
  341. package/src/components/pilots/pilots-lint-clean.test.ts +11 -10
  342. package/src/components/run-progress.ts +6 -6
  343. package/src/components/verbs/run-agent.ts +11 -17
  344. package/src/components/verbs/verify.ts +2 -2
  345. package/src/deep-observation.test.ts +75 -0
  346. package/src/deep-observation.ts +48 -0
  347. package/src/discovery/collect.test.ts +3 -3
  348. package/src/discovery/files.ts +4 -4
  349. package/src/discovery/fold-import.ts +105 -2
  350. package/src/discovery/param-deps.test.ts +112 -1
  351. package/src/discovery/param-deps.ts +193 -0
  352. package/src/discovery/sandbox/bundle.ts +15 -1
  353. package/src/discovery/sandbox/config-wire.test.ts +2 -2
  354. package/src/discovery/sandbox/config-wire.ts +2 -2
  355. package/src/fold/fold.test.ts +56 -0
  356. package/src/fold/foldable-helpers.ts +81 -1
  357. package/src/fold-provenance-exclusion.test.ts +186 -0
  358. package/src/fold-provenance.test.ts +495 -0
  359. package/src/fold-provenance.ts +441 -0
  360. package/src/graph-ops.test.ts +2 -2
  361. package/src/graph-ops.ts +1 -1
  362. package/src/held-elsewhere.test.ts +102 -0
  363. package/src/held-elsewhere.ts +114 -0
  364. package/src/import/ir-utils.ts +11 -1
  365. package/src/import/parser.ts +35 -0
  366. package/src/index.ts +3 -0
  367. package/src/kubectl-context.ts +3 -3
  368. package/src/lexicon-config.test.ts +5 -5
  369. package/src/lexicon-config.ts +4 -4
  370. package/src/lexicon-plugin-helpers.ts +1 -1
  371. package/src/lexicon.ts +120 -14
  372. package/src/lifecycle/change-set.test.ts +49 -0
  373. package/src/lifecycle/change-set.ts +67 -0
  374. package/src/lifecycle/converge-ledger.ts +1 -1
  375. package/src/lifecycle/deep-diff.test.ts +267 -14
  376. package/src/lifecycle/deep-diff.ts +217 -14
  377. package/src/lifecycle/deep-observe.test.ts +3 -1
  378. package/src/lifecycle/deep-observe.ts +16 -6
  379. package/src/lifecycle/gate-ledger.test.ts +74 -2
  380. package/src/lifecycle/gate-ledger.ts +143 -36
  381. package/src/lifecycle/index.ts +1 -0
  382. package/src/lifecycle/release-ledger.ts +2 -2
  383. package/src/lifecycle/run-ledger.test.ts +268 -0
  384. package/src/lifecycle/run-ledger.ts +201 -0
  385. package/src/lifecycle/symptoms.ts +3 -3
  386. package/src/lint/config.test.ts +82 -1
  387. package/src/lint/config.ts +112 -3
  388. package/src/lint/policy.test.ts +1 -1
  389. package/src/lint/post-synth.test.ts +31 -0
  390. package/src/lint/post-synth.ts +54 -1
  391. package/src/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.ts +2 -2
  392. package/src/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.ts +1 -1
  393. package/src/lint/rules/comp/comp.test.ts +1 -1
  394. package/src/lint/rules/comp/comp004-gate-needs-durable-runtime.ts +74 -0
  395. package/src/lint/rules/comp/index.ts +3 -3
  396. package/src/lint/rules/op/index.ts +23 -0
  397. package/src/lint/rules/op/ops012-activity-contract.test.ts +161 -0
  398. package/src/lint/rules/op/ops012-activity-contract.ts +64 -0
  399. package/src/lint/rules/op/ops013-step-output-ref.test.ts +237 -0
  400. package/src/lint/rules/op/ops013-step-output-ref.ts +98 -0
  401. package/src/lint/rules/op/ops014-converge-rule-refusals.test.ts +263 -0
  402. package/src/lint/rules/op/ops014-converge-rule-refusals.ts +179 -0
  403. package/src/lint/rules/op/support.ts +11 -0
  404. package/src/lint/suppressions.test.ts +163 -0
  405. package/src/lint/suppressions.ts +204 -0
  406. package/src/live-endpoint.ts +1 -1
  407. package/src/op/activities/activity-contracts.ts +66 -0
  408. package/src/op/activities/apply.test.ts +567 -0
  409. package/src/op/activities/apply.ts +662 -0
  410. package/src/op/activities/build.ts +26 -0
  411. package/src/op/activities/converge.test.ts +330 -0
  412. package/src/op/activities/converge.ts +449 -0
  413. package/src/op/activities/env-teardown.test.ts +187 -0
  414. package/src/op/activities/env-teardown.ts +171 -0
  415. package/src/op/activities/guard-validate.test.ts +139 -0
  416. package/src/op/activities/guard-validate.ts +223 -0
  417. package/src/op/activities/http-check.test.ts +48 -0
  418. package/src/op/activities/http-check.ts +55 -0
  419. package/src/op/activities/index.ts +119 -0
  420. package/src/op/activities/lexicon-upgrade.test.ts +492 -0
  421. package/src/op/activities/lexicon-upgrade.ts +673 -0
  422. package/src/op/activities/lifecycle.ts +85 -0
  423. package/src/op/activities/pipeline-audit.test.ts +67 -0
  424. package/src/op/activities/pipeline-audit.ts +201 -0
  425. package/src/op/activities/policy.test.ts +19 -0
  426. package/src/op/activities/policy.ts +37 -0
  427. package/src/op/activities/reconcile.test.ts +81 -0
  428. package/src/op/activities/reconcile.ts +187 -0
  429. package/src/op/activities/shell.ts +26 -0
  430. package/src/op/activities/teardown.ts +22 -0
  431. package/src/op/activities/util.ts +6 -0
  432. package/src/op/activities/wait.ts +69 -0
  433. package/src/op/activities/workflow-audit.test.ts +90 -0
  434. package/src/op/activities/workflow-audit.ts +203 -0
  435. package/src/op/activity-contract-registry.test.ts +98 -0
  436. package/src/op/activity-contract-registry.ts +114 -0
  437. package/src/op/activity-contract.test.ts +1 -1
  438. package/src/op/activity-contract.ts +37 -33
  439. package/src/op/activity-failure.ts +34 -0
  440. package/src/op/activity-profiles.test.ts +56 -0
  441. package/src/op/activity-profiles.ts +116 -0
  442. package/src/op/activity-registry.test.ts +37 -28
  443. package/src/op/activity-registry.ts +27 -56
  444. package/src/op/activity-runtime.ts +3 -47
  445. package/src/op/builders-exports.test.ts +0 -49
  446. package/src/op/builders-typed.test.ts +164 -0
  447. package/src/op/builders.ts +123 -39
  448. package/src/op/composites/apply-op.ts +208 -0
  449. package/src/op/composites/composites.test.ts +366 -0
  450. package/src/op/composites/converge-op.test.ts +168 -0
  451. package/src/op/composites/converge-op.ts +214 -0
  452. package/src/op/composites/index.ts +26 -0
  453. package/src/op/composites/lexicon-upgrade-op.test.ts +68 -0
  454. package/src/op/composites/lexicon-upgrade-op.ts +134 -0
  455. package/src/op/composites/pipeline-audit-op.test.ts +25 -0
  456. package/src/op/composites/pipeline-audit-op.ts +78 -0
  457. package/src/op/composites/reconcile-op.test.ts +51 -0
  458. package/src/op/composites/reconcile-op.ts +107 -0
  459. package/src/op/composites/watch-op.ts +108 -0
  460. package/src/op/composites/workflow-audit-op.test.ts +25 -0
  461. package/src/op/composites/workflow-audit-op.ts +88 -0
  462. package/src/op/converge-rule.ts +12 -10
  463. package/src/op/cron.test.ts +128 -0
  464. package/src/op/cron.ts +149 -0
  465. package/src/op/discover-root.test.ts +105 -1
  466. package/src/op/discover.test.ts +6 -2
  467. package/src/op/discover.ts +84 -16
  468. package/src/op/duration.ts +21 -0
  469. package/src/op/effect-step.test.ts +26 -11
  470. package/src/op/emulator-lifecycle.ts +1 -2
  471. package/src/op/gate.ts +155 -0
  472. package/src/op/generate-pipeline.test.ts +65 -3
  473. package/src/op/generate-pipeline.ts +19 -2
  474. package/src/op/index.ts +39 -6
  475. package/src/op/local-executor.test.ts +163 -34
  476. package/src/op/local-executor.ts +370 -102
  477. package/src/op/local-output.test.ts +95 -11
  478. package/src/op/local-output.ts +33 -4
  479. package/src/op/op-ir.test.ts +281 -0
  480. package/src/op/op-ir.ts +417 -0
  481. package/src/op/op-verb-class.ts +19 -5
  482. package/src/op/op.test.ts +26 -5
  483. package/src/op/operator.test.ts +175 -6
  484. package/src/op/operator.ts +63 -16
  485. package/src/op/receipt-store.ts +1 -1
  486. package/src/op/resource.ts +20 -2
  487. package/src/op/runtime.ts +203 -0
  488. package/src/op/runtimes/local.test.ts +181 -0
  489. package/src/op/runtimes/local.ts +212 -0
  490. package/src/op/step-output-ref.ts +15 -13
  491. package/src/op/types.ts +84 -24
  492. package/src/ownership.ts +1 -1
  493. package/src/provenance.test.ts +6 -0
  494. package/src/provenance.ts +21 -1
  495. package/src/serializer-walker.test.ts +42 -0
  496. package/src/serializer-walker.ts +21 -2
  497. package/src/stack-output.ts +6 -1
  498. package/dist/cli/handlers/op-progress.d.ts +0 -90
  499. package/dist/cli/handlers/op-progress.d.ts.map +0 -1
  500. package/dist/cli/handlers/run-client.d.ts +0 -127
  501. package/dist/cli/handlers/run-client.d.ts.map +0 -1
  502. package/dist/cli/handlers/run-report.d.ts +0 -5
  503. package/dist/cli/handlers/run-report.d.ts.map +0 -1
  504. package/dist/components/temporal-codegen-loader.d.ts +0 -27
  505. package/dist/components/temporal-codegen-loader.d.ts.map +0 -1
  506. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts +0 -37
  507. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts.map +0 -1
  508. package/src/cli/handlers/op-progress.test.ts +0 -268
  509. package/src/cli/handlers/op-progress.ts +0 -248
  510. package/src/cli/handlers/run-client.test.ts +0 -82
  511. package/src/cli/handlers/run-client.ts +0 -219
  512. package/src/cli/handlers/run-report.test.ts +0 -62
  513. package/src/cli/handlers/run-report.ts +0 -122
  514. package/src/components/temporal-codegen-loader.ts +0 -41
  515. package/src/lint/rules/comp/comp004-gate-needs-temporal.ts +0 -66
@@ -0,0 +1,85 @@
1
+ import { exec } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+
4
+ const execAsync = promisify(exec);
5
+
6
+ export interface LifecycleSnapshotArgs {
7
+ /** Environment name (e.g. "dev", "staging", "prod"). */
8
+ env: string;
9
+ }
10
+
11
+ /**
12
+ * Take a chant lifecycle snapshot for the given environment.
13
+ * Uses fastIdempotent profile — 5m timeout, 3 retries.
14
+ */
15
+ export async function lifecycleSnapshot(args: LifecycleSnapshotArgs, signal?: AbortSignal): Promise<void> {
16
+ const { stdout, stderr } = await execAsync(`chant lifecycle snapshot ${args.env}`, { signal });
17
+ if (stdout) console.log(stdout);
18
+ if (stderr) console.error(stderr);
19
+ }
20
+
21
+ export interface LifecycleDiffArgs {
22
+ /** Environment name (e.g. "dev", "staging", "prod"). */
23
+ env: string;
24
+ /**
25
+ * When true, run `chant lifecycle diff <env> --live` (queries cloud APIs).
26
+ * When false (default), run digest-only diff against the last snapshot.
27
+ */
28
+ live?: boolean;
29
+ }
30
+
31
+ export interface LifecycleDiffResult {
32
+ /** Combined stdout + stderr from the chant command. */
33
+ output: string;
34
+ /** Process exit code (0 = success). */
35
+ exitCode: number;
36
+ /**
37
+ * True when the diff output contains any drift indicators
38
+ * (MISSING / ORPHAN / DRIFTED / DISAPPEARED section headers from
39
+ * `chant lifecycle diff --live`).
40
+ */
41
+ drifted: boolean;
42
+ }
43
+
44
+ /**
45
+ * Section headers emitted by `chant lifecycle diff --live` that indicate a
46
+ * non-empty drift category. See packages/core/src/cli/handlers/state.ts.
47
+ */
48
+ const DRIFT_HEADERS = [
49
+ "MISSING",
50
+ "ORPHAN",
51
+ "DISAPPEARED",
52
+ "DRIFTED",
53
+ "ARTIFACTS ADDED",
54
+ "ARTIFACTS REMOVED",
55
+ "ARTIFACTS CHANGED",
56
+ ];
57
+
58
+ function detectDrift(output: string): boolean {
59
+ return DRIFT_HEADERS.some((h) => output.includes(`${h} (`) || output.includes(`\n${h}`));
60
+ }
61
+
62
+ /**
63
+ * Run `chant lifecycle diff <env>` and return the output + structured drift
64
+ * flag. Read-only; intended for use inside watch/observation Ops.
65
+ * Uses fastIdempotent profile.
66
+ *
67
+ * The `drifted` field is computed by scanning the output for any of the
68
+ * MISSING / ORPHAN / DRIFTED / DISAPPEARED section headers documented in
69
+ * cli/state.mdx. Pair with `outcomeAttribute: { name: "Drift", from: "drifted" }`
70
+ * on a WatchOp activity step to surface drift as the run's `Drift` outcome.
71
+ */
72
+ export async function lifecycleDiff(args: LifecycleDiffArgs, signal?: AbortSignal): Promise<LifecycleDiffResult> {
73
+ const liveFlag = args.live ? " --live" : "";
74
+ try {
75
+ const { stdout, stderr } = await execAsync(`chant lifecycle diff ${args.env}${liveFlag}`, { signal });
76
+ const output = `${stdout}${stderr}`.trim();
77
+ if (output) console.log(output);
78
+ return { output, exitCode: 0, drifted: detectDrift(output) };
79
+ } catch (err) {
80
+ const e = err as { code?: number; stdout?: string; stderr?: string };
81
+ const output = `${e.stdout ?? ""}${e.stderr ?? ""}`.trim();
82
+ if (output) console.error(output);
83
+ return { output, exitCode: e.code ?? 1, drifted: detectDrift(output) };
84
+ }
85
+ }
@@ -0,0 +1,67 @@
1
+ import { describe, test, expect } from "vitest";
2
+ import {
3
+ pipelineSupplyChainAudit,
4
+ collectPipelineRefs,
5
+ type PipelineRefResolution,
6
+ type GitlabRefResolver,
7
+ } from "./pipeline-audit";
8
+
9
+ const PIPELINE = `include:
10
+ - project: my-group/ci-templates
11
+ ref: v1.2.3
12
+ file: /build.yml
13
+ - component: gitlab.example.com/my-group/my-comp@1.0.0
14
+
15
+ build:
16
+ image:
17
+ name: node:20
18
+ script:
19
+ - npm ci
20
+ `;
21
+
22
+ function recordedResolver(records: Record<string, PipelineRefResolution>): GitlabRefResolver {
23
+ return async (kind, identifier, ref) =>
24
+ records[`${kind}:${identifier}@${ref}`] ?? { exists: true, archived: false, advisories: [] };
25
+ }
26
+
27
+ describe("collectPipelineRefs (#303)", () => {
28
+ test("collects include:project, component, and image refs", () => {
29
+ const refs = collectPipelineRefs(PIPELINE);
30
+ const ids = refs.map((r) => `${r.refKind}:${r.identifier}`);
31
+ expect(ids).toContain("include:my-group/ci-templates");
32
+ expect(ids).toContain("component:gitlab.example.com/my-group/my-comp");
33
+ expect(ids).toContain("image:node:20");
34
+ expect(refs.find((r) => r.refKind === "include")?.ref).toBe("v1.2.3");
35
+ expect(refs.find((r) => r.refKind === "component")?.ref).toBe("1.0.0");
36
+ });
37
+
38
+ test("skips variable-based images", () => {
39
+ const refs = collectPipelineRefs(`build:\n image:\n name: $CI_REGISTRY_IMAGE:latest\n`);
40
+ expect(refs.filter((r) => r.refKind === "image")).toHaveLength(0);
41
+ });
42
+ });
43
+
44
+ describe("pipelineSupplyChainAudit (#303)", () => {
45
+ test("flags unresolved, archived, and moved references", async () => {
46
+ const resolver = recordedResolver({
47
+ "include:my-group/ci-templates@v1.2.3": { exists: true, archived: true, advisories: [] },
48
+ "component:gitlab.example.com/my-group/my-comp@1.0.0": { exists: false, archived: false, advisories: [] },
49
+ "image:node:20@20": { exists: true, archived: false, advisories: ["GHSA-img"], movedTo: undefined },
50
+ });
51
+ const result = await pipelineSupplyChainAudit({ yaml: PIPELINE, resolver, mode: "merge-request" });
52
+ const kinds = result.findings.map((f) => `${f.identifier}:${f.kind}`);
53
+
54
+ expect(kinds).toContain("my-group/ci-templates:archived");
55
+ expect(kinds).toContain("gitlab.example.com/my-group/my-comp:unresolved");
56
+ expect(kinds).toContain("node:20:advisory");
57
+ expect(result.mode).toBe("merge-request");
58
+ expect(result.summary).toContain("Pipeline include/component audit");
59
+ });
60
+
61
+ test("clean pipeline yields no findings", async () => {
62
+ const resolver = recordedResolver({});
63
+ const result = await pipelineSupplyChainAudit({ yaml: PIPELINE, resolver });
64
+ expect(result.findings).toHaveLength(0);
65
+ expect(result.summary).toContain("No live drift");
66
+ });
67
+ });
@@ -0,0 +1,201 @@
1
+ /**
2
+ * pipelineSupplyChainAudit — the live counterpart to the gitlab post-synth
3
+ * include/component/image checks (#297).
4
+ *
5
+ * Some include/component checks can only be answered against a moving external
6
+ * truth: whether a pinned component/include ref still resolves upstream, whether
7
+ * the upstream project was archived or moved, or whether a new advisory now
8
+ * covers a component/image already in use. These change independent of the
9
+ * source, so they cannot live in the deterministic build — this activity owns
10
+ * *only* the checks that require live resolution.
11
+ *
12
+ * Dependency-free and primitives-only: it reads the emitted `.gitlab-ci.yml`
13
+ * (passed as a string) and resolves references through an injectable
14
+ * `GitlabRefResolver`, so `chant run` can schedule it and tests can run
15
+ * it against recorded responses with no live network.
16
+ */
17
+
18
+ /** What the audit produces on findings. For GitLab the PR mode is a merge request. */
19
+ export type PipelineAuditMode = "report" | "issue" | "merge-request";
20
+
21
+ /** The kind of reference being resolved. */
22
+ export type PipelineRefKind = "include" | "component" | "image";
23
+
24
+ /** Live resolution of a single pipeline reference against its upstream. */
25
+ export interface PipelineRefResolution {
26
+ /** Does the project/component/image (at this ref/version) still resolve? */
27
+ exists: boolean;
28
+ /** Has the upstream project been archived? */
29
+ archived: boolean;
30
+ /** Has the upstream project moved (new path), if known. */
31
+ movedTo?: string;
32
+ /** Disclosed advisory identifiers affecting this reference. */
33
+ advisories: string[];
34
+ }
35
+
36
+ /** Pluggable upstream resolver — overridden in tests with recorded responses. */
37
+ export type GitlabRefResolver = (kind: PipelineRefKind, identifier: string, ref: string) => Promise<PipelineRefResolution>;
38
+
39
+ export type PipelineAuditFindingKind = "unresolved" | "archived" | "moved" | "advisory";
40
+
41
+ export interface PipelineAuditFinding {
42
+ kind: PipelineAuditFindingKind;
43
+ refKind: PipelineRefKind;
44
+ /** project path / component address / image. */
45
+ identifier: string;
46
+ /** ref / version / tag. */
47
+ ref: string;
48
+ detail: string;
49
+ }
50
+
51
+ export interface PipelineAuditArgs {
52
+ /** One emitted `.gitlab-ci.yml` document. */
53
+ yaml?: string;
54
+ /** Several emitted documents. */
55
+ yamls?: string[];
56
+ /** Path to an emitted `.gitlab-ci.yml` to read at run time (default `.gitlab-ci.yml`). */
57
+ pipelineFile?: string;
58
+ /** What to produce. Default: report. */
59
+ mode?: PipelineAuditMode;
60
+ /** Injectable upstream resolver. Defaults to the live GitLab REST resolver. */
61
+ resolver?: GitlabRefResolver;
62
+ }
63
+
64
+ export interface PipelineAuditResult {
65
+ mode: PipelineAuditMode;
66
+ findings: PipelineAuditFinding[];
67
+ summary: string;
68
+ }
69
+
70
+ interface CollectedRef {
71
+ refKind: PipelineRefKind;
72
+ identifier: string;
73
+ ref: string;
74
+ }
75
+
76
+ /**
77
+ * Collect `include:project` (+ ref), `component:` (with @version), and `image:`
78
+ * references from a `.gitlab-ci.yml`.
79
+ */
80
+ export function collectPipelineRefs(yaml: string): CollectedRef[] {
81
+ const out: CollectedRef[] = [];
82
+
83
+ // include:\n - project: group/proj\n ref: main
84
+ const projectRe = /-\s+project:\s*['"]?([^\s'"]+)['"]?[\s\S]*?ref:\s*['"]?([^\s'"]+)['"]?/g;
85
+ let m: RegExpExecArray | null;
86
+ while ((m = projectRe.exec(yaml)) !== null) {
87
+ out.push({ refKind: "include", identifier: m[1], ref: m[2] });
88
+ }
89
+
90
+ // - component: host/group/comp@1.0.0
91
+ const componentRe = /-\s+component:\s*['"]?([^\s'"@]+)@([^\s'"]+)['"]?/g;
92
+ while ((m = componentRe.exec(yaml)) !== null) {
93
+ out.push({ refKind: "component", identifier: m[1], ref: m[2] });
94
+ }
95
+
96
+ // Images: inline `image: name` or block `image:\n name: ...` (and services).
97
+ const lines = yaml.split("\n");
98
+ const imgSeen = new Set<string>();
99
+ const addImage = (raw: string) => {
100
+ const image = raw.trim().replace(/^['"]|['"]$/g, "");
101
+ if (!image || image.includes("$") || imgSeen.has(image)) return;
102
+ imgSeen.add(image);
103
+ const at = image.lastIndexOf("@");
104
+ const colon = image.lastIndexOf(":");
105
+ const ref = at !== -1 ? image.slice(at + 1) : colon > image.lastIndexOf("/") ? image.slice(colon + 1) : "latest";
106
+ out.push({ refKind: "image", identifier: image, ref });
107
+ };
108
+ for (let i = 0; i < lines.length; i++) {
109
+ const inline = lines[i].match(/^\s+image:[ \t]+(\S.*)$/);
110
+ if (inline) { addImage(inline[1]); continue; }
111
+ if (/^\s+image:\s*$/.test(lines[i]) || /^\s+-\s+name:[ \t]+(\S.*)$/.test(lines[i])) {
112
+ const svcName = lines[i].match(/^\s+-\s+name:[ \t]+(\S.*)$/);
113
+ if (svcName) { addImage(svcName[1]); continue; }
114
+ const nameLine = lines.slice(i + 1, i + 4).find((l) => /^\s+name:[ \t]+/.test(l));
115
+ if (nameLine) addImage(nameLine.replace(/^\s+name:[ \t]+/, ""));
116
+ }
117
+ }
118
+
119
+ return out;
120
+ }
121
+
122
+ /**
123
+ * Default resolver: query the GitLab REST API for project existence / archive /
124
+ * move status. Best-effort and resilient — errors yield an "unknown" resolution
125
+ * (exists=true, no findings) rather than a false positive. Component/image
126
+ * advisory lookups are left to a configured resolver.
127
+ */
128
+ export const defaultGitlabRefResolver: GitlabRefResolver = async (kind, identifier) => {
129
+ const unknown: PipelineRefResolution = { exists: true, archived: false, advisories: [] };
130
+ if (kind === "image") return unknown; // image advisory feeds are resolver-specific
131
+ const host = "https://gitlab.com";
132
+ const project = kind === "component" ? identifier.split("/").slice(1, 3).join("/") : identifier;
133
+ try {
134
+ const resp = await fetch(`${host}/api/v4/projects/${encodeURIComponent(project)}`, {
135
+ headers: process.env.GITLAB_TOKEN ? { "PRIVATE-TOKEN": process.env.GITLAB_TOKEN } : {},
136
+ });
137
+ if (resp.status === 404) return { exists: false, archived: false, advisories: [] };
138
+ if (!resp.ok) return unknown;
139
+ const p = (await resp.json()) as { archived?: boolean };
140
+ return { exists: true, archived: !!p.archived, advisories: [] };
141
+ } catch {
142
+ return unknown;
143
+ }
144
+ };
145
+
146
+ function renderSummary(findings: PipelineAuditFinding[]): string {
147
+ if (findings.length === 0) return "## Pipeline include/component audit\n\nNo live drift detected against upstream references.\n";
148
+ let out = `## Pipeline include/component audit\n\n${findings.length} finding(s) against live upstream truth:\n\n`;
149
+ out += "| Reference | Ref | Finding | Detail |\n|---|---|---|---|\n";
150
+ for (const f of findings) out += `| ${f.identifier} (${f.refKind}) | ${f.ref} | ${f.kind} | ${f.detail} |\n`;
151
+ return out;
152
+ }
153
+
154
+ async function readFileBestEffort(path: string): Promise<string[]> {
155
+ try {
156
+ const { readFile } = await import("node:fs/promises");
157
+ return [await readFile(path, "utf-8")];
158
+ } catch {
159
+ return [];
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Resolve each include/component/image reference in the emitted pipeline against
165
+ * live upstreams and report drift.
166
+ */
167
+ export async function pipelineSupplyChainAudit(args: PipelineAuditArgs): Promise<PipelineAuditResult> {
168
+ const resolver = args.resolver ?? defaultGitlabRefResolver;
169
+ const mode = args.mode ?? "report";
170
+ let yamls = args.yamls ?? (args.yaml ? [args.yaml] : []);
171
+ if (yamls.length === 0) {
172
+ yamls = await readFileBestEffort(args.pipelineFile ?? ".gitlab-ci.yml");
173
+ }
174
+
175
+ const findings: PipelineAuditFinding[] = [];
176
+ const seen = new Set<string>();
177
+ for (const yaml of yamls) {
178
+ for (const { refKind, identifier, ref } of collectPipelineRefs(yaml)) {
179
+ const key = `${refKind}:${identifier}@${ref}`;
180
+ if (seen.has(key)) continue;
181
+ seen.add(key);
182
+
183
+ const res = await resolver(refKind, identifier, ref);
184
+ if (!res.exists) {
185
+ findings.push({ kind: "unresolved", refKind, identifier, ref, detail: `${refKind} "${identifier}@${ref}" no longer resolves upstream` });
186
+ continue;
187
+ }
188
+ if (res.archived) {
189
+ findings.push({ kind: "archived", refKind, identifier, ref, detail: `${identifier} has been archived upstream` });
190
+ }
191
+ if (res.movedTo) {
192
+ findings.push({ kind: "moved", refKind, identifier, ref, detail: `${identifier} moved to ${res.movedTo}` });
193
+ }
194
+ for (const adv of res.advisories) {
195
+ findings.push({ kind: "advisory", refKind, identifier, ref, detail: `disclosed advisory ${adv} covers ${identifier}` });
196
+ }
197
+ }
198
+ }
199
+
200
+ return { mode, findings, summary: renderSummary(findings) };
201
+ }
@@ -0,0 +1,19 @@
1
+ import { describe, test, expect } from "vitest";
2
+ import { resolve } from "node:path";
3
+ import { policyGate } from "./policy";
4
+
5
+ // The #201 org-policy example: a compliant workload + a policy pack
6
+ // (ORG-COST-CENTER all-envs, ORG-PROD-TLS prod-only). policyGate builds it and
7
+ // runs the policies — passing in dev, blocking in prod.
8
+ const orgPolicySrc = resolve(import.meta.dirname, "../../../../../lexicons/k8s/examples/org-policy/src");
9
+
10
+ describe("policyGate activity (#201 T4 — gate an apply on policy)", () => {
11
+ test("passes when policy is satisfied (no env, and dev)", async () => {
12
+ await expect(policyGate({ path: orgPolicySrc })).resolves.toBeUndefined();
13
+ await expect(policyGate({ path: orgPolicySrc, env: "dev" })).resolves.toBeUndefined();
14
+ }, 30_000);
15
+
16
+ test("blocks the apply on a prod policy violation", async () => {
17
+ await expect(policyGate({ path: orgPolicySrc, env: "prod" })).rejects.toThrow(/ORG-PROD-TLS/);
18
+ }, 30_000);
19
+ });
@@ -0,0 +1,37 @@
1
+ import { nonRetryableFailure } from "../activity-failure";
2
+ import { evaluateProjectPolicies } from "../../lint/policy";
3
+
4
+ export interface PolicyGateArgs {
5
+ /** Project/source directory to build and evaluate. Default ".". */
6
+ path?: string;
7
+ /** Environment for policy evaluation (falls back to `ownership.env`). */
8
+ env?: string;
9
+ }
10
+
11
+ /**
12
+ * Gate an apply on organizational policy: build the project, run its
13
+ * `lint.policies` over the resolved resources, and **block** on any violation.
14
+ * Place it as a step before the apply phase — a violation fails the run
15
+ * (non-retryable; the `policyCheck` profile is single-attempt) so nothing is
16
+ * applied. A clean evaluation passes through.
17
+ *
18
+ * A plain activity on every runtime, not a `gate` step.
19
+ */
20
+ export async function policyGate(args: PolicyGateArgs, _signal?: AbortSignal): Promise<void> {
21
+ const { violations, suppressed, env } = await evaluateProjectPolicies({ path: args.path ?? ".", env: args.env });
22
+ if (violations.length > 0) {
23
+ const summary = violations
24
+ .map((v) => `[${v.checkId}]${v.entity ? ` ${v.entity}:` : ""} ${v.message}`)
25
+ .join("; ");
26
+ throw nonRetryableFailure(
27
+ `Organizational policy blocked the apply — ${violations.length} violation(s): ${summary}`,
28
+ "PolicyViolation",
29
+ );
30
+ }
31
+ // chant #1138 — a check `lint.rules` turned "off" is counted here rather
32
+ // than vanishing silently, mirroring `chant build`'s own suppressed-count line.
33
+ if (suppressed.length > 0) {
34
+ console.log(`[policy] ${suppressed.length} finding(s) suppressed via lint.rules`);
35
+ }
36
+ console.log(`[policy] ${env ? `env=${env}: ` : ""}no violations — apply may proceed`);
37
+ }
@@ -0,0 +1,81 @@
1
+ import { describe, test, expect } from "vitest";
2
+ import { reconcilePr, reconcileSummary, reconcileBranchName, entriesFromPlan } from "./reconcile";
3
+
4
+ const entries = [
5
+ { name: "bucket", action: "adopt", type: "AWS::S3::Bucket" },
6
+ { name: "queue", action: "update", type: "AWS::SQS::Queue" },
7
+ ];
8
+
9
+ describe("reconcileBranchName (#122)", () => {
10
+ test("deterministic, slugified per env", () => {
11
+ expect(reconcileBranchName("prod")).toBe("chant/reconcile-prod");
12
+ expect(reconcileBranchName("us-east/1")).toBe("chant/reconcile-us-east-1");
13
+ });
14
+ });
15
+
16
+ describe("reconcileSummary (#122)", () => {
17
+ test("summarizes which entries triggered the reconcile", () => {
18
+ const body = reconcileSummary("prod", entries);
19
+ expect(body).toContain("live environment `prod`");
20
+ expect(body).toContain("| bucket | adopt | AWS::S3::Bucket |");
21
+ expect(body).toContain("| queue | update | AWS::SQS::Queue |");
22
+ });
23
+
24
+ test("handles an empty entry set", () => {
25
+ expect(reconcileSummary("prod", [])).toContain("_(none)_");
26
+ });
27
+ });
28
+
29
+ describe("entriesFromPlan (#123)", () => {
30
+ test("maps a ChangeSet, dropping noop entries", () => {
31
+ const plan = JSON.stringify({
32
+ env: "prod",
33
+ entries: [
34
+ { name: "a", action: "create", type: "T1", evidence: {}, ownership: "unknown" },
35
+ { name: "b", action: "noop", type: "T2", evidence: {}, ownership: "unknown" },
36
+ { name: "c", action: "delete", type: "T3", evidence: {}, ownership: "owned" },
37
+ ],
38
+ });
39
+ expect(entriesFromPlan(plan)).toEqual([
40
+ { name: "a", action: "create", type: "T1" },
41
+ { name: "c", action: "delete", type: "T3" },
42
+ ]);
43
+ });
44
+
45
+ test("tolerates an empty / entry-less plan", () => {
46
+ expect(entriesFromPlan(JSON.stringify({ env: "prod" }))).toEqual([]);
47
+ });
48
+ });
49
+
50
+ describe("reconcilePr report mode (#122)", () => {
51
+ test("returns the summary without any git/network IO", async () => {
52
+ const result = await reconcilePr({ env: "prod", entries, mode: "report" });
53
+ expect(result.mode).toBe("report");
54
+ expect(result.prUrl).toBeUndefined();
55
+ expect(result.branch).toBeUndefined();
56
+ expect(result.summary).toContain("| bucket | adopt | AWS::S3::Bucket |");
57
+ expect(result.entries).toEqual(entries);
58
+ });
59
+ });
60
+
61
+ describe("reconcilePr pre-built body (#2087)", () => {
62
+ test("a caller-supplied body is used verbatim, in place of the change-set table", async () => {
63
+ const plan = "Terraform will perform the following actions:\n\n # null_resource.first will be created";
64
+ const result = await reconcilePr({ env: "app", mode: "report", body: plan });
65
+ expect(result.summary).toBe(plan);
66
+ expect(result.summary).not.toContain("| Entry | Action | Type |");
67
+ });
68
+
69
+ test("supplying a body derives no plan, so nothing shells to `chant lifecycle plan`", async () => {
70
+ // No `entries`, no mock, no network: if the derivation still ran this
71
+ // would spawn `chant lifecycle plan --json` and reject.
72
+ const result = await reconcilePr({ env: "app", mode: "report", body: "drift" });
73
+ expect(result.entries).toEqual([]);
74
+ });
75
+
76
+ test("explicit entries still ride alongside a supplied body", async () => {
77
+ const result = await reconcilePr({ env: "prod", mode: "report", entries, body: "drift" });
78
+ expect(result.summary).toBe("drift");
79
+ expect(result.entries).toEqual(entries);
80
+ });
81
+ });
@@ -0,0 +1,187 @@
1
+ import { exec } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+
4
+ const execAsync = promisify(exec);
5
+
6
+ /** What the reconcile activity does with the regenerated source. */
7
+ export type ReconcileMode = "pull-request" | "issue" | "report";
8
+
9
+ /** A change-set entry that triggered reconciliation. */
10
+ export interface ReconcileEntry {
11
+ /** chant entity name. */
12
+ name: string;
13
+ /** create | update | delete | adopt | noop (from `chant lifecycle plan`). */
14
+ action: string;
15
+ /** Resource type, when known. */
16
+ type?: string;
17
+ }
18
+
19
+ export interface ReconcilePrArgs {
20
+ /** Environment to reconcile from (passed to `chant import --from`). */
21
+ env: string;
22
+ /**
23
+ * The change-set entries that triggered this reconcile. Omit to derive them
24
+ * from `chant lifecycle plan <env> --json` at run time — the form used inside
25
+ * an Op, where the entries aren't known until the activity runs.
26
+ */
27
+ entries?: ReconcileEntry[];
28
+ /** What to produce. Default: pull-request. */
29
+ mode?: ReconcileMode;
30
+ /** Output directory for regenerated source. Default: ./infra. */
31
+ output?: string;
32
+ /** Branch to open the PR from. Default: chant/reconcile-<env>. */
33
+ branch?: string;
34
+ /** Restrict live import to chant-owned resources. */
35
+ owned?: boolean;
36
+ /** PR / issue title. Default derived from env. */
37
+ title?: string;
38
+ /**
39
+ * A finding body built by the caller, used verbatim as the issue/PR body in
40
+ * place of {@link reconcileSummary} (chant #2087).
41
+ *
42
+ * The summary this activity writes itself is a change-set table, which is
43
+ * the right artifact when the finding IS a change set. Some observe-dial
44
+ * Ops already hold a better one: `TerraformWatchOp` carries the
45
+ * `terraform plan -no-color` render its own Plan step produced, and
46
+ * re-deriving that from `chant lifecycle plan` here would both lose the
47
+ * plan and run a second, differently-timed read.
48
+ *
49
+ * Supplying it also suppresses the `chant lifecycle plan --json` derivation
50
+ * that fills `entries`: a caller that already knows what it wants to say
51
+ * is not asking this activity to go and find out.
52
+ */
53
+ body?: string;
54
+ }
55
+
56
+ export interface ReconcileResult {
57
+ mode: ReconcileMode;
58
+ /** Branch created (pull-request mode). */
59
+ branch?: string;
60
+ /** Opened PR URL (pull-request mode). */
61
+ prUrl?: string;
62
+ /** Opened issue URL (issue mode). */
63
+ issueUrl?: string;
64
+ /** The markdown summary used as the PR/issue body. */
65
+ summary: string;
66
+ /** The entries that triggered the reconcile. */
67
+ entries: ReconcileEntry[];
68
+ }
69
+
70
+ /** Default branch name for a reconcile PR. Deterministic — no timestamp. */
71
+ export function reconcileBranchName(env: string): string {
72
+ const safe = env.replace(/[^a-zA-Z0-9._-]+/g, "-");
73
+ return `chant/reconcile-${safe}`;
74
+ }
75
+
76
+ /**
77
+ * Build the markdown body summarizing which change-set entries triggered the
78
+ * reconcile. Pure — used as the PR/issue body and returned in `report` mode.
79
+ */
80
+ export function reconcileSummary(env: string, entries: ReconcileEntry[]): string {
81
+ const lines = [
82
+ `Reconcile from live environment \`${env}\`.`,
83
+ "",
84
+ "This PR regenerates chant TypeScript from live state to close the gap between the cloud and source. It was triggered by the following change-set entries:",
85
+ "",
86
+ "| Entry | Action | Type |",
87
+ "|---|---|---|",
88
+ ];
89
+ for (const e of entries) {
90
+ lines.push(`| ${e.name} | ${e.action} | ${e.type ?? ""} |`);
91
+ }
92
+ if (entries.length === 0) {
93
+ lines.push("| _(none)_ | | |");
94
+ }
95
+ lines.push("");
96
+ lines.push("Review the diff before merging — live import may surface values that need redaction.");
97
+ return lines.join("\n");
98
+ }
99
+
100
+ function shellQuote(s: string): string {
101
+ return `'${s.replace(/'/g, "'\\''")}'`;
102
+ }
103
+
104
+ /**
105
+ * Map a `chant lifecycle plan --json` ChangeSet to reconcile entries, dropping
106
+ * `noop` entries (nothing to reconcile). Pure — exported for testing.
107
+ */
108
+ export function entriesFromPlan(planJson: string): ReconcileEntry[] {
109
+ const cs = JSON.parse(planJson) as {
110
+ entries?: Array<{ name: string; action: string; type?: string }>;
111
+ };
112
+ return (cs.entries ?? [])
113
+ .filter((e) => e.action !== "noop")
114
+ .map((e) => ({ name: e.name, action: e.action, type: e.type }));
115
+ }
116
+
117
+ /** Derive reconcile entries from `chant lifecycle plan`. */
118
+ async function derivePlanEntries(
119
+ env: string,
120
+ owned: boolean,
121
+ signal?: AbortSignal,
122
+ ): Promise<ReconcileEntry[]> {
123
+ const ownedFlag = owned ? " --owned" : "";
124
+ const { stdout } = await execAsync(
125
+ `chant lifecycle plan ${shellQuote(env)}${ownedFlag} --json`,
126
+ { signal },
127
+ );
128
+ return entriesFromPlan(stdout);
129
+ }
130
+
131
+ /**
132
+ * Reconcile activity: turn regenerated TypeScript into a reviewable artifact.
133
+ *
134
+ * - `report` — return the summary only; no git, no network.
135
+ * - `issue` — open a GitHub issue describing the drift (no code change).
136
+ * - `pull-request` — create a branch, regenerate source via
137
+ * `chant import --from <env>`, commit, push, and open a PR whose diff is the
138
+ * regenerated TypeScript. Never commits to the main branch.
139
+ *
140
+ * The body is {@link reconcileSummary}'s change-set table unless `args.body`
141
+ * supplies one, in which case that text is used verbatim and no plan is
142
+ * derived (chant #2087).
143
+ *
144
+ * Requires `chant` and (for non-report modes) `gh`/`git` in the environment.
145
+ */
146
+ export async function reconcilePr(args: ReconcilePrArgs, signal?: AbortSignal): Promise<ReconcileResult> {
147
+ const mode = args.mode ?? "pull-request";
148
+ const owned = args.owned ?? false;
149
+ // A caller-supplied body means the finding is already written, so there is
150
+ // nothing for `chant lifecycle plan` to tell us (#2087).
151
+ const entries = args.entries ?? (args.body !== undefined ? [] : await derivePlanEntries(args.env, owned, signal));
152
+ const summary = args.body ?? reconcileSummary(args.env, entries);
153
+ const title = args.title ?? `Reconcile ${args.env}: ${entries.length} change(s) from live`;
154
+
155
+ if (mode === "report") {
156
+ return { mode, summary, entries };
157
+ }
158
+
159
+ if (mode === "issue") {
160
+ const { stdout } = await execAsync(
161
+ `gh issue create --title ${shellQuote(title)} --body ${shellQuote(summary)}`,
162
+ { signal },
163
+ );
164
+ return { mode, summary, entries, issueUrl: stdout.trim() };
165
+ }
166
+
167
+ // pull-request
168
+ const branch = args.branch ?? reconcileBranchName(args.env);
169
+ const output = args.output ?? "./infra";
170
+ const ownedFlag = owned ? " --owned" : "";
171
+
172
+ // Never touch the main branch: cut a fresh branch first.
173
+ await execAsync(`git checkout -b ${shellQuote(branch)}`, { signal });
174
+ await execAsync(
175
+ `chant import --from ${shellQuote(args.env)}${ownedFlag} --output ${shellQuote(output)} --force`,
176
+ { signal },
177
+ );
178
+ await execAsync(`git add ${shellQuote(output)}`, { signal });
179
+ await execAsync(`git commit -m ${shellQuote(title)}`, { signal });
180
+ await execAsync(`git push -u origin ${shellQuote(branch)}`, { signal });
181
+ const { stdout } = await execAsync(
182
+ `gh pr create --title ${shellQuote(title)} --body ${shellQuote(summary)} --head ${shellQuote(branch)}`,
183
+ { signal },
184
+ );
185
+
186
+ return { mode, branch, summary, entries, prUrl: stdout.trim() };
187
+ }