@intentius/chant 0.57.0 → 0.59.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 (558) 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 +36 -1
  4. package/dist/audit/core.d.ts.map +1 -1
  5. package/dist/audit/discover.d.ts +26 -9
  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/carve-bridge.d.ts.map +1 -1
  34. package/dist/cli/commands/check-lexicon-docs.d.ts +1 -1
  35. package/dist/cli/commands/check-lexicon-examples.d.ts +16 -6
  36. package/dist/cli/commands/check-lexicon-examples.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 +13 -0
  44. package/dist/cli/commands/lint.d.ts.map +1 -1
  45. package/dist/cli/commands/onboard.d.ts +2 -0
  46. package/dist/cli/commands/onboard.d.ts.map +1 -1
  47. package/dist/cli/handlers/build.d.ts.map +1 -1
  48. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  49. package/dist/cli/handlers/operator.d.ts +82 -16
  50. package/dist/cli/handlers/operator.d.ts.map +1 -1
  51. package/dist/cli/handlers/run.d.ts +49 -42
  52. package/dist/cli/handlers/run.d.ts.map +1 -1
  53. package/dist/cli/main.d.ts +12 -1
  54. package/dist/cli/main.d.ts.map +1 -1
  55. package/dist/cli/mcp/op-tools.d.ts +16 -1
  56. package/dist/cli/mcp/op-tools.d.ts.map +1 -1
  57. package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
  58. package/dist/cli/mcp/resources/context.d.ts.map +1 -1
  59. package/dist/cli/plugins.d.ts +22 -0
  60. package/dist/cli/plugins.d.ts.map +1 -1
  61. package/dist/cli/registry.d.ts +40 -9
  62. package/dist/cli/registry.d.ts.map +1 -1
  63. package/dist/codegen/generate-post-synth-barrel.d.ts +1 -1
  64. package/dist/components/auto-release.d.ts +10 -20
  65. package/dist/components/auto-release.d.ts.map +1 -1
  66. package/dist/components/capability.d.ts +4 -4
  67. package/dist/components/cli-support.d.ts +24 -22
  68. package/dist/components/cli-support.d.ts.map +1 -1
  69. package/dist/components/component.d.ts +21 -6
  70. package/dist/components/component.d.ts.map +1 -1
  71. package/dist/components/driver-output.d.ts.map +1 -1
  72. package/dist/components/driver.d.ts +73 -37
  73. package/dist/components/driver.d.ts.map +1 -1
  74. package/dist/components/index.d.ts +1 -2
  75. package/dist/components/index.d.ts.map +1 -1
  76. package/dist/components/manifest-persistence.d.ts +1 -9
  77. package/dist/components/manifest-persistence.d.ts.map +1 -1
  78. package/dist/components/run-progress.d.ts +6 -6
  79. package/dist/components/run-progress.d.ts.map +1 -1
  80. package/dist/components/verbs/run-agent.d.ts +10 -22
  81. package/dist/components/verbs/run-agent.d.ts.map +1 -1
  82. package/dist/components/verbs/verify.d.ts +2 -2
  83. package/dist/deep-observation.d.ts +17 -0
  84. package/dist/deep-observation.d.ts.map +1 -1
  85. package/dist/detectLexicon.d.ts +13 -0
  86. package/dist/detectLexicon.d.ts.map +1 -1
  87. package/dist/discovery/files.d.ts +4 -4
  88. package/dist/discovery/fold-import.d.ts.map +1 -1
  89. package/dist/discovery/param-deps.d.ts +33 -0
  90. package/dist/discovery/param-deps.d.ts.map +1 -1
  91. package/dist/discovery/sandbox/bundle.d.ts.map +1 -1
  92. package/dist/discovery/sandbox/config-wire.d.ts +2 -2
  93. package/dist/fold/foldable-helpers.d.ts +5 -1
  94. package/dist/fold/foldable-helpers.d.ts.map +1 -1
  95. package/dist/fold-provenance.d.ts +236 -0
  96. package/dist/fold-provenance.d.ts.map +1 -0
  97. package/dist/graph-ops.d.ts +1 -1
  98. package/dist/held-elsewhere.d.ts +85 -0
  99. package/dist/held-elsewhere.d.ts.map +1 -0
  100. package/dist/index.d.ts +3 -0
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/kubectl-context.d.ts +3 -3
  103. package/dist/lexicon-config.d.ts +4 -4
  104. package/dist/lexicon-plugin-helpers.d.ts +1 -1
  105. package/dist/lexicon.d.ts +273 -16
  106. package/dist/lexicon.d.ts.map +1 -1
  107. package/dist/lifecycle/change-set.d.ts +13 -0
  108. package/dist/lifecycle/change-set.d.ts.map +1 -1
  109. package/dist/lifecycle/deep-diff.d.ts +133 -6
  110. package/dist/lifecycle/deep-diff.d.ts.map +1 -1
  111. package/dist/lifecycle/deep-observe.d.ts.map +1 -1
  112. package/dist/lifecycle/gate-ledger.d.ts +79 -3
  113. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  114. package/dist/lifecycle/index.d.ts +1 -0
  115. package/dist/lifecycle/index.d.ts.map +1 -1
  116. package/dist/lifecycle/release-ledger.d.ts +2 -2
  117. package/dist/lifecycle/run-ledger.d.ts +53 -0
  118. package/dist/lifecycle/run-ledger.d.ts.map +1 -0
  119. package/dist/lifecycle/symptoms.d.ts +3 -3
  120. package/dist/lifecycle/symptoms.d.ts.map +1 -1
  121. package/dist/lint/config.d.ts +81 -2
  122. package/dist/lint/config.d.ts.map +1 -1
  123. package/dist/lint/post-synth.d.ts +55 -2
  124. package/dist/lint/post-synth.d.ts.map +1 -1
  125. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts +1 -1
  126. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts.map +1 -1
  127. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts +1 -1
  128. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts.map +1 -1
  129. package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts +44 -0
  130. package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts.map +1 -0
  131. package/dist/lint/rules/comp/index.d.ts +1 -1
  132. package/dist/lint/rules/comp/index.d.ts.map +1 -1
  133. package/dist/lint/rules/op/index.d.ts +16 -0
  134. package/dist/lint/rules/op/index.d.ts.map +1 -0
  135. package/dist/lint/rules/op/ops012-activity-contract.d.ts +27 -0
  136. package/dist/lint/rules/op/ops012-activity-contract.d.ts.map +1 -0
  137. package/dist/lint/rules/op/ops013-step-output-ref.d.ts +61 -0
  138. package/dist/lint/rules/op/ops013-step-output-ref.d.ts.map +1 -0
  139. package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts +55 -0
  140. package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts.map +1 -0
  141. package/dist/lint/rules/op/support.d.ts +12 -0
  142. package/dist/lint/rules/op/support.d.ts.map +1 -0
  143. package/dist/lint/suppressions.d.ts +108 -0
  144. package/dist/lint/suppressions.d.ts.map +1 -0
  145. package/dist/live-endpoint.d.ts +1 -1
  146. package/dist/op/activities/activity-contracts.d.ts +54 -0
  147. package/dist/op/activities/activity-contracts.d.ts.map +1 -0
  148. package/dist/op/activities/apply.d.ts +262 -0
  149. package/dist/op/activities/apply.d.ts.map +1 -0
  150. package/dist/op/activities/build.d.ts +13 -0
  151. package/dist/op/activities/build.d.ts.map +1 -0
  152. package/dist/op/activities/converge.d.ts +110 -0
  153. package/dist/op/activities/converge.d.ts.map +1 -0
  154. package/dist/op/activities/env-teardown.d.ts +76 -0
  155. package/dist/op/activities/env-teardown.d.ts.map +1 -0
  156. package/dist/op/activities/guard-validate.d.ts +64 -0
  157. package/dist/op/activities/guard-validate.d.ts.map +1 -0
  158. package/dist/op/activities/http-check.d.ts +34 -0
  159. package/dist/op/activities/http-check.d.ts.map +1 -0
  160. package/dist/op/activities/index.d.ts +31 -0
  161. package/dist/op/activities/index.d.ts.map +1 -0
  162. package/dist/op/activities/lexicon-upgrade.d.ts +182 -0
  163. package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -0
  164. package/dist/op/activities/lifecycle.d.ts +42 -0
  165. package/dist/op/activities/lifecycle.d.ts.map +1 -0
  166. package/dist/op/activities/pipeline-audit.d.ts +84 -0
  167. package/dist/op/activities/pipeline-audit.d.ts.map +1 -0
  168. package/dist/op/activities/policy.d.ts +17 -0
  169. package/dist/op/activities/policy.d.ts.map +1 -0
  170. package/dist/op/activities/reconcile.d.ts +149 -0
  171. package/dist/op/activities/reconcile.d.ts.map +1 -0
  172. package/dist/op/activities/shell.d.ts +13 -0
  173. package/dist/op/activities/shell.d.ts.map +1 -0
  174. package/dist/op/activities/teardown.d.ts +10 -0
  175. package/dist/op/activities/teardown.d.ts.map +1 -0
  176. package/dist/op/activities/util.d.ts +7 -0
  177. package/dist/op/activities/util.d.ts.map +1 -0
  178. package/dist/op/activities/wait.d.ts +28 -0
  179. package/dist/op/activities/wait.d.ts.map +1 -0
  180. package/dist/op/activities/workflow-audit.d.ts +92 -0
  181. package/dist/op/activities/workflow-audit.d.ts.map +1 -0
  182. package/dist/op/activity-contract-registry.d.ts +76 -0
  183. package/dist/op/activity-contract-registry.d.ts.map +1 -0
  184. package/dist/op/activity-contract.d.ts +25 -18
  185. package/dist/op/activity-contract.d.ts.map +1 -1
  186. package/dist/op/activity-failure.d.ts +28 -0
  187. package/dist/op/activity-failure.d.ts.map +1 -0
  188. package/dist/op/activity-profiles.d.ts +118 -0
  189. package/dist/op/activity-profiles.d.ts.map +1 -0
  190. package/dist/op/activity-registry.d.ts +22 -38
  191. package/dist/op/activity-registry.d.ts.map +1 -1
  192. package/dist/op/activity-runtime.d.ts +3 -14
  193. package/dist/op/activity-runtime.d.ts.map +1 -1
  194. package/dist/op/builders.d.ts +85 -30
  195. package/dist/op/builders.d.ts.map +1 -1
  196. package/dist/op/change-signal.d.ts +91 -0
  197. package/dist/op/change-signal.d.ts.map +1 -0
  198. package/dist/op/composites/apply-op.d.ts +118 -0
  199. package/dist/op/composites/apply-op.d.ts.map +1 -0
  200. package/dist/op/composites/converge-op.d.ts +120 -0
  201. package/dist/op/composites/converge-op.d.ts.map +1 -0
  202. package/dist/op/composites/index.d.ts +26 -0
  203. package/dist/op/composites/index.d.ts.map +1 -0
  204. package/dist/op/composites/lexicon-upgrade-op.d.ts +78 -0
  205. package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -0
  206. package/dist/op/composites/pipeline-audit-op.d.ts +50 -0
  207. package/dist/op/composites/pipeline-audit-op.d.ts.map +1 -0
  208. package/dist/op/composites/reconcile-op.d.ts +57 -0
  209. package/dist/op/composites/reconcile-op.d.ts.map +1 -0
  210. package/dist/op/composites/watch-op.d.ts +60 -0
  211. package/dist/op/composites/watch-op.d.ts.map +1 -0
  212. package/dist/op/composites/workflow-audit-op.d.ts +57 -0
  213. package/dist/op/composites/workflow-audit-op.d.ts.map +1 -0
  214. package/dist/op/converge-rule.d.ts +12 -10
  215. package/dist/op/converge-rule.d.ts.map +1 -1
  216. package/dist/op/cron.d.ts +51 -0
  217. package/dist/op/cron.d.ts.map +1 -0
  218. package/dist/op/discover.d.ts +9 -0
  219. package/dist/op/discover.d.ts.map +1 -1
  220. package/dist/op/duration.d.ts +11 -0
  221. package/dist/op/duration.d.ts.map +1 -0
  222. package/dist/op/emulator-lifecycle.d.ts.map +1 -1
  223. package/dist/op/gate-name.d.ts +40 -0
  224. package/dist/op/gate-name.d.ts.map +1 -0
  225. package/dist/op/gate-summary.d.ts +43 -0
  226. package/dist/op/gate-summary.d.ts.map +1 -0
  227. package/dist/op/gate.d.ts +90 -0
  228. package/dist/op/gate.d.ts.map +1 -0
  229. package/dist/op/generate-pipeline.d.ts +9 -0
  230. package/dist/op/generate-pipeline.d.ts.map +1 -1
  231. package/dist/op/index.d.ts +29 -9
  232. package/dist/op/index.d.ts.map +1 -1
  233. package/dist/op/local-executor.d.ts +114 -28
  234. package/dist/op/local-executor.d.ts.map +1 -1
  235. package/dist/op/local-output.d.ts +11 -1
  236. package/dist/op/local-output.d.ts.map +1 -1
  237. package/dist/op/op-ir.d.ts +185 -0
  238. package/dist/op/op-ir.d.ts.map +1 -0
  239. package/dist/op/op-verb-class.d.ts +10 -2
  240. package/dist/op/op-verb-class.d.ts.map +1 -1
  241. package/dist/op/operator.d.ts +93 -5
  242. package/dist/op/operator.d.ts.map +1 -1
  243. package/dist/op/receipt-store.d.ts +1 -1
  244. package/dist/op/receipt-store.d.ts.map +1 -1
  245. package/dist/op/resource.d.ts +15 -1
  246. package/dist/op/resource.d.ts.map +1 -1
  247. package/dist/op/runtime.d.ts +200 -0
  248. package/dist/op/runtime.d.ts.map +1 -0
  249. package/dist/op/runtimes/local.d.ts +29 -0
  250. package/dist/op/runtimes/local.d.ts.map +1 -0
  251. package/dist/op/step-output-ref.d.ts +15 -13
  252. package/dist/op/step-output-ref.d.ts.map +1 -1
  253. package/dist/op/types.d.ts +96 -29
  254. package/dist/op/types.d.ts.map +1 -1
  255. package/dist/ownership.d.ts +1 -1
  256. package/dist/provenance.d.ts +21 -0
  257. package/dist/provenance.d.ts.map +1 -1
  258. package/dist/serializer-walker.d.ts.map +1 -1
  259. package/dist/terraform/bridge.d.ts +26 -7
  260. package/dist/terraform/bridge.d.ts.map +1 -1
  261. package/dist/terraform/carve-provider.d.ts +28 -2
  262. package/dist/terraform/carve-provider.d.ts.map +1 -1
  263. package/dist/terraform/data-source-shape.d.ts +77 -0
  264. package/dist/terraform/data-source-shape.d.ts.map +1 -0
  265. package/dist/terraform/graph.d.ts.map +1 -1
  266. package/dist/terraform/providers/kubernetes.d.ts +5 -0
  267. package/dist/terraform/providers/kubernetes.d.ts.map +1 -1
  268. package/dist/terraform/tier-map.d.ts +16 -4
  269. package/dist/terraform/tier-map.d.ts.map +1 -1
  270. package/dist/terraform/types.d.ts +8 -0
  271. package/dist/terraform/types.d.ts.map +1 -1
  272. package/package.json +6 -1
  273. package/src/audit/catalog.test.ts +79 -3
  274. package/src/audit/catalog.ts +111 -13
  275. package/src/audit/core.test.ts +26 -0
  276. package/src/audit/core.ts +84 -18
  277. package/src/audit/discover.test.ts +39 -0
  278. package/src/audit/discover.ts +202 -18
  279. package/src/audit/lineage.ts +100 -0
  280. package/src/audit/prior-art.ts +153 -0
  281. package/src/audit/proof.test.ts +67 -0
  282. package/src/audit/proof.ts +70 -0
  283. package/src/audit/report-html.ts +9 -4
  284. package/src/audit/report-model.ts +16 -2
  285. package/src/audit/report.test.ts +33 -0
  286. package/src/audit/report.ts +5 -3
  287. package/src/audit/rules-doc.test.ts +34 -2
  288. package/src/audit/rules-doc.ts +82 -9
  289. package/src/audit/secrets.ts +15 -3
  290. package/src/audit/terraform-state.test.ts +144 -0
  291. package/src/audit/terraform-state.ts +131 -0
  292. package/src/build.ts +31 -1
  293. package/src/claimed-fields.test.ts +145 -0
  294. package/src/claimed-fields.ts +208 -0
  295. package/src/cli/build-params-cli.ts +2 -2
  296. package/src/cli/command-group.ts +14 -0
  297. package/src/cli/commands/__fixtures__/audit-terraform/infra/.terraform/providers/lock.txt +1 -0
  298. package/src/cli/commands/__fixtures__/audit-terraform/infra/main.tf +1 -0
  299. package/src/cli/commands/__fixtures__/audit-terraform/infra/modules/foo/main.tf +1 -0
  300. package/src/cli/commands/__fixtures__/audit-terraform/infra/variables.tf +3 -0
  301. package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +13 -4
  302. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/docs-cli.ts +4 -0
  303. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/package.ts +1 -0
  304. package/src/cli/commands/__fixtures__/init-lexicon-output/src/package-cli.ts +21 -0
  305. package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +1 -0
  306. package/src/cli/commands/audit.test.ts +191 -11
  307. package/src/cli/commands/audit.ts +78 -28
  308. package/src/cli/commands/build.test.ts +330 -9
  309. package/src/cli/commands/build.ts +170 -24
  310. package/src/cli/commands/carve-bridge.test.ts +237 -14
  311. package/src/cli/commands/carve-bridge.ts +21 -17
  312. package/src/cli/commands/carve-emit-k8s.test.ts +17 -10
  313. package/src/cli/commands/check-lexicon-docs.ts +1 -1
  314. package/src/cli/commands/check-lexicon-examples.test.ts +29 -1
  315. package/src/cli/commands/check-lexicon-examples.ts +42 -10
  316. package/src/cli/commands/init-lexicon/templates/codegen.ts +9 -0
  317. package/src/cli/commands/init-lexicon/templates/project.ts +44 -4
  318. package/src/cli/commands/init-lexicon.test.ts +97 -2
  319. package/src/cli/commands/init-lexicon.ts +4 -2
  320. package/src/cli/commands/init.test.ts +35 -1
  321. package/src/cli/commands/init.ts +14 -3
  322. package/src/cli/commands/lint.test.ts +194 -1
  323. package/src/cli/commands/lint.ts +205 -7
  324. package/src/cli/commands/onboard.test.ts +75 -0
  325. package/src/cli/commands/onboard.ts +54 -23
  326. package/src/cli/handlers/build.test.ts +81 -1
  327. package/src/cli/handlers/build.ts +15 -1
  328. package/src/cli/handlers/graph.test.ts +3 -3
  329. package/src/cli/handlers/lifecycle.test.ts +195 -12
  330. package/src/cli/handlers/lifecycle.ts +123 -6
  331. package/src/cli/handlers/misc.ts +1 -1
  332. package/src/cli/handlers/operator.test.ts +183 -11
  333. package/src/cli/handlers/operator.ts +341 -55
  334. package/src/cli/handlers/run.test.ts +568 -1295
  335. package/src/cli/handlers/run.ts +446 -912
  336. package/src/cli/main.test.ts +22 -16
  337. package/src/cli/main.ts +66 -33
  338. package/src/cli/mcp/op-tools.ts +147 -88
  339. package/src/cli/mcp/resource-handlers.ts +10 -37
  340. package/src/cli/mcp/resources/context.ts +11 -12
  341. package/src/cli/mcp/server.test.ts +25 -27
  342. package/src/cli/mcp/server.ts +2 -2
  343. package/src/cli/plugins.test.ts +68 -2
  344. package/src/cli/plugins.ts +39 -0
  345. package/src/cli/registry.ts +40 -9
  346. package/src/codegen/docs.ts +1 -1
  347. package/src/codegen/generate-post-synth-barrel.test.ts +1 -1
  348. package/src/codegen/generate-post-synth-barrel.ts +1 -1
  349. package/src/components/README.md +4 -3
  350. package/src/components/__fixtures__/neo4j-fanout.json +1 -1
  351. package/src/components/auto-release.test.ts +4 -20
  352. package/src/components/auto-release.ts +17 -42
  353. package/src/components/capability.ts +4 -4
  354. package/src/components/cli-support.test.ts +28 -12
  355. package/src/components/cli-support.ts +60 -47
  356. package/src/components/component-schema.test.ts +18 -2
  357. package/src/components/component.schema.json +18 -5
  358. package/src/components/component.test.ts +2 -2
  359. package/src/components/component.ts +27 -7
  360. package/src/components/config-defaults.test.ts +2 -2
  361. package/src/components/driver-output.ts +14 -2
  362. package/src/components/driver.test.ts +109 -20
  363. package/src/components/driver.ts +201 -84
  364. package/src/components/index.ts +1 -6
  365. package/src/components/manifest-persistence.test.ts +2 -20
  366. package/src/components/manifest-persistence.ts +4 -24
  367. package/src/components/pilots/neo4j-fanout.pilot.ts +3 -3
  368. package/src/components/pilots/pilots-lint-clean.test.ts +11 -10
  369. package/src/components/run-progress.ts +6 -6
  370. package/src/components/verbs/run-agent.test.ts +19 -0
  371. package/src/components/verbs/run-agent.ts +12 -24
  372. package/src/components/verbs/verify.ts +2 -2
  373. package/src/deep-observation.test.ts +75 -0
  374. package/src/deep-observation.ts +48 -0
  375. package/src/detectLexicon.ts +18 -1
  376. package/src/discovery/collect.test.ts +3 -3
  377. package/src/discovery/files.ts +4 -4
  378. package/src/discovery/fold-import.test.ts +1 -1
  379. package/src/discovery/fold-import.ts +105 -2
  380. package/src/discovery/param-deps.test.ts +112 -1
  381. package/src/discovery/param-deps.ts +193 -0
  382. package/src/discovery/sandbox/bundle.ts +15 -1
  383. package/src/discovery/sandbox/config-wire.test.ts +2 -2
  384. package/src/discovery/sandbox/config-wire.ts +2 -2
  385. package/src/fold/fold.test.ts +56 -0
  386. package/src/fold/foldable-helpers.ts +82 -2
  387. package/src/fold-provenance-exclusion.test.ts +186 -0
  388. package/src/fold-provenance.test.ts +495 -0
  389. package/src/fold-provenance.ts +441 -0
  390. package/src/graph-ops.test.ts +3 -3
  391. package/src/graph-ops.ts +1 -1
  392. package/src/held-elsewhere.test.ts +102 -0
  393. package/src/held-elsewhere.ts +114 -0
  394. package/src/index.ts +3 -0
  395. package/src/kubectl-context.ts +3 -3
  396. package/src/lexicon-config.test.ts +5 -5
  397. package/src/lexicon-config.ts +4 -4
  398. package/src/lexicon-plugin-helpers.ts +1 -1
  399. package/src/lexicon.ts +296 -16
  400. package/src/lifecycle/change-set.test.ts +49 -0
  401. package/src/lifecycle/change-set.ts +67 -0
  402. package/src/lifecycle/converge-ledger.ts +1 -1
  403. package/src/lifecycle/deep-diff.test.ts +267 -14
  404. package/src/lifecycle/deep-diff.ts +217 -14
  405. package/src/lifecycle/deep-observe.test.ts +3 -1
  406. package/src/lifecycle/deep-observe.ts +16 -6
  407. package/src/lifecycle/gate-ledger.test.ts +74 -2
  408. package/src/lifecycle/gate-ledger.ts +154 -36
  409. package/src/lifecycle/index.ts +1 -0
  410. package/src/lifecycle/release-ledger.ts +2 -2
  411. package/src/lifecycle/run-ledger.test.ts +268 -0
  412. package/src/lifecycle/run-ledger.ts +201 -0
  413. package/src/lifecycle/symptoms.ts +3 -3
  414. package/src/lint/config.test.ts +82 -1
  415. package/src/lint/config.ts +112 -3
  416. package/src/lint/pipeline-change-gate.test.ts +2 -2
  417. package/src/lint/policy.test.ts +1 -1
  418. package/src/lint/post-synth.test.ts +31 -0
  419. package/src/lint/post-synth.ts +54 -1
  420. package/src/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.ts +2 -2
  421. package/src/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.ts +1 -1
  422. package/src/lint/rules/comp/comp.test.ts +27 -1
  423. package/src/lint/rules/comp/comp004-gate-needs-durable-runtime.ts +76 -0
  424. package/src/lint/rules/comp/index.ts +3 -3
  425. package/src/lint/rules/op/index.ts +23 -0
  426. package/src/lint/rules/op/ops012-activity-contract.test.ts +161 -0
  427. package/src/lint/rules/op/ops012-activity-contract.ts +64 -0
  428. package/src/lint/rules/op/ops013-step-output-ref.test.ts +237 -0
  429. package/src/lint/rules/op/ops013-step-output-ref.ts +98 -0
  430. package/src/lint/rules/op/ops014-converge-rule-refusals.test.ts +263 -0
  431. package/src/lint/rules/op/ops014-converge-rule-refusals.ts +179 -0
  432. package/src/lint/rules/op/support.ts +11 -0
  433. package/src/lint/suppressions.test.ts +163 -0
  434. package/src/lint/suppressions.ts +204 -0
  435. package/src/live-endpoint.ts +1 -1
  436. package/src/op/activities/activity-contracts.ts +66 -0
  437. package/src/op/activities/apply.test.ts +567 -0
  438. package/src/op/activities/apply.ts +662 -0
  439. package/src/op/activities/build.ts +26 -0
  440. package/src/op/activities/converge.test.ts +330 -0
  441. package/src/op/activities/converge.ts +449 -0
  442. package/src/op/activities/env-teardown.test.ts +187 -0
  443. package/src/op/activities/env-teardown.ts +171 -0
  444. package/src/op/activities/guard-validate.test.ts +139 -0
  445. package/src/op/activities/guard-validate.ts +223 -0
  446. package/src/op/activities/http-check.test.ts +48 -0
  447. package/src/op/activities/http-check.ts +55 -0
  448. package/src/op/activities/index.ts +119 -0
  449. package/src/op/activities/lexicon-upgrade.test.ts +492 -0
  450. package/src/op/activities/lexicon-upgrade.ts +673 -0
  451. package/src/op/activities/lifecycle.ts +85 -0
  452. package/src/op/activities/pipeline-audit.test.ts +67 -0
  453. package/src/op/activities/pipeline-audit.ts +201 -0
  454. package/src/op/activities/policy.test.ts +19 -0
  455. package/src/op/activities/policy.ts +37 -0
  456. package/src/op/activities/reconcile.test.ts +161 -0
  457. package/src/op/activities/reconcile.ts +354 -0
  458. package/src/op/activities/shell.ts +26 -0
  459. package/src/op/activities/teardown.ts +22 -0
  460. package/src/op/activities/util.ts +6 -0
  461. package/src/op/activities/wait.ts +69 -0
  462. package/src/op/activities/workflow-audit.test.ts +90 -0
  463. package/src/op/activities/workflow-audit.ts +203 -0
  464. package/src/op/activity-contract-registry.test.ts +98 -0
  465. package/src/op/activity-contract-registry.ts +114 -0
  466. package/src/op/activity-contract.test.ts +1 -1
  467. package/src/op/activity-contract.ts +37 -33
  468. package/src/op/activity-failure.ts +34 -0
  469. package/src/op/activity-profiles.test.ts +56 -0
  470. package/src/op/activity-profiles.ts +116 -0
  471. package/src/op/activity-registry.test.ts +37 -28
  472. package/src/op/activity-registry.ts +27 -56
  473. package/src/op/activity-runtime.ts +3 -47
  474. package/src/op/builders-exports.test.ts +0 -49
  475. package/src/op/builders-typed.test.ts +164 -0
  476. package/src/op/builders.ts +125 -41
  477. package/src/op/change-signal.test.ts +117 -0
  478. package/src/op/change-signal.ts +169 -0
  479. package/src/op/composites/apply-op.ts +218 -0
  480. package/src/op/composites/composites.test.ts +379 -0
  481. package/src/op/composites/converge-op.test.ts +168 -0
  482. package/src/op/composites/converge-op.ts +214 -0
  483. package/src/op/composites/index.ts +26 -0
  484. package/src/op/composites/lexicon-upgrade-op.test.ts +68 -0
  485. package/src/op/composites/lexicon-upgrade-op.ts +134 -0
  486. package/src/op/composites/pipeline-audit-op.test.ts +25 -0
  487. package/src/op/composites/pipeline-audit-op.ts +78 -0
  488. package/src/op/composites/reconcile-op.test.ts +51 -0
  489. package/src/op/composites/reconcile-op.ts +110 -0
  490. package/src/op/composites/watch-op.ts +108 -0
  491. package/src/op/composites/workflow-audit-op.test.ts +25 -0
  492. package/src/op/composites/workflow-audit-op.ts +88 -0
  493. package/src/op/converge-rule.ts +12 -10
  494. package/src/op/cron.test.ts +128 -0
  495. package/src/op/cron.ts +149 -0
  496. package/src/op/discover-root.test.ts +105 -1
  497. package/src/op/discover.test.ts +6 -2
  498. package/src/op/discover.ts +84 -16
  499. package/src/op/duration.ts +21 -0
  500. package/src/op/effect-step.test.ts +29 -14
  501. package/src/op/emulator-lifecycle.ts +1 -2
  502. package/src/op/gate-name.test.ts +65 -0
  503. package/src/op/gate-name.ts +60 -0
  504. package/src/op/gate-summary.ts +84 -0
  505. package/src/op/gate.ts +155 -0
  506. package/src/op/generate-pipeline.test.ts +65 -3
  507. package/src/op/generate-pipeline.ts +19 -2
  508. package/src/op/index.ts +48 -8
  509. package/src/op/local-executor.test.ts +178 -34
  510. package/src/op/local-executor.ts +371 -102
  511. package/src/op/local-output.test.ts +95 -11
  512. package/src/op/local-output.ts +33 -4
  513. package/src/op/op-ir.test.ts +292 -0
  514. package/src/op/op-ir.ts +419 -0
  515. package/src/op/op-verb-class.test.ts +2 -2
  516. package/src/op/op-verb-class.ts +19 -5
  517. package/src/op/op.test.ts +28 -7
  518. package/src/op/operator.test.ts +543 -6
  519. package/src/op/operator.ts +203 -20
  520. package/src/op/receipt-store.ts +1 -1
  521. package/src/op/resource.ts +20 -2
  522. package/src/op/runtime.ts +203 -0
  523. package/src/op/runtimes/local.test.ts +181 -0
  524. package/src/op/runtimes/local.ts +212 -0
  525. package/src/op/step-output-ref.ts +15 -13
  526. package/src/op/types.ts +106 -26
  527. package/src/ownership.ts +1 -1
  528. package/src/provenance.test.ts +6 -0
  529. package/src/provenance.ts +21 -1
  530. package/src/serializer-walker.test.ts +42 -0
  531. package/src/serializer-walker.ts +21 -2
  532. package/src/terraform/aws-resources.test.ts +13 -4
  533. package/src/terraform/bridge.test.ts +22 -9
  534. package/src/terraform/bridge.ts +89 -28
  535. package/src/terraform/carve-provider.ts +38 -2
  536. package/src/terraform/data-source-shape.ts +95 -0
  537. package/src/terraform/graph.ts +35 -7
  538. package/src/terraform/providers/kubernetes.ts +48 -2
  539. package/src/terraform/tier-map.ts +21 -6
  540. package/src/terraform/types.ts +8 -0
  541. package/dist/cli/handlers/op-progress.d.ts +0 -90
  542. package/dist/cli/handlers/op-progress.d.ts.map +0 -1
  543. package/dist/cli/handlers/run-client.d.ts +0 -127
  544. package/dist/cli/handlers/run-client.d.ts.map +0 -1
  545. package/dist/cli/handlers/run-report.d.ts +0 -5
  546. package/dist/cli/handlers/run-report.d.ts.map +0 -1
  547. package/dist/components/temporal-codegen-loader.d.ts +0 -27
  548. package/dist/components/temporal-codegen-loader.d.ts.map +0 -1
  549. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts +0 -37
  550. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts.map +0 -1
  551. package/src/cli/handlers/op-progress.test.ts +0 -268
  552. package/src/cli/handlers/op-progress.ts +0 -248
  553. package/src/cli/handlers/run-client.test.ts +0 -82
  554. package/src/cli/handlers/run-client.ts +0 -219
  555. package/src/cli/handlers/run-report.test.ts +0 -62
  556. package/src/cli/handlers/run-report.ts +0 -122
  557. package/src/components/temporal-codegen-loader.ts +0 -41
  558. package/src/lint/rules/comp/comp004-gate-needs-temporal.ts +0 -66
