@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
@@ -1,13 +1,13 @@
1
1
  /**
2
- * `chant operator` (#1485, epic #1487) — native durable ticks without
3
- * Temporal. A deliberately small daemon: a timer, a lease
2
+ * `chant operator` (#1485, epic #1487) — durable ticks with no service behind
3
+ * them. A deliberately small daemon: a timer, a lease
4
4
  * (../lifecycle/lease.ts), and the existing local executor
5
5
  * (./local-executor.ts) in a loop. No new service, no new state store — the
6
6
  * lease lives on a git ref, tick state lives on the `chant/lifecycle`
7
7
  * orphan branch via the same `convergeTick` activity `chant run <op>`
8
8
  * already calls for a single one-shot tick.
9
9
  *
10
- * One round: discover every `ConvergeOp` (searchAttributes `Converge:
10
+ * One round: discover every `ConvergeOp` (label `Converge:
11
11
  * "true"`, optionally filtered to one env), and for each one, try to
12
12
  * acquire (or renew) its lease. Win it — run one tick via `runOpLocally` on
13
13
  * the Op's own Observe → Converge phases, the identical path `chant run
@@ -37,23 +37,34 @@
37
37
  */
38
38
  import type { ActivityFn, ActivityProfile } from "./activity-registry";
39
39
  import { discoverOps, type DiscoveredOp } from "./discover";
40
- import { runOpLocally, OpRunFailure, LocalGateUnsupportedError, type OpRunResult } from "./local-executor";
40
+ import { runOpLocally, OpRunFailure, type OpRunResult } from "./local-executor";
41
41
  import { acquireLease, stillHoldsLease, currentHolderId, DEFAULT_LEASE_TTL_MS, type LeaseRecord, type AcquireLeaseResult } from "../lifecycle/lease";
42
42
  import { StaleLockError } from "../lifecycle/git";
43
+ import { cronMatches, cronDueBetween } from "./cron";
44
+ import { createChangeSignalGate, DEFAULT_SIGNAL_FLOOR_MS, type WakeReason } from "./change-signal";
45
+ import type { ChangeSubscription } from "../lexicon";
43
46
 
44
- /** Poll interval between rounds — the operator's own cadence, distinct from a `ConvergeOp`'s Temporal `schedule` cron (that field drives the durable path's `TemporalSchedule`, not anything the local executor can read back at discovery time; see this module's doc). Chosen short enough to converge promptly, long enough not to hammer `chant lifecycle plan --live` every few seconds. */
47
+ /**
48
+ * Poll interval between rounds — the operator's own cadence, and the fallback
49
+ * for an Op that declares none. Since #2120 an Op can carry its own
50
+ * `schedule.cron` (`./composites/converge-op.ts`), which this loop honours per
51
+ * Op: the round still wakes every `intervalMs`, but an Op with a cron is
52
+ * ticked only on the minutes that cron fires. Chosen short enough to converge
53
+ * promptly, long enough not to hammer `chant lifecycle plan --live` every few
54
+ * seconds — and short enough that a per-minute cron is not missed.
55
+ */
45
56
  export const DEFAULT_OPERATOR_INTERVAL_MS = 60_000;
46
57
 
47
- /** One ConvergeOp's own `searchAttributes.Env` (`../../lexicons/temporal/src/composites/converge-op.ts` always sets it). `undefined` for a non-ConvergeOp or a hand-built one missing it — filtered out by `isConvergeOp` before this is trusted. */
58
+ /** One ConvergeOp's own `labels.Env` (`./composites/converge-op.ts` always sets it). `undefined` for a non-ConvergeOp or a hand-built one missing it — filtered out by `isConvergeOp` before this is trusted. */
48
59
  function envOf(config: DiscoveredOp["config"]): string | undefined {
49
- return config.searchAttributes?.Env;
60
+ return config.labels?.Env;
50
61
  }
51
62
 