@@ -0,0 +1,203 @@
1
+ /**
2
+ * workflowSupplyChainAudit — the live counterpart to the github post-synth
3
+ * supply-chain checks (#286-#291).
4
+ *
5
+ * Some workflow-reference checks can only be answered against a moving external
6
+ * truth: whether a pinned SHA still corresponds to a real tag/release upstream,
7
+ * whether a referenced commit exists in the repo it claims, whether a ref is
8
+ * confusable as both a tag and a branch, whether a pin matches its version
9
+ * comment, or whether a new advisory now covers an action already in use. These
10
+ * change over time independent of the source, so they cannot live in the
11
+ * deterministic build — this activity owns *only* the checks that require live
12
+ * resolution.
13
+ *
14
+ * It is dependency-free and primitives-only: it reads the emitted workflow YAML
15
+ * (passed as a string) and resolves references through an injectable
16
+ * `ActionRefResolver`, so `chant run` can schedule it and tests can run
17
+ * it against recorded responses with no live network.
18
+ */
19
+
20
+ /** What the audit produces on findings, mirroring ReconcileOp's modes. */
21
+ export type WorkflowAuditMode = "report" | "issue" | "pull-request";
22
+
23
+ /** Live resolution of a single action reference against its upstream. */
24
+ export interface ActionRefResolution {
25
+ /** Does the ref/SHA exist in the claimed repo? */
26
+ exists: boolean;
27
+ /** Tag/release names that point at this commit (empty for a SHA ⇒ stale pin). */
28
+ tags: string[];
29
+ /** Is the upstream repository archived? */
30
+ archived: boolean;
31
+ /** Disclosed advisory identifiers affecting this action. */
32
+ advisories: string[];
33
+ /** Is the ref resolvable as BOTH a tag and a branch upstream? */
34
+ ambiguousRef?: boolean;
35
+ }
36
+
37
+ /** Pluggable upstream resolver — overridden in tests with recorded responses. */
38
+ export type ActionRefResolver = (slug: string, ref: string) => Promise<ActionRefResolution>;
39
+
40
+ export type WorkflowAuditFindingKind =
41
+ | "stale-pin"
42
+ | "impostor"
43
+ | "ambiguous-ref"
44
+ | "pin-comment-mismatch"
45
+ | "advisory"
46
+ | "archived";
47
+
48
+ export interface WorkflowAuditFinding {
49
+ /** owner/repo slug. */
50
+ slug: string;
51
+ /** The pinned ref/SHA. */
52
+ ref: string;
53
+ kind: WorkflowAuditFindingKind;
54
+ detail: string;
55
+ }
56
+
57
+ export interface WorkflowAuditArgs {
58
+ /** One emitted workflow YAML document. */
59
+ yaml?: string;
60
+ /** Several emitted workflow YAML documents. */
61
+ yamls?: string[];
62
+ /**
63
+ * Directory of emitted workflow files (`*.yml`/`*.yaml`) to read at run time
64
+ * when no `yaml`/`yamls` is given — the form used inside a scheduled Op.
65
+ * Default `.github/workflows`.
66
+ */
67
+ workflowsDir?: string;
68
+ /** What to produce. Default: report. */
69
+ mode?: WorkflowAuditMode;
70
+ /** Injectable upstream resolver. Defaults to the live GitHub REST resolver. */
71
+ resolver?: ActionRefResolver;
72
+ }
73
+
74
+ /** Read `*.yml`/`*.yaml` files from a directory (best-effort). */
75
+ async function readWorkflowDir(dir: string): Promise<string[]> {
76
+ try {
77
+ const { readdir, readFile } = await import("node:fs/promises");
78
+ const { join } = await import("node:path");
79
+ const files = (await readdir(dir)).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
80
+ return Promise.all(files.map((f) => readFile(join(dir, f), "utf-8")));
81
+ } catch {
82
+ return [];
83
+ }
84
+ }
85
+
86
+ export interface WorkflowAuditResult {
87
+ mode: WorkflowAuditMode;
88
+ findings: WorkflowAuditFinding[];
89
+ /** Markdown summary used as the PR/issue body or printed in report mode. */
90
+ summary: string;
91
+ }
92
+
93
+ interface CollectedRef {
94
+ slug: string;
95
+ ref: string;
96
+ /** Adjacent `# vX.Y.Z` comment, when present. */
97
+ comment?: string;
98
+ }
99
+
100
+ const SHA_RE = /^[0-9a-f]{40}$/;
101
+
102
+ /**
103
+ * Collect external action references (`uses: owner/repo@ref`, with an optional
104
+ * trailing version comment) from a workflow YAML. Local (`./`) and `docker://`
105
+ * references are excluded.
106
+ */
107
+ export function collectAuditRefs(yaml: string): CollectedRef[] {
108
+ const out: CollectedRef[] = [];
109
+ const re = /uses:\s*['"]?([\w.-]+\/[\w.-]+(?:\/[\w./-]+)?)@([\w.-]+)['"]?\s*(?:#\s*(\S+))?/g;
110
+ let m: RegExpExecArray | null;
111
+ while ((m = re.exec(yaml)) !== null) {
112
+ out.push({ slug: m[1].split("/").slice(0, 2).join("/"), ref: m[2], comment: m[3] });
113
+ }
114
+ return out;
115
+ }
116
+
117
+ /**
118
+ * Default resolver: query the GitHub REST API for whether the ref exists, what
119
+ * tags point at it, archive status, and advisories. Best-effort and resilient —
120
+ * a network/HTTP error yields an "unknown" resolution (exists=true, no findings)
121
+ * rather than a false positive.
122
+ */
123
+ export const defaultActionRefResolver: ActionRefResolver = async (slug, ref) => {
124
+ const base = `https://api.github.com/repos/${slug}`;
125
+ const headers: Record<string, string> = { Accept: "application/vnd.github+json" };
126
+ if (process.env.GITHUB_TOKEN) headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
127
+ const unknown: ActionRefResolution = { exists: true, tags: [], archived: false, advisories: [] };
128
+ try {
129
+ const repoResp = await fetch(base, { headers });
130
+ if (repoResp.status === 404) return { exists: false, tags: [], archived: false, advisories: [] };
131
+ if (!repoResp.ok) return unknown;
132
+ const repo = (await repoResp.json()) as { archived?: boolean };
133
+
134
+ const refResp = await fetch(`${base}/commits/${ref}`, { headers });
135
+ if (refResp.status === 404) return { exists: false, tags: [], archived: !!repo.archived, advisories: [] };
136
+
137
+ let tags: string[] = [];
138
+ if (SHA_RE.test(ref)) {
139
+ const tagsResp = await fetch(`${base}/tags?per_page=100`, { headers });
140
+ if (tagsResp.ok) {
141
+ const tagList = (await tagsResp.json()) as Array<{ name: string; commit?: { sha?: string } }>;
142
+ tags = tagList.filter((t) => t.commit?.sha === ref).map((t) => t.name);
143
+ }
144
+ }
145
+ return { exists: refResp.ok, tags, archived: !!repo.archived, advisories: [] };
146
+ } catch {
147
+ return unknown;
148
+ }
149
+ };
150
+
151
+ function renderSummary(findings: WorkflowAuditFinding[]): string {
152
+ if (findings.length === 0) return "## Workflow supply-chain audit\n\nNo live drift detected against upstream references.\n";
153
+ let out = `## Workflow supply-chain audit\n\n${findings.length} finding(s) against live upstream truth:\n\n`;
154
+ out += "| Action | Ref | Finding | Detail |\n|---|---|---|---|\n";
155
+ for (const f of findings) out += `| ${f.slug} | ${f.ref} | ${f.kind} | ${f.detail} |\n`;
156
+ return out;
157
+ }
158
+
159
+ /**
160
+ * Resolve each pinned external reference in the emitted workflows against live
161
+ * upstreams and report drift.
162
+ */
163
+ export async function workflowSupplyChainAudit(args: WorkflowAuditArgs): Promise<WorkflowAuditResult> {
164
+ const resolver = args.resolver ?? defaultActionRefResolver;
165
+ const mode = args.mode ?? "report";
166
+ let yamls = args.yamls ?? (args.yaml ? [args.yaml] : []);
167
+ if (yamls.length === 0) {
168
+ yamls = await readWorkflowDir(args.workflowsDir ?? ".github/workflows");
169
+ }
170
+
171
+ const findings: WorkflowAuditFinding[] = [];
172
+ const seen = new Set<string>();
173
+ for (const yaml of yamls) {
174
+ for (const { slug, ref, comment } of collectAuditRefs(yaml)) {
175
+ const key = `${slug}@${ref}`;
176
+ if (seen.has(key)) continue;
177
+ seen.add(key);
178
+
179
+ const res = await resolver(slug, ref);
180
+ if (!res.exists) {
181
+ findings.push({ slug, ref, kind: "impostor", detail: `ref does not exist in ${slug} — possible impostor or deleted ref` });
182
+ continue;
183
+ }
184
+ if (SHA_RE.test(ref) && res.tags.length === 0) {
185
+ findings.push({ slug, ref, kind: "stale-pin", detail: "pinned SHA is no longer on any tag/release upstream" });
186
+ }
187
+ if (res.ambiguousRef) {
188
+ findings.push({ slug, ref, kind: "ambiguous-ref", detail: "ref resolves as both a tag and a branch upstream (symbolic-ref confusion)" });
189
+ }
190
+ if (comment && res.tags.length > 0 && !res.tags.includes(comment)) {
191
+ findings.push({ slug, ref, kind: "pin-comment-mismatch", detail: `pin comment "${comment}" does not match the SHA's actual tag(s): ${res.tags.join(", ")}` });
192
+ }
193
+ if (res.archived) {
194
+ findings.push({ slug, ref, kind: "archived", detail: `${slug} has been archived upstream since pinning` });
195
+ }
196
+ for (const adv of res.advisories) {
197
+ findings.push({ slug, ref, kind: "advisory", detail: `disclosed advisory ${adv} covers ${slug}` });
198
+ }
199
+ }
200
+ }
201
+
202
+ return { mode, findings, summary: renderSummary(findings) };
203
+ }
@@ -0,0 +1,98 @@
1
+ import { describe, test, expect } from "vitest";
2
+ import { z } from "zod";
3
+ import { loadActivityContracts, mergeActivityContracts } from "./activity-contract-registry";
4
+ import { activityContract, type ActivityContract } from "./activity-contract";
5
+
6
+ describe("loadActivityContracts", () => {
7
+ test("core's own contracts load with no lexicons configured", async () => {
8
+ const contracts = await loadActivityContracts();
9
+ expect(contracts.get("shellCmd")).toBeDefined();
10
+ expect(contracts.get("httpCheck")?.returns).toBeDefined();
11
+ expect(contracts.get("lifecycleDiff")?.name).toBe("lifecycleDiff");
12
+ });
13
+
14
+ test("a lexicon's contracts are resolved by the same subpath convention loadActivities uses", async () => {
15
+ const contracts = await loadActivityContracts(["terraform"]);
16
+ // terraform ships `@intentius/chant-lexicon-terraform/op/activity-contracts`.
17
+ expect(contracts.get("terraformPlan")).toBeDefined();
18
+ expect(contracts.get("terraformApply")).toBeDefined();
19
+ // Core's own are still there — the lexicon adds, it does not replace.
20
+ expect(contracts.get("shellCmd")).toBeDefined();
21
+ });
22
+
23
+ test("terraformPlan's declared return schema carries the fields an Apply step references", async () => {
24
+ const contracts = await loadActivityContracts(["terraform"]);
25
+ const returns = contracts.get("terraformPlan")?.returns as z.ZodObject<z.ZodRawShape>;
26
+ expect(Object.keys(returns.shape)).toEqual(
27
+ expect.arrayContaining(["planFile", "text", "changed", "adds", "changes", "destroys"]),
28
+ );
29
+ });
30
+
31
+ test("an absent lexicon contributes nothing and does not throw", async () => {
32
+ const contracts = await loadActivityContracts(["no-such-lexicon-anywhere"]);
33
+ expect(contracts.get("shellCmd")).toBeDefined();
34
+ expect(contracts.size).toBeGreaterThan(0);
35
+ });
36
+
37
+ test("a plugin member contributes contracts alongside the conventional subpath", async () => {
38
+ const contracts = await loadActivityContracts([
39
+ {
40
+ name: "no-such-lexicon-anywhere",
41
+ activityContracts: () => [activityContract("bespokeApply", z.strictObject({ target: z.string() }))],
42
+ },
43
+ ]);
44
+ expect(contracts.get("bespokeApply")).toBeDefined();
45
+ });
46
+
47
+ test("a plugin member that throws contributes nothing rather than failing the load", async () => {
48
+ const contracts = await loadActivityContracts([
49
+ {
50
+ name: "no-such-lexicon-anywhere",
51
+ activityContracts: () => {
52
+ throw new Error("boom");
53
+ },
54
+ },
55
+ ]);
56
+ expect(contracts.get("shellCmd")).toBeDefined();
57
+ });
58
+
59
+ test("the last lexicon in wins a name collision, as loadActivities does for implementations", async () => {
60
+ const mine = activityContract("shellCmd", z.strictObject({ cmd: z.string(), extra: z.string() }));
61
+ const contracts = await loadActivityContracts([
62
+ { name: "no-such-lexicon-anywhere", activityContracts: () => [mine] },
63
+ ]);
64
+ expect(contracts.get("shellCmd")).toBe(mine);
65
+ });
66
+ });
67
+
68
+ describe("mergeActivityContracts", () => {
69
+ const base = new Map<string, ActivityContract>([
70
+ ["shellCmd", activityContract("shellCmd", z.strictObject({ cmd: z.string() }))],
71
+ ]);
72
+
73
+ test("an undefined loaded map returns the base unchanged", () => {
74
+ expect(mergeActivityContracts(base, undefined)).toBe(base);
75
+ });
76
+
77
+ test("an empty loaded map returns the base unchanged", () => {
78
+ expect(mergeActivityContracts(base, new Map())).toBe(base);
79
+ });
80
+
81
+ test("loaded contracts are added without mutating the base", () => {
82
+ const loaded = new Map<string, ActivityContract>([
83
+ ["terraformPlan", activityContract("terraformPlan", z.strictObject({ root: z.string() }))],
84
+ ]);
85
+ const merged = mergeActivityContracts(base, loaded);
86
+ expect(merged.get("terraformPlan")).toBeDefined();
87
+ expect(merged.get("shellCmd")).toBeDefined();
88
+ expect(base.has("terraformPlan")).toBe(false);
89
+ });
90
+
91
+ test("the loaded map wins a name collision — it is the one resolved against this build's lexicons", () => {
92
+ const loaded = new Map<string, ActivityContract>([
93
+ ["shellCmd", activityContract("shellCmd", z.strictObject({ cmd: z.string(), cwd: z.string() }))],
94
+ ]);
95
+ const merged = mergeActivityContracts(base, loaded);
96
+ expect(merged.get("shellCmd")).toBe(loaded.get("shellCmd"));
97
+ });
98
+ });
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Activity contract registry — the schema-shaped sibling of
3
+ * `./activity-registry.ts` (chant #2101).
4
+ *
5
+ * `activity-contract.ts` left contract ownership per-caller on purpose: an
6
+ * activity declares its contract alongside its implementation, and whoever
7
+ * builds a contract map decides which activities it covers. What was missing
8
+ * was the other half of that arrangement, a way for the checks that validate
9
+ * Op steps to SEE a contract a lexicon declared. OPS012 and OPS013
10
+ * (`../lint/rules/op/`) walk every Op entity in the build, including steps
11
+ * calling `terraformPlan`, `k3sInstall`, `flyApply` or `gcpApply`, over
12
+ * core's own static table alone. Since #2122 moved those checks into core
13
+ * they fire on every project that declares an Op, so an Op built out of one
14
+ * lexicon's steps failed `chant build` outright even though `loadActivities`
15
+ * resolves the very same names at run time. Both terraform examples that
16
+ * carry an Op kept it outside `src/` because of the older, lexicon-hosted
17
+ * form of the same gap, which meant no example ever proved its Op builds.
18
+ *
19
+ * The resolution mirrors activities exactly, by convention:
20
+ * `@intentius/chant-lexicon-<name>/op/activity-contracts` is imported for
21
+ * every configured lexicon and every {@link ActivityContract} it exports is
22
+ * collected, keyed by the contract's declared `name`. A lexicon that ships
23
+ * no such module is skipped, the same way `loadActivities` skips one that
24
+ * contributes no activities. A plugin may also hand its contracts over
25
+ * directly through {@link LexiconActivityContractContributor}, for a lexicon
26
+ * whose contracts are not reachable at a subpath (a bundled single-file
27
+ * plugin, a project-local one).
28
+ *
29
+ * The merged map reaches a post-synth check as
30
+ * `PostSynthContext.activityContracts` (`../lint/post-synth.ts`), filled in
31
+ * by whoever runs the checks: `chant build`, `chant lint` and
32
+ * `check-lexicon`'s example harness all do. A check merges it over its own
33
+ * base table rather than replacing it, so a context that carries none (a
34
+ * hand-built one in a test) behaves exactly as before this existed.
35
+ */
36
+
37
+ import * as coreContracts from "./activities/activity-contracts";
38
+ import { collectActivityContracts, type ActivityContract } from "./activity-contract";
39
+
40
+ /**
41
+ * The shape {@link loadActivityContracts} reads off a plugin: an optional
42
+ * member returning this lexicon's contracts directly, for one that cannot be
43
+ * reached by the subpath convention. Structural on purpose, so core's op
44
+ * layer does not depend on `../lexicon.ts`.
45
+ */
46
+ export interface LexiconActivityContractContributor {
47
+ /** The lexicon's name, as in `@intentius/chant-lexicon-<name>`. */
48
+ name: string;
49
+ /** This lexicon's activity contracts, if it declares them in code rather than at the conventional subpath. */
50
+ activityContracts?(): ActivityContract[];
51
+ }
52
+
53
+ /**
54
+ * Build the activity-contract map for build-time validation: core's own
55
+ * contracts (`./activities/activity-contracts`, a static import so a project
56
+ * that has installed nothing but chant still gets them) plus every contract
57
+ * the named lexicons contribute.
58
+ *
59
+ * `lexicons` may be plain names or plugin-shaped objects; a plugin's
60
+ * `activityContracts()` is read after its conventional module, so a plugin
61
+ * that declares both wins on a name collision with itself. Across lexicons,
62
+ * last one in wins, which is the same rule `loadActivities` applies to
63
+ * implementations.
64
+ *
65
+ * Never throws: an absent lexicon, a missing subpath, or a plugin member that
66
+ * throws all leave the map as it was.
67
+ */
68
+ export async function loadActivityContracts(
69
+ lexicons: ReadonlyArray<string | LexiconActivityContractContributor> = [],
70
+ ): Promise<Map<string, ActivityContract>> {
71
+ const contracts = new Map<string, ActivityContract>();
72
+
73
+ collectActivityContracts(coreContracts as unknown as Record<string, unknown>, contracts);
74
+
75
+ for (const entry of lexicons) {
76
+ const name = typeof entry === "string" ? entry : entry.name;
77
+ try {
78
+ const spec = `@intentius/chant-lexicon-${name}/op/activity-contracts`;
79
+ collectActivityContracts((await import(spec)) as Record<string, unknown>, contracts);
80
+ } catch {
81
+ // Lexicon absent or declares no contracts at the conventional subpath.
82
+ }
83
+
84
+ if (typeof entry !== "string" && typeof entry.activityContracts === "function") {
85
+ try {
86
+ for (const contract of entry.activityContracts()) contracts.set(contract.name, contract);
87
+ } catch {
88
+ // A plugin member that throws contributes nothing, the same as one that is absent.
89
+ }
90
+ }
91
+ }
92
+
93
+ return contracts;
94
+ }
95
+
96
+ /**
97
+ * Merge a check's own base contracts with the ones the build loaded, into the
98
+ * map `validateActivitySteps`/`validateStepOutputRefs` take. The loaded set
99
+ * wins on a name collision: it is the one resolved against the lexicons this
100
+ * build actually configured, whereas the base table is whatever the check
101
+ * imported statically.
102
+ *
103
+ * `loaded` being `undefined` is the ordinary case for a hand-built
104
+ * `PostSynthContext` and yields the base table unchanged.
105
+ */
106
+ export function mergeActivityContracts(
107
+ base: ReadonlyMap<string, ActivityContract>,
108
+ loaded: ReadonlyMap<string, ActivityContract> | undefined,
109
+ ): ReadonlyMap<string, ActivityContract> {
110
+ if (!loaded || loaded.size === 0) return base;
111
+ const merged = new Map(base);
112
+ for (const [name, contract] of loaded) merged.set(name, contract);
113
+ return merged;
114
+ }
@@ -90,7 +90,7 @@ describe("validateActivitySteps() — passing Ops", () => {
90
90
 
91
91
  it("a step-output reference standing in for a typed arg does not trip a false-positive type mismatch (#1290)", () => {
92
92
  // args.env is z.string() — a StepOutputRef placeholder there is not a
93
- // string, but it's not a real error either; TMP013 validates it against
93
+ // string, but it's not a real error either; OPS013 validates it against
94
94
  // the producer's return schema separately.
95
95
  const step = activity("lifecycleDiff", { env: stepOutput("diff", "name") as unknown as string });
96
96
  const config = opWith({ phases: [phase("Diff", [step])] });
@@ -16,37 +16,39 @@
16
16
  * An activity declares a contract alongside its implementation — the same
17
17
  * "registration surface" the issue asked for (`activity-registry.ts`'s
18
18
  * `collectActivities` already does this for implementations; this is the
19
- * schema-shaped sibling). `chant build` — via a lexicon's own post-synth
20
- * check, e.g. the temporal lexicon's TMP012 — resolves each step's `fn`
21
- * against a contract map built the same way and validates `args` and
22
- * `outcomeAttribute.from`. A step whose `fn` has no registered contract is
23
- * skipped (not an error): this is deliberately incremental — a lexicon opts
24
- * an activity in by declaring a contract for it, and the k8s/aws/azure/gcp/
25
- * fly activity sets are expected to pick this up lexicon by lexicon rather
26
- * than all at once (see the issue's "worth checking this lands cleanly"
27
- * note).
19
+ * schema-shaped sibling). `chant build` resolves each step's `fn` against a
20
+ * contract map built from `Object.values()` of an activity-contracts module
21
+ * and validates `args` and `outcomeAttribute.from` — core's own OPS012
22
+ * (`../lint/rules/op/ops012-activity-contract.ts`, chant #2122) does this
23
+ * for every declared Op regardless of which lexicons are configured; a
24
+ * lexicon's own post-synth check can additionally validate against its own
25
+ * roster the same way (a lexicon-owned check did this before #2122 moved the
26
+ * op-model checks to core). A step whose `fn` has no registered
27
+ * contract is skipped (not an error): this is deliberately incremental — a
28
+ * contract map opts an activity in by declaring one for it, and the
29
+ * k8s/aws/azure/gcp/fly activity sets are expected to pick this up one at a
30
+ * time rather than all at once (see the issue's "worth checking this lands
31
+ * cleanly" note).
28
32
  *
29
- * Ownership is decentralized on purpose: each lexicon declares contracts for
30
- * the activities it implements and validates them with its own post-synth
31
- * check (the same `rulePrefix`-per-lexicon pattern every other check in
32
- * chant already uses), rather than a shared cross-lexicon registry. A
33
- * `Temporal::Op` step can call an activity contributed by any lexicon, and
34
- * `PostSynthContext.entities` already carries the whole resolved graph to
35
- * every lexicon's checks, so no new plumbing is needed for that to work.
33
+ * Ownership of a contract map is per-caller, not a shared cross-lexicon
34
+ * registry: whoever builds the map (core's OPS012/OPS013, or a lexicon's own
35
+ * check) decides which activities it covers. A `Chant::Op` step can call an
36
+ * activity contributed by any lexicon, and `PostSynthContext.entities`
37
+ * already carries the whole resolved graph to every check, so no new
38
+ * plumbing is needed for that to work.
36
39
  */
37
40
 
38
41
  import { z } from "zod";
42
+ import { outcomeAttributesOf } from "./types";
39
43
  import type { OpConfig, PhaseDefinition, ActivityStep, StepDefinition } from "./types";
44
+ import { ACTIVITY_PROFILE_NAMES } from "./activity-profiles";
40
45
 
41
- /** Every literal value {@link ActivityStep.profile} may hold. Kept in sync with `types.ts`'s `ActivityStep["profile"]`. */
42
- export const KNOWN_ACTIVITY_PROFILES = [
43
- "fastIdempotent",
44
- "longInfra",
45
- "k8sWait",
46
- "humanGate",
47
- "argoSync",
48
- "policyCheck",
49
- ] as const;
46
+ /**
47
+ * Every literal value {@link ActivityStep.profile} may hold, read off
48
+ * {@link ACTIVITY_PROFILES} itself so the runtime list cannot drift from the
49
+ * table the type is derived from.
50
+ */
51
+ export const KNOWN_ACTIVITY_PROFILES = ACTIVITY_PROFILE_NAMES;
50
52
 
51
53
  const CONTRACT_BRAND = Symbol.for("chant.op.activityContract");
52
54
 
@@ -266,7 +268,7 @@ export function validateActivitySteps(
266
268
  // A step-output reference (#1290) sitting at this path is a
267
269
  // placeholder object at build time, not the value it will
268
270
  // resolve to — so an args-schema type mismatch here is a false
269
- // positive; TMP013 (`validateStepOutputRefs`) is what validates
271
+ // positive; OPS013 (`validateStepOutputRefs`) is what validates
270
272
  // a reference, against the *producer's* declared return schema.
271
273
  // An unrecognized-key issue's path is the parent object (`[]`
272
274
  // for a top-level extra key), which is never itself a reference,
@@ -277,13 +279,15 @@ export function validateActivitySteps(
277
279
  }
278
280
  }
279
281
 
280
- if (step.outcomeAttribute?.from && contract.returns && !pathExistsInSchema(contract.returns, step.outcomeAttribute.from)) {
281
- issues.push({
282
- opName: config.name,
283
- phase: phase.name,
284
- fn: step.fn,
285
- message: `outcomeAttribute.from "${step.outcomeAttribute.from}" does not exist on "${step.fn}"'s declared return type`,
286
- });
282
+ for (const attr of outcomeAttributesOf(step)) {
283
+ if (attr.from && contract.returns && !pathExistsInSchema(contract.returns, attr.from)) {
284
+ issues.push({
285
+ opName: config.name,
286
+ phase: phase.name,
287
+ fn: step.fn,
288
+ message: `outcomeAttribute.from "${attr.from}" does not exist on "${step.fn}"'s declared return type`,
289
+ });
290
+ }
287
291
  }
288
292
  }
289
293
  }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * A terminal activity failure.
3
+ *
4
+ * Some activities fail deterministically: an organizational policy violation
5
+ * (`policyGate`) or a cfn-guard finding (`guardValidate`) reads the same on the
6
+ * second attempt as on the first, so a retry only delays the report. Those
7
+ * activities used to raise an orchestrator SDK's non-retryable failure type,
8
+ * which put that SDK on the import path of every base activity. Core owns the
9
+ * base activities now (chant #2114), and depends on no runtime SDK — this is
10
+ * the same signal in a plain `Error`.
11
+ *
12
+ * `name` is the failure type, not the class name, so the local executor's
13
+ * `retry.nonRetryableErrorTypes` (which matches on `Error.name`) can name it in
14
+ * a profile. `type` and `nonRetryable` are kept alongside it because that is
15
+ * the shape callers and tests already read.
16
+ */
17
+ export class NonRetryableActivityError extends Error {
18
+ /** Always true — present so a caller can branch without an `instanceof`. */
19
+ readonly nonRetryable = true;
20
+
21
+ constructor(
22
+ message: string,
23
+ /** Failure type, e.g. `"PolicyViolation"`. Also this error's `name`. */
24
+ readonly type: string,
25
+ ) {
26
+ super(message);
27
+ this.name = type;
28
+ }
29
+ }
30
+
31
+ /** Build a {@link NonRetryableActivityError} — a non-retryable failure with no SDK behind it. */
32
+ export function nonRetryableFailure(message: string, type: string): NonRetryableActivityError {
33
+ return new NonRetryableActivityError(message, type);
34
+ }
@@ -0,0 +1,56 @@
1
+ import { describe, test, expect } from "vitest";
2
+ import { ACTIVITY_PROFILES, ACTIVITY_PROFILE_NAMES, type ActivityProfileName } from "./activity-profiles";
3
+ import { KNOWN_ACTIVITY_PROFILES } from "./activity-contract";
4
+ import { activity } from "./builders";
5
+
6
+ /**
7
+ * chant #2114 — the profile table moved out of a hosting lexicon into core and
8
+ * lost its prefix. What a profile carries is a timeout and a retry policy;
9
+ * `heartbeatTimeout` configured a worker's liveness protocol and has no subject
10
+ * in an in-process step, so it did not come along.
11
+ */
12
+ describe("ACTIVITY_PROFILES", () => {
13
+ test("carries the six named profiles", () => {
14
+ expect(Object.keys(ACTIVITY_PROFILES).sort()).toEqual(
15
+ ["argoSync", "fastIdempotent", "humanGate", "k8sWait", "longInfra", "policyCheck"],
16
+ );
17
+ });
18
+
19
+ test("every profile has a timeout, and none has a worker-era field", () => {
20
+ for (const [name, profile] of Object.entries(ACTIVITY_PROFILES)) {
21
+ expect(typeof profile.timeout, `${name}.timeout`).toBe("string");
22
+ expect(profile, `${name} should have no heartbeatTimeout`).not.toHaveProperty("heartbeatTimeout");
23
+ expect(profile, `${name} should have no startToCloseTimeout`).not.toHaveProperty("startToCloseTimeout");
24
+ }
25
+ });
26
+
27
+ test("the retry shapes that mattered survived the move", () => {
28
+ expect(ACTIVITY_PROFILES.humanGate.retry.maximumAttempts).toBe(1);
29
+ expect(ACTIVITY_PROFILES.policyCheck.retry.maximumAttempts).toBe(1);
30
+ expect(ACTIVITY_PROFILES.k8sWait.retry.nonRetryableErrorTypes).toContain("ReadinessFailedError");
31
+ expect(ACTIVITY_PROFILES.argoSync.retry.nonRetryableErrorTypes).toContain("ArgoSyncFailedError");
32
+ });
33
+
34
+ test("KNOWN_ACTIVITY_PROFILES is the table's own key list, not a second copy", () => {
35
+ expect(KNOWN_ACTIVITY_PROFILES).toBe(ACTIVITY_PROFILE_NAMES);
36
+ expect([...KNOWN_ACTIVITY_PROFILES].sort()).toEqual(Object.keys(ACTIVITY_PROFILES).sort());
37
+ });
38
+ });
39
+
40
+ // ── Compile-time-only: ActivityStep.profile is derived from the table ────────
41
+ function _typeChecksOnly(): void {
42
+ const names: ActivityProfileName[] = [
43
+ "fastIdempotent", "longInfra", "k8sWait", "humanGate", "argoSync", "policyCheck",
44
+ ];
45
+ void names;
46
+
47
+ activity("shellCmd", { cmd: "true" }, "policyCheck");
48
+
49
+ // @ts-expect-error — a seventh profile name is not in ACTIVITY_PROFILES.
50
+ activity("shellCmd", { cmd: "true" }, "leisurely");
51
+
52
+ // @ts-expect-error — same rejection through the step's own field.
53
+ const _step: { profile?: ActivityProfileName } = { profile: "leisurely" };
54
+ void _step;
55
+ }
56
+ void _typeChecksOnly;