52
63
  function isConvergeOp(config: DiscoveredOp["config"]): boolean {
53
- return config.searchAttributes?.Converge === "true";
64
+ return config.labels?.Converge === "true";
54
65
  }
55
66
 
56
- /** Discover every `ConvergeOp` in the project (`searchAttributes.Converge === "true"`), optionally filtered to one environment. Ops that don't declare an `Env` search attribute are excluded from an `--env`-filtered discovery (there's nothing to match), but included when no `env` filter is given. */
67
+ /** Discover every `ConvergeOp` in the project (`labels.Converge === "true"`), optionally filtered to one environment. Ops that don't declare an `Env` label are excluded from an `--env`-filtered discovery (there's nothing to match), but included when no `env` filter is given. */
57
68
  export async function discoverConvergeOps(
58
69
  opts?: { cwd?: string; env?: string },
59
70
  ): Promise<{ ops: DiscoveredOp[]; errors: string[] }> {
@@ -67,6 +78,8 @@ export async function discoverConvergeOps(
67
78
  export type OperatorTickEvent =
68
79
  | { kind: "ticked"; op: string; env: string; result: OpRunResult }
69
80
  | { kind: "skipped-lease-held"; op: string; env: string; heldBy?: string }
81
+ /** The Op declares its own `schedule.cron` (#2120) and this round did not land on a firing minute — the lease was never touched. An Op without a cron is never reported this way: it ticks every round, on `--interval`. */
82
+ | { kind: "skipped-not-due"; op: string; env: string; cron: string }
70
83
  | { kind: "tick-failed"; op: string; env: string; error: string }
71
84
  /** The lease was lost between acquiring it and the tick finishing (e.g. this process stalled past its TTL and another operator reclaimed it) — the tick's own ledger record (written inside `convergeTick`) still landed, since a converge tick is idempotent by design; this event exists purely so `chant operator`'s log and the ledger-independent test surface can see the fencing violation happened. Never a hard failure. */
72
85
  | { kind: "fenced"; op: string; env: string }
@@ -96,6 +109,16 @@ export interface OperatorRoundOptions {
96
109
  profiles: Record<string, ActivityProfile>;
97
110
  now?: () => Date;
98
111
  signal?: AbortSignal;
112
+ /**
113
+ * When each Op with its own `schedule.cron` was last considered (#2120) —
114
+ * the state a round needs to answer "has a firing minute passed since I
115
+ * last looked at this Op?". `runOperatorForever` owns one map across
116
+ * rounds; a bare `runOperatorRound` with none given falls back to "is this
117
+ * minute a firing minute", which is what a single round can know on its
118
+ * own. Every considered Op's entry is updated, ticked or not, so a late
119
+ * round makes up exactly one tick rather than a backlog.
120
+ */
121
+ scheduleState?: Map<string, Date>;
99
122
  }
100
123
 
101
124
  /**
@@ -112,6 +135,24 @@ export async function runOperatorRound(opts: OperatorRoundOptions): Promise<Oper
112
135
  for (const { config } of ops) {
113
136
  const env = envOf(config) ?? "unknown";
114
137
 
138
+ // An Op that declares its own cadence (#2120) is ticked on that cron
139
+ // rather than on every round. Level-triggered: the question is whether a
140
+ // firing minute has passed since this Op was last looked at, so a round
141
+ // that arrives late still owes exactly one tick — a converge tick
142
+ // re-observes everything anyway, and several missed fires are one tick's
143
+ // worth of work.
144
+ const cron = config.schedule?.cron;
145
+ if (cron) {
146
+ const now = (opts.now ?? (() => new Date()))();
147
+ const lastSeen = opts.scheduleState?.get(config.name);
148
+ const due = lastSeen === undefined ? cronMatches(cron, now) : cronDueBetween(cron, lastSeen, now);
149
+ opts.scheduleState?.set(config.name, now);
150
+ if (!due) {
151
+ events.push({ kind: "skipped-not-due", op: config.name, env, cron });
152
+ continue;
153
+ }
154
+ }
155
+
115
156
  let acquired: AcquireLeaseResult;
116
157
  try {
117
158
  acquired = await acquireLease(config.name, holder, {
@@ -137,18 +178,18 @@ export async function runOperatorRound(opts: OperatorRoundOptions): Promise<Oper
137
178
 
138
179
  const lease = acquired.lease as LeaseRecord;
139
180
  try {
140
- const result = await runOpLocally(config, opts.activities, opts.profiles, opts.signal);
181
+ const result = await runOpLocally(config, opts.activities, opts.profiles, opts.signal, {
182
+ ledger: { cwd: opts.cwd },
183
+ });
141
184
  const held = await stillHoldsLease(config.name, holder, lease.token, { cwd: opts.cwd });
142
185
  events.push(held ? { kind: "ticked", op: config.name, env, result } : { kind: "fenced", op: config.name, env });
143
186
  } catch (err) {
144
187
  const message =
145
188
  err instanceof OpRunFailure
146
189
  ? `Op "${config.name}" failed — see its ledger record for step-level detail`
147
- : err instanceof LocalGateUnsupportedError
190
+ : err instanceof Error
148
191
  ? err.message
149
- : err instanceof Error
150
- ? err.message
151
- : String(err);
192
+ : String(err);
152
193
  events.push({ kind: "tick-failed", op: config.name, env, error: message });
153
194
  }
154
195
  }
@@ -172,11 +213,67 @@ function sleepAbortable(ms: number, signal?: AbortSignal): Promise<void> {
172
213
  });
173
214
  }
174
215
 
216
+ /**
217
+ * One lexicon's change-signal seam, already bound to an environment (#1981).
218
+ *
219
+ * The loop never sees a `LexiconPlugin`: core's operator does not import
220
+ * plugins, and binding happens where plugins are already loaded
221
+ * (`collectChangeSubscribers` in `../cli/plugins.ts`), the same extract-then-
222
+ * thread shape `collectBuildRootContributors` uses for build roots.
223
+ */
224
+ export interface ChangeSubscriber {
225
+ /** Which lexicon supplies the signal. Log lines only; never a fact. */
226
+ lexicon: string;
227
+ subscribe(ctx: {
228
+ onChange: () => void;
229
+ onError: (message: string) => void;
230
+ signal: AbortSignal;
231
+ }): Promise<ChangeSubscription>;
232
+ }
233
+
234
+ /**
235
+ * What the loop's change-signal machinery reports, alongside the per-op tick
236
+ * events. Separate from {@link OperatorTickEvent} on purpose: none of this is
237
+ * about an Op, and none of it is an observation.
238
+ */
239
+ export type OperatorSignalEvent =
240
+ /** A lexicon's subscription is live and the loop may now wake early. */
241
+ | { kind: "subscribed"; lexicon: string }
242
+ /** Establishing it failed. The loop keeps its timer and retries next round. */
243
+ | { kind: "subscribe-failed"; lexicon: string; error: string }
244
+ /** A live subscription died. Reported once; the next round re-subscribes. */
245
+ | { kind: "subscription-lost"; lexicon: string; error: string }
246
+ /** A signal shortened the sleep. The round that follows is an ordinary round. */
247
+ | { kind: "woken"; afterMs: number };
248
+
249
+ /** Render one signal event as `chant operator`'s log line shape. */
250
+ export function formatSignalLine(event: OperatorSignalEvent): string {
251
+ switch (event.kind) {
252
+ case "subscribed":
253
+ return `operator: ${event.lexicon} change signal subscribed (the timer still runs)`;
254
+ case "subscribe-failed":
255
+ return `operator: ${event.lexicon} change signal unavailable, polling on the timer (${event.error})`;
256
+ case "subscription-lost":
257
+ return `operator: ${event.lexicon} change signal lost, polling on the timer until the next round re-subscribes (${event.error})`;
258
+ case "woken":
259
+ return `operator: woken by a change signal after ${event.afterMs}ms, running an ordinary round`;
260
+ }
261
+ }
262
+
175
263
  export interface OperatorLoopOptions extends OperatorRoundOptions {
176
264
  /** @default DEFAULT_OPERATOR_INTERVAL_MS */
177
265
  intervalMs?: number;
178
266
  /** Called after every round completes — the CLI's one-line-per-tick log lives here, not inside the loop itself, so a test can drive rounds without capturing stdout. */
179
267
  onRound?: (events: OperatorTickEvent[]) => void;
268
+ /**
269
+ * Change-signal seams to keep subscribed for the life of the loop (#1981).
270
+ * Omitted, or empty, and the loop is exactly what it was: a timer.
271
+ */
272
+ subscribers?: readonly ChangeSubscriber[];
273
+ /** @default DEFAULT_SIGNAL_FLOOR_MS. The shortest gap a signal may force between two rounds. */
274
+ signalFloorMs?: number;
275
+ /** Called for every subscription and wake event. The CLI logs one line each. */
276
+ onSignalEvent?: (event: OperatorSignalEvent) => void;
180
277
  }
181
278
 
182
279
  /**
@@ -185,14 +282,97 @@ export interface OperatorLoopOptions extends OperatorRoundOptions {
185
282
  * (issue: "if it dies, nothing breaks and nothing is lost") — this function
186
283
  * is exactly `while (!aborted) { round(); sleep(); }`, nothing durable lives
187
284
  * in its own memory that a restart would need to recover.
285
+ *
286
+ * With `subscribers` (#1981) the sleep gains a second wake source, and only a
287
+ * wake source. A signal aborts the current sleep; the round that follows is
288
+ * the same round the timer would have run, deriving everything from a fresh
289
+ * observation. Nothing a subscription reports reaches a tick, because the
290
+ * seam has nowhere to put it: `onChange` takes no arguments.
188
291
  */
189
292
  export async function runOperatorForever(opts: OperatorLoopOptions): Promise<void> {
190
293
  const holder = opts.holder ?? currentHolderId();
191
- while (!opts.signal?.aborted) {
192
- const events = await runOperatorRound({ ...opts, holder });
193
- opts.onRound?.(events);
194
- if (opts.signal?.aborted) break;
195
- await sleepAbortable(opts.intervalMs ?? DEFAULT_OPERATOR_INTERVAL_MS, opts.signal);
294
+ // Per-op cron state (#2120) lives for the life of the loop. A restart starts
295
+ // it empty, which means the first round ticks a scheduled Op only if it
296
+ // lands on a firing minute — a fresh operator owes no catch-up for the ticks
297
+ // it was not running for.
298
+ const scheduleState = opts.scheduleState ?? new Map<string, Date>();
299
+ const intervalMs = opts.intervalMs ?? DEFAULT_OPERATOR_INTERVAL_MS;
300
+ const subscribers = opts.subscribers ?? [];
301
+
302
+ // Nothing to subscribe to: the loop below is byte for byte the loop that
303
+ // shipped before #1981, and takes none of the machinery's cost.
304
+ if (subscribers.length === 0) {
305
+ while (!opts.signal?.aborted) {
306
+ const events = await runOperatorRound({ ...opts, holder, scheduleState });
307
+ opts.onRound?.(events);
308
+ if (opts.signal?.aborted) break;
309
+ await sleepAbortable(intervalMs, opts.signal);
310
+ }
311
+ return;
312
+ }
313
+
314
+ const gate = createChangeSignalGate({ floorMs: opts.signalFloorMs ?? DEFAULT_SIGNAL_FLOOR_MS });
315
+ // The subscriptions the loop currently holds, by lexicon. A lexicon absent
316
+ // from this map is one to (re-)subscribe on the next round, which is how a
317
+ // killed watch comes back without a special retry path.
318
+ const live = new Map<string, ChangeSubscription>();
319
+ // Stops every subscription at once when the loop ends, whatever ends it.
320
+ const subscriptionsAbort = new AbortController();
321
+
322
+ const closeAll = async (): Promise<void> => {
323
+ subscriptionsAbort.abort();
324
+ const open = [...live.values()];
325
+ live.clear();
326
+ // A close that throws must not mask why the loop is unwinding.
327
+ await Promise.allSettled(open.map((s) => s.close()));
328
+ };
329
+
330
+ const ensureSubscribed = async (): Promise<void> => {
331
+ for (const subscriber of subscribers) {
332
+ if (live.has(subscriber.lexicon) || subscriptionsAbort.signal.aborted) continue;
333
+ try {
334
+ const subscription = await subscriber.subscribe({
335
+ onChange: () => gate.signal(),
336
+ onError: (error) => {
337
+ // Reported once, then forgotten: dropping it from `live` is what
338
+ // makes the next round re-subscribe. The loop itself is untouched.
339
+ if (!live.delete(subscriber.lexicon)) return;
340
+ opts.onSignalEvent?.({ kind: "subscription-lost", lexicon: subscriber.lexicon, error });
341
+ },
342
+ signal: subscriptionsAbort.signal,
343
+ });
344
+ live.set(subscriber.lexicon, subscription);
345
+ opts.onSignalEvent?.({ kind: "subscribed", lexicon: subscriber.lexicon });
346
+ } catch (err) {
347
+ // A subscription that cannot be established is a slower loop, never a
348
+ // stopped one: report it and fall through to the timer.
349
+ opts.onSignalEvent?.({
350
+ kind: "subscribe-failed",
351
+ lexicon: subscriber.lexicon,
352
+ error: err instanceof Error ? err.message : String(err),
353
+ });
354
+ }
355
+ }
356
+ };
357
+
358
+ try {
359
+ while (!opts.signal?.aborted) {
360
+ gate.roundStarted();
361
+ const startedAt = Date.now();
362
+ const events = await runOperatorRound({ ...opts, holder, scheduleState });
363
+ opts.onRound?.(events);
364
+ if (opts.signal?.aborted) break;
365
+
366
+ await ensureSubscribed();
367
+ if (opts.signal?.aborted) break;
368
+
369
+ const reason: WakeReason = await gate.wait(intervalMs, opts.signal);
370
+ if (reason === "signal") {
371
+ opts.onSignalEvent?.({ kind: "woken", afterMs: Date.now() - startedAt });
372
+ }
373
+ }
374
+ } finally {
375
+ await closeAll();
196
376
  }
197
377
  }
198
378
 
@@ -200,9 +380,12 @@ export async function runOperatorForever(opts: OperatorLoopOptions): Promise<voi
200
380
  export function formatRoundLine(event: OperatorTickEvent): string {
201
381
  switch (event.kind) {
202
382
  case "ticked":
203
- return `operator: ${event.op}@${event.env} ticked=1 ok=${event.result.ok}`;
383
+ return `operator: ${event.op}@${event.env} ticked=1 status=${event.result.status}` +
384
+ (event.result.gate ? ` gate="${event.result.gate.gate}"` : "");
204
385
  case "skipped-lease-held":
205
386
  return `operator: ${event.op}@${event.env} skipped=1(lease-held${event.heldBy ? `:${event.heldBy}` : ""})`;
387
+ case "skipped-not-due":
388
+ return `operator: ${event.op}@${event.env} skipped=1(not-due:${event.cron})`;
206
389
  case "tick-failed":
207
390
  return `operator: ${event.op}@${event.env} failed=1 error="${event.error}"`;
208
391
  case "fenced":
@@ -108,7 +108,7 @@ export interface ReceiptReadArgs extends ReceiptCheckInput {}
108
108
  export interface ReceiptReadResult {
109
109
  /** The receipt's live stored value, or null when absent. */
110
110
  current: string | null;
111
- /** The resolved expectation the workflow compares and later writes. */
111
+ /** The resolved expectation the effect step compares and later writes. */
112
112
  expectation: string;
113
113
  /** Convenience: `current === expectation`. */
114
114
  applied: boolean;
@@ -2,7 +2,25 @@ import { createResource } from "../runtime";
2
2
 
3
3
  /**
4
4
  * The Declarable resource backing an Op definition.
5
- * entityType: "Temporal::Op", lexicon: "temporal"
5
+ * entityType: "Chant::Op", lexicon: "chant"
6
6
  * Discovered automatically alongside infra files — no pipeline changes needed.
7
+ *
8
+ * An Op is chant's own verb, not a runtime's (#2118, epic #2114): the model,
9
+ * the executor and the ledger all live in core, and a lexicon that hosts runs
10
+ * is one runtime among several. The entity type says so.
7
11
  */
8
- export const OpResource = createResource("Temporal::Op", "temporal", {});
12
+ export const OP_ENTITY_TYPE = "Chant::Op";
13
+
14
+ export const OpResource = createResource(OP_ENTITY_TYPE, "chant", {});
15
+
16
+ /**
17
+ * Is this entity an Op declaration?
18
+ *
19
+ * Duck-typed on `entityType` rather than `instanceof OpResource` for the same
20
+ * reason `../build.ts`'s dependency inference is: an entity can be built
21
+ * against a different copy of core (a linked lexicon, a child project) and
22
+ * still be the same declaration.
23
+ */
24
+ export function isOpEntity(entity: unknown): boolean {
25
+ return (entity as { entityType?: string } | null)?.entityType === OP_ENTITY_TYPE;
26
+ }
@@ -0,0 +1,203 @@
1
+ /**
2
+ * The op-runtime seam (#2121, epic #2114).
3
+ *
4
+ * An Op is a convergent verb; *where* it runs is a separate question from
5
+ * *what* it does. This module states that question once, as a provider
6
+ * contract, so `chant run` has one dispatch path instead of a branch per
7
+ * runtime. Core ships the `local` provider (./runtimes/local.ts) and never
8
+ * imports a hosting lexicon; a lexicon that hosts runs implements
9
+ * {@link OpRuntimeProvider} and hangs it off `LexiconPlugin.opRuntime`, where
10
+ * `chant run <op> --on <lexicon>` finds it.
11
+ *
12
+ * The record shapes below are the run ledger's (#2118): what a run wrote to
13
+ * `<env>/runs__<op>.jsonl` is what a provider's `status` and `log` read back,
14
+ * so a run's outcome survives the process that produced it. A provider with no
15
+ * ledger behind it (a hosting lexicon reporting a live run) fills the same
16
+ * shape from whatever it does know.
17
+ */
18
+
19
+ import type { OpConfig } from "./types";
20
+ import type { StepRecord, OpRunResult } from "./local-executor";
21
+ import type { GateResolutionRecord } from "../lifecycle/gate-ledger";
22
+ import type { RunComponentsOptions, RunComponentsResult } from "../components/cli-support";
23
+
24
+ /**
25
+ * How a run ended, or that it has not. `gated` is the state a run reaches when
26
+ * a gate has no recorded resolution — a fact, not a wait (#2119).
27
+ *
28
+ * This is the coarse state a *runtime* reports. The ledger records the
29
+ * executor's own three-state outcome instead ({@link OpRunRecord.status}),
30
+ * which has no `running` or `cancelled`: a record is only written once a run
31
+ * has settled.
32
+ */
33
+ export type OpRunState = "running" | "completed" | "failed" | "gated" | "cancelled";
34
+
35
+ /** Map a settled run's ledger status onto the coarser runtime state. */
36
+ export function runStateOf(status: OpRunRecord["status"]): OpRunState {
37
+ return status === "ok" ? "completed" : status === "gated" ? "gated" : "failed";
38
+ }
39
+
40
+ /** One step's outcome as the run ledger keeps it — `StepRecord` minus the fields only a live renderer needs. */
41
+ export interface OpRunStepRecord {
42
+ /** The activity function name, or `gate:<signal>` for a gate step. */
43
+ fn: string;
44
+ status: StepRecord["status"];
45
+ durationMs: number;
46
+ /** The step's `outcomeAttribute` capture, when it declared one. */
47
+ outcome?: { name: string; value: unknown };
48
+ /** Who cleared this gate step and when (#2119), for a gate the run passed. */
49
+ approval?: { gate: string; resolvedBy: string; timestamp: string; url?: string };
50
+ /** The failure message, for `status: "fail"`. */
51
+ error?: string;
52
+ }
53
+
54
+ /** One phase's steps and the verdict they add up to. */
55
+ export interface OpRunPhaseRecord {
56
+ name: string;
57
+ /** `fail` if any step failed, `skipped` if every step was skipped, else `ok`. */
58
+ status: "ok" | "fail" | "skipped";
59
+ steps: OpRunStepRecord[];
60
+ }
61
+
62
+ /**
63
+ * A run's current state, as every runtime reports it.
64
+ *
65
+ * `records` and `result` are populated by a runtime that has the executor's
66
+ * own step records to hand (the local one does, for a run it just executed in
67
+ * this process); a runtime answering from the ledger alone omits them rather
68
+ * than inventing them.
69
+ */
70
+ export interface OpRunStatus {
71
+ op: string;
72
+ runId: string;
73
+ state: OpRunState;
74
+ /** ISO-8601 instant the run started. */
75
+ startedAt: string;
76
+ /** ISO-8601 instant the run settled; absent while it is still running. */
77
+ endedAt?: string;
78
+ /** Per-step records, when the runtime has them. */
79
+ records?: StepRecord[];
80
+ /** The executor's own result, when the runtime is executing in this process. */
81
+ result?: OpRunResult;
82
+ /** The gate this run is waiting on, when `state` is `gated`. */
83
+ gate?: { name: string; since: string };
84
+ /** Free-text detail for a failed run. */
85
+ error?: string;
86
+ }
87
+
88
+ /**
89
+ * One immutable, settled Op run — the run ledger's line
90
+ * (`../lifecycle/run-ledger.ts`), and what `chant run log <op>` prints a row
91
+ * per.
92
+ *
93
+ * This is where a run's outcome lives now that `OpConfig` no longer carries
94
+ * `searchAttributes` for the generated workflow to upsert (#2118). `labels` is
95
+ * copied off the declaration so a reader can filter runs the way it filters
96
+ * declarations; `outcomes` is every `outcomeAttribute` the run captured, later
97
+ * steps winning over earlier ones for a repeated name, exactly as an upsert
98
+ * would have.
99
+ */
100
+ export interface OpRunRecord {
101
+ /** Schema version, so an incompatible future shape is detected before being misread. */
102
+ version: 1;
103
+ /** Stable run id — the same string a pending gate fact records for this run. */
104
+ id: string;
105
+ /** The Op's name (`OpConfig.name`). */
106
+ op: string;
107
+ /** The Op's `labels.Env`, or `local` when it declares none. */
108
+ env: string;
109
+ /** ISO-8601 instant the run started. */
110
+ started: string;
111
+ /** ISO-8601 instant the run settled. */
112
+ ended: string;
113
+ /**
114
+ * The executor's three-state outcome. `gated` is neither success nor
115
+ * failure: the run reached a gate nobody has approved, recorded the fact,
116
+ * and stopped (#2119).
117
+ */
118
+ status: "ok" | "fail" | "gated";
119
+ /** The Op's own `labels`, copied at run time. */
120
+ labels: Record<string, string>;
121
+ /** Every `outcomeAttribute` the run captured, name → value. */
122
+ outcomes: Record<string, unknown>;
123
+ /** Per-phase, per-step status, in execution order. */
124
+ phases: OpRunPhaseRecord[];
125
+ /** The gate the run stopped on, for `status: "gated"`. */
126
+ gate?: { name: string; since: string };
127
+ }
128
+
129
+ /** An {@link OpRunRecord} before the ledger stamps its version and mints an id. */
130
+ export type OpRunRecordInput = Omit<OpRunRecord, "version" | "id"> & { id?: string };
131
+
132
+ /** A started run. `result()` settles with the run's final status. */
133
+ export interface OpRunHandle {
134
+ op: string;
135
+ runId: string;
136
+ result(): Promise<OpRunStatus>;
137
+ }
138
+
139
+ /** What a caller hands a runtime when it starts a run. */
140
+ export interface OpRunStartOptions {
141
+ /** Build-time parameter bindings, when the caller resolved any. */
142
+ params?: Record<string, unknown>;
143
+ /** Target environment (`--env`). */
144
+ env?: string;
145
+ /**
146
+ * `-p, --profile <name>` (#2124, restored in #2192) — the named connection
147
+ * profile this runtime should target. Only a runtime that has profiles
148
+ * reads it; the local one ignores it, and a runtime that reads it decides
149
+ * for itself what an unknown name means (fountain refuses it rather than
150
+ * falling back to its default).
151
+ */
152
+ profile?: string;
153
+ /** Called once per settled step, so `--progress-json` can stream. */
154
+ progress?: (record: StepRecord) => void;
155
+ /** Aborts in-flight work (Ctrl-C). */
156
+ signal?: AbortSignal;
157
+ }
158
+
159
+ /**
160
+ * The contract a runtime implements to host Op runs.
161
+ *
162
+ * `chant run` resolves exactly one provider per invocation — the named
163
+ * lexicon's for `--on <name>`, the built-in `local` one otherwise — and every
164
+ * subcommand calls it. A provider that cannot do something (cancel a
165
+ * foreground run, host components) says so by throwing a message the CLI
166
+ * prints verbatim, or by omitting the optional member.
167
+ */
168
+ export interface OpRuntimeProvider {
169
+ /** How the CLI names this runtime in messages, and what `--on` matches. */
170
+ readonly name: string;
171
+
172
+ /** Start a run. Rejects with an actionable Error when it cannot. */
173
+ start(op: OpConfig, opts: OpRunStartOptions): Promise<OpRunHandle>;
174
+
175
+ /** The latest run's state, or `undefined` when this runtime has none recorded. */
176
+ status(op: string): Promise<OpRunStatus | undefined>;
177
+
178
+ /** Run history, newest first. Empty when nothing is recorded. */
179
+ log(op: string, opts?: { limit?: number }): Promise<OpRunRecord[]>;
180
+
181
+ /** Latest state for each of `ops`, keyed by op name. */
182
+ list(ops: OpConfig[]): Promise<Map<string, OpRunStatus | undefined>>;
183
+
184
+ /** Cancel the active run. `force` is the CLI's confirmation, already checked. */
185
+ cancel(op: string, opts: { force: boolean }): Promise<void>;
186
+
187
+ /**
188
+ * Wake a run whose gate has just been resolved. The ledger write is the
189
+ * fact and `chant approve` owns it; this is the runtime's chance to act on
190
+ * it. Omit when a run re-reads the ledger on its own.
191
+ */
192
+ resolveGate?(op: string, gate: string, resolution: GateResolutionRecord): Promise<void>;
193
+
194
+ /**
195
+ * Host `chant run --components`. Optional: a runtime without it refuses the
196
+ * flag with one line naming itself.
197
+ */
198
+ runComponents?(
199
+ projectPath: string,
200
+ selector: string,
201
+ options: RunComponentsOptions,
202
+ ): Promise<RunComponentsResult>;
203
+ }