@intentius/chant 0.57.0 → 0.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (504) hide show
  1. package/dist/audit/catalog.d.ts +51 -1
  2. package/dist/audit/catalog.d.ts.map +1 -1
  3. package/dist/audit/core.d.ts +20 -1
  4. package/dist/audit/core.d.ts.map +1 -1
  5. package/dist/audit/discover.d.ts +14 -8
  6. package/dist/audit/discover.d.ts.map +1 -1
  7. package/dist/audit/lineage.d.ts +10 -0
  8. package/dist/audit/lineage.d.ts.map +1 -0
  9. package/dist/audit/prior-art.d.ts +385 -0
  10. package/dist/audit/prior-art.d.ts.map +1 -0
  11. package/dist/audit/proof.d.ts +16 -0
  12. package/dist/audit/proof.d.ts.map +1 -1
  13. package/dist/audit/report-html.d.ts.map +1 -1
  14. package/dist/audit/report-model.d.ts +8 -1
  15. package/dist/audit/report-model.d.ts.map +1 -1
  16. package/dist/audit/report.d.ts.map +1 -1
  17. package/dist/audit/rules-doc.d.ts +3 -0
  18. package/dist/audit/rules-doc.d.ts.map +1 -1
  19. package/dist/audit/secrets.d.ts +15 -0
  20. package/dist/audit/secrets.d.ts.map +1 -1
  21. package/dist/audit/terraform-state.d.ts +46 -0
  22. package/dist/audit/terraform-state.d.ts.map +1 -0
  23. package/dist/build.d.ts +17 -1
  24. package/dist/build.d.ts.map +1 -1
  25. package/dist/claimed-fields.d.ts +161 -0
  26. package/dist/claimed-fields.d.ts.map +1 -0
  27. package/dist/cli/build-params-cli.d.ts +2 -2
  28. package/dist/cli/command-group.d.ts +14 -0
  29. package/dist/cli/command-group.d.ts.map +1 -1
  30. package/dist/cli/commands/audit.d.ts +17 -1
  31. package/dist/cli/commands/audit.d.ts.map +1 -1
  32. package/dist/cli/commands/build.d.ts.map +1 -1
  33. package/dist/cli/commands/check-lexicon-docs.d.ts +1 -1
  34. package/dist/cli/commands/check-lexicon-examples.d.ts +16 -6
  35. package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
  36. package/dist/cli/commands/init-lexicon/templates/codegen.d.ts +1 -0
  37. package/dist/cli/commands/init-lexicon/templates/codegen.d.ts.map +1 -1
  38. package/dist/cli/commands/init-lexicon/templates/project.d.ts +7 -0
  39. package/dist/cli/commands/init-lexicon/templates/project.d.ts.map +1 -1
  40. package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
  41. package/dist/cli/commands/init.d.ts.map +1 -1
  42. package/dist/cli/commands/lint.d.ts.map +1 -1
  43. package/dist/cli/commands/onboard.d.ts +2 -0
  44. package/dist/cli/commands/onboard.d.ts.map +1 -1
  45. package/dist/cli/handlers/build.d.ts.map +1 -1
  46. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  47. package/dist/cli/handlers/operator.d.ts +82 -16
  48. package/dist/cli/handlers/operator.d.ts.map +1 -1
  49. package/dist/cli/handlers/run.d.ts +49 -42
  50. package/dist/cli/handlers/run.d.ts.map +1 -1
  51. package/dist/cli/main.d.ts +28 -1
  52. package/dist/cli/main.d.ts.map +1 -1
  53. package/dist/cli/mcp/op-tools.d.ts +16 -1
  54. package/dist/cli/mcp/op-tools.d.ts.map +1 -1
  55. package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
  56. package/dist/cli/mcp/resources/context.d.ts.map +1 -1
  57. package/dist/cli/registry.d.ts +26 -9
  58. package/dist/cli/registry.d.ts.map +1 -1
  59. package/dist/codegen/generate-post-synth-barrel.d.ts +1 -1
  60. package/dist/components/auto-release.d.ts +10 -20
  61. package/dist/components/auto-release.d.ts.map +1 -1
  62. package/dist/components/capability.d.ts +4 -4
  63. package/dist/components/cli-support.d.ts +20 -21
  64. package/dist/components/cli-support.d.ts.map +1 -1
  65. package/dist/components/component.d.ts +3 -3
  66. package/dist/components/component.d.ts.map +1 -1
  67. package/dist/components/driver-output.d.ts.map +1 -1
  68. package/dist/components/driver.d.ts +66 -36
  69. package/dist/components/driver.d.ts.map +1 -1
  70. package/dist/components/index.d.ts +1 -2
  71. package/dist/components/index.d.ts.map +1 -1
  72. package/dist/components/manifest-persistence.d.ts +1 -9
  73. package/dist/components/manifest-persistence.d.ts.map +1 -1
  74. package/dist/components/run-progress.d.ts +6 -6
  75. package/dist/components/run-progress.d.ts.map +1 -1
  76. package/dist/components/verbs/run-agent.d.ts +9 -15
  77. package/dist/components/verbs/run-agent.d.ts.map +1 -1
  78. package/dist/components/verbs/verify.d.ts +2 -2
  79. package/dist/deep-observation.d.ts +17 -0
  80. package/dist/deep-observation.d.ts.map +1 -1
  81. package/dist/discovery/files.d.ts +4 -4
  82. package/dist/discovery/fold-import.d.ts.map +1 -1
  83. package/dist/discovery/param-deps.d.ts +33 -0
  84. package/dist/discovery/param-deps.d.ts.map +1 -1
  85. package/dist/discovery/sandbox/bundle.d.ts.map +1 -1
  86. package/dist/discovery/sandbox/config-wire.d.ts +2 -2
  87. package/dist/fold/foldable-helpers.d.ts +5 -1
  88. package/dist/fold/foldable-helpers.d.ts.map +1 -1
  89. package/dist/fold-provenance.d.ts +236 -0
  90. package/dist/fold-provenance.d.ts.map +1 -0
  91. package/dist/graph-ops.d.ts +1 -1
  92. package/dist/held-elsewhere.d.ts +85 -0
  93. package/dist/held-elsewhere.d.ts.map +1 -0
  94. package/dist/index.d.ts +3 -0
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/kubectl-context.d.ts +3 -3
  97. package/dist/lexicon-config.d.ts +4 -4
  98. package/dist/lexicon-plugin-helpers.d.ts +1 -1
  99. package/dist/lexicon.d.ts +104 -14
  100. package/dist/lexicon.d.ts.map +1 -1
  101. package/dist/lifecycle/change-set.d.ts +13 -0
  102. package/dist/lifecycle/change-set.d.ts.map +1 -1
  103. package/dist/lifecycle/deep-diff.d.ts +133 -6
  104. package/dist/lifecycle/deep-diff.d.ts.map +1 -1
  105. package/dist/lifecycle/deep-observe.d.ts.map +1 -1
  106. package/dist/lifecycle/gate-ledger.d.ts +71 -3
  107. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  108. package/dist/lifecycle/index.d.ts +1 -0
  109. package/dist/lifecycle/index.d.ts.map +1 -1
  110. package/dist/lifecycle/release-ledger.d.ts +2 -2
  111. package/dist/lifecycle/run-ledger.d.ts +53 -0
  112. package/dist/lifecycle/run-ledger.d.ts.map +1 -0
  113. package/dist/lifecycle/symptoms.d.ts +3 -3
  114. package/dist/lifecycle/symptoms.d.ts.map +1 -1
  115. package/dist/lint/config.d.ts +81 -2
  116. package/dist/lint/config.d.ts.map +1 -1
  117. package/dist/lint/post-synth.d.ts +55 -2
  118. package/dist/lint/post-synth.d.ts.map +1 -1
  119. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts +1 -1
  120. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts.map +1 -1
  121. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts +1 -1
  122. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts.map +1 -1
  123. package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts +44 -0
  124. package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts.map +1 -0
  125. package/dist/lint/rules/comp/index.d.ts +1 -1
  126. package/dist/lint/rules/comp/index.d.ts.map +1 -1
  127. package/dist/lint/rules/op/index.d.ts +16 -0
  128. package/dist/lint/rules/op/index.d.ts.map +1 -0
  129. package/dist/lint/rules/op/ops012-activity-contract.d.ts +27 -0
  130. package/dist/lint/rules/op/ops012-activity-contract.d.ts.map +1 -0
  131. package/dist/lint/rules/op/ops013-step-output-ref.d.ts +61 -0
  132. package/dist/lint/rules/op/ops013-step-output-ref.d.ts.map +1 -0
  133. package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts +55 -0
  134. package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts.map +1 -0
  135. package/dist/lint/rules/op/support.d.ts +12 -0
  136. package/dist/lint/rules/op/support.d.ts.map +1 -0
  137. package/dist/lint/suppressions.d.ts +108 -0
  138. package/dist/lint/suppressions.d.ts.map +1 -0
  139. package/dist/live-endpoint.d.ts +1 -1
  140. package/dist/op/activities/activity-contracts.d.ts +54 -0
  141. package/dist/op/activities/activity-contracts.d.ts.map +1 -0
  142. package/dist/op/activities/apply.d.ts +262 -0
  143. package/dist/op/activities/apply.d.ts.map +1 -0
  144. package/dist/op/activities/build.d.ts +13 -0
  145. package/dist/op/activities/build.d.ts.map +1 -0
  146. package/dist/op/activities/converge.d.ts +110 -0
  147. package/dist/op/activities/converge.d.ts.map +1 -0
  148. package/dist/op/activities/env-teardown.d.ts +76 -0
  149. package/dist/op/activities/env-teardown.d.ts.map +1 -0
  150. package/dist/op/activities/guard-validate.d.ts +64 -0
  151. package/dist/op/activities/guard-validate.d.ts.map +1 -0
  152. package/dist/op/activities/http-check.d.ts +34 -0
  153. package/dist/op/activities/http-check.d.ts.map +1 -0
  154. package/dist/op/activities/index.d.ts +31 -0
  155. package/dist/op/activities/index.d.ts.map +1 -0
  156. package/dist/op/activities/lexicon-upgrade.d.ts +182 -0
  157. package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -0
  158. package/dist/op/activities/lifecycle.d.ts +42 -0
  159. package/dist/op/activities/lifecycle.d.ts.map +1 -0
  160. package/dist/op/activities/pipeline-audit.d.ts +84 -0
  161. package/dist/op/activities/pipeline-audit.d.ts.map +1 -0
  162. package/dist/op/activities/policy.d.ts +17 -0
  163. package/dist/op/activities/policy.d.ts.map +1 -0
  164. package/dist/op/activities/reconcile.d.ts +89 -0
  165. package/dist/op/activities/reconcile.d.ts.map +1 -0
  166. package/dist/op/activities/shell.d.ts +13 -0
  167. package/dist/op/activities/shell.d.ts.map +1 -0
  168. package/dist/op/activities/teardown.d.ts +10 -0
  169. package/dist/op/activities/teardown.d.ts.map +1 -0
  170. package/dist/op/activities/util.d.ts +7 -0
  171. package/dist/op/activities/util.d.ts.map +1 -0
  172. package/dist/op/activities/wait.d.ts +28 -0
  173. package/dist/op/activities/wait.d.ts.map +1 -0
  174. package/dist/op/activities/workflow-audit.d.ts +92 -0
  175. package/dist/op/activities/workflow-audit.d.ts.map +1 -0
  176. package/dist/op/activity-contract-registry.d.ts +76 -0
  177. package/dist/op/activity-contract-registry.d.ts.map +1 -0
  178. package/dist/op/activity-contract.d.ts +25 -18
  179. package/dist/op/activity-contract.d.ts.map +1 -1
  180. package/dist/op/activity-failure.d.ts +28 -0
  181. package/dist/op/activity-failure.d.ts.map +1 -0
  182. package/dist/op/activity-profiles.d.ts +118 -0
  183. package/dist/op/activity-profiles.d.ts.map +1 -0
  184. package/dist/op/activity-registry.d.ts +22 -38
  185. package/dist/op/activity-registry.d.ts.map +1 -1
  186. package/dist/op/activity-runtime.d.ts +3 -14
  187. package/dist/op/activity-runtime.d.ts.map +1 -1
  188. package/dist/op/builders.d.ts +84 -29
  189. package/dist/op/builders.d.ts.map +1 -1
  190. package/dist/op/composites/apply-op.d.ts +113 -0
  191. package/dist/op/composites/apply-op.d.ts.map +1 -0
  192. package/dist/op/composites/converge-op.d.ts +120 -0
  193. package/dist/op/composites/converge-op.d.ts.map +1 -0
  194. package/dist/op/composites/index.d.ts +26 -0
  195. package/dist/op/composites/index.d.ts.map +1 -0
  196. package/dist/op/composites/lexicon-upgrade-op.d.ts +78 -0
  197. package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -0
  198. package/dist/op/composites/pipeline-audit-op.d.ts +50 -0
  199. package/dist/op/composites/pipeline-audit-op.d.ts.map +1 -0
  200. package/dist/op/composites/reconcile-op.d.ts +57 -0
  201. package/dist/op/composites/reconcile-op.d.ts.map +1 -0
  202. package/dist/op/composites/watch-op.d.ts +60 -0
  203. package/dist/op/composites/watch-op.d.ts.map +1 -0
  204. package/dist/op/composites/workflow-audit-op.d.ts +57 -0
  205. package/dist/op/composites/workflow-audit-op.d.ts.map +1 -0
  206. package/dist/op/converge-rule.d.ts +12 -10
  207. package/dist/op/converge-rule.d.ts.map +1 -1
  208. package/dist/op/cron.d.ts +51 -0
  209. package/dist/op/cron.d.ts.map +1 -0
  210. package/dist/op/discover.d.ts +9 -0
  211. package/dist/op/discover.d.ts.map +1 -1
  212. package/dist/op/duration.d.ts +11 -0
  213. package/dist/op/duration.d.ts.map +1 -0
  214. package/dist/op/emulator-lifecycle.d.ts.map +1 -1
  215. package/dist/op/gate.d.ts +90 -0
  216. package/dist/op/gate.d.ts.map +1 -0
  217. package/dist/op/generate-pipeline.d.ts +9 -0
  218. package/dist/op/generate-pipeline.d.ts.map +1 -1
  219. package/dist/op/index.d.ts +23 -7
  220. package/dist/op/index.d.ts.map +1 -1
  221. package/dist/op/local-executor.d.ts +114 -28
  222. package/dist/op/local-executor.d.ts.map +1 -1
  223. package/dist/op/local-output.d.ts +11 -1
  224. package/dist/op/local-output.d.ts.map +1 -1
  225. package/dist/op/op-ir.d.ts +184 -0
  226. package/dist/op/op-ir.d.ts.map +1 -0
  227. package/dist/op/op-verb-class.d.ts +10 -2
  228. package/dist/op/op-verb-class.d.ts.map +1 -1
  229. package/dist/op/operator.d.ts +30 -5
  230. package/dist/op/operator.d.ts.map +1 -1
  231. package/dist/op/receipt-store.d.ts +1 -1
  232. package/dist/op/receipt-store.d.ts.map +1 -1
  233. package/dist/op/resource.d.ts +15 -1
  234. package/dist/op/resource.d.ts.map +1 -1
  235. package/dist/op/runtime.d.ts +200 -0
  236. package/dist/op/runtime.d.ts.map +1 -0
  237. package/dist/op/runtimes/local.d.ts +29 -0
  238. package/dist/op/runtimes/local.d.ts.map +1 -0
  239. package/dist/op/step-output-ref.d.ts +15 -13
  240. package/dist/op/step-output-ref.d.ts.map +1 -1
  241. package/dist/op/types.d.ts +79 -27
  242. package/dist/op/types.d.ts.map +1 -1
  243. package/dist/ownership.d.ts +1 -1
  244. package/dist/provenance.d.ts +21 -0
  245. package/dist/provenance.d.ts.map +1 -1
  246. package/dist/serializer-walker.d.ts.map +1 -1
  247. package/package.json +6 -1
  248. package/src/audit/catalog.test.ts +79 -3
  249. package/src/audit/catalog.ts +111 -13
  250. package/src/audit/core.test.ts +26 -0
  251. package/src/audit/core.ts +61 -17
  252. package/src/audit/discover.test.ts +39 -0
  253. package/src/audit/discover.ts +91 -15
  254. package/src/audit/lineage.ts +100 -0
  255. package/src/audit/prior-art.ts +153 -0
  256. package/src/audit/proof.test.ts +67 -0
  257. package/src/audit/proof.ts +70 -0
  258. package/src/audit/report-html.ts +9 -4
  259. package/src/audit/report-model.ts +16 -2
  260. package/src/audit/report.test.ts +33 -0
  261. package/src/audit/report.ts +5 -3
  262. package/src/audit/rules-doc.test.ts +34 -2
  263. package/src/audit/rules-doc.ts +82 -9
  264. package/src/audit/secrets.ts +15 -3
  265. package/src/audit/terraform-state.test.ts +144 -0
  266. package/src/audit/terraform-state.ts +131 -0
  267. package/src/build.ts +31 -1
  268. package/src/claimed-fields.test.ts +145 -0
  269. package/src/claimed-fields.ts +208 -0
  270. package/src/cli/build-params-cli.ts +2 -2
  271. package/src/cli/command-group.ts +14 -0
  272. package/src/cli/commands/__fixtures__/audit-terraform/infra/.terraform/providers/lock.txt +1 -0
  273. package/src/cli/commands/__fixtures__/audit-terraform/infra/main.tf +1 -0
  274. package/src/cli/commands/__fixtures__/audit-terraform/infra/modules/foo/main.tf +1 -0
  275. package/src/cli/commands/__fixtures__/audit-terraform/infra/variables.tf +3 -0
  276. package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +13 -4
  277. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/docs-cli.ts +4 -0
  278. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/package.ts +1 -0
  279. package/src/cli/commands/__fixtures__/init-lexicon-output/src/package-cli.ts +21 -0
  280. package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +1 -0
  281. package/src/cli/commands/audit.test.ts +189 -11
  282. package/src/cli/commands/audit.ts +73 -27
  283. package/src/cli/commands/build.test.ts +292 -9
  284. package/src/cli/commands/build.ts +156 -24
  285. package/src/cli/commands/check-lexicon-docs.ts +1 -1
  286. package/src/cli/commands/check-lexicon-examples.test.ts +29 -1
  287. package/src/cli/commands/check-lexicon-examples.ts +42 -10
  288. package/src/cli/commands/init-lexicon/templates/codegen.ts +9 -0
  289. package/src/cli/commands/init-lexicon/templates/project.ts +44 -4
  290. package/src/cli/commands/init-lexicon.test.ts +97 -2
  291. package/src/cli/commands/init-lexicon.ts +4 -2
  292. package/src/cli/commands/init.test.ts +35 -1
  293. package/src/cli/commands/init.ts +14 -3
  294. package/src/cli/commands/lint.test.ts +48 -0
  295. package/src/cli/commands/lint.ts +123 -0
  296. package/src/cli/commands/onboard.test.ts +75 -0
  297. package/src/cli/commands/onboard.ts +54 -23
  298. package/src/cli/handlers/build.test.ts +81 -1
  299. package/src/cli/handlers/build.ts +15 -1
  300. package/src/cli/handlers/graph.test.ts +3 -3
  301. package/src/cli/handlers/lifecycle.test.ts +195 -12
  302. package/src/cli/handlers/lifecycle.ts +123 -6
  303. package/src/cli/handlers/misc.ts +1 -1
  304. package/src/cli/handlers/operator.test.ts +183 -11
  305. package/src/cli/handlers/operator.ts +260 -55
  306. package/src/cli/handlers/run.test.ts +431 -1287
  307. package/src/cli/handlers/run.ts +365 -915
  308. package/src/cli/main.test.ts +23 -15
  309. package/src/cli/main.ts +88 -33
  310. package/src/cli/mcp/op-tools.ts +147 -88
  311. package/src/cli/mcp/resource-handlers.ts +10 -37
  312. package/src/cli/mcp/resources/context.ts +11 -12
  313. package/src/cli/mcp/server.test.ts +25 -27
  314. package/src/cli/mcp/server.ts +2 -2
  315. package/src/cli/registry.ts +26 -9
  316. package/src/codegen/docs.ts +1 -1
  317. package/src/codegen/generate-post-synth-barrel.test.ts +1 -1
  318. package/src/codegen/generate-post-synth-barrel.ts +1 -1
  319. package/src/components/README.md +3 -2
  320. package/src/components/auto-release.test.ts +4 -20
  321. package/src/components/auto-release.ts +17 -42
  322. package/src/components/capability.ts +4 -4
  323. package/src/components/cli-support.test.ts +10 -5
  324. package/src/components/cli-support.ts +52 -44
  325. package/src/components/component.schema.json +2 -2
  326. package/src/components/component.ts +3 -3
  327. package/src/components/driver-output.ts +14 -2
  328. package/src/components/driver.test.ts +92 -22
  329. package/src/components/driver.ts +193 -83
  330. package/src/components/index.ts +1 -6
  331. package/src/components/manifest-persistence.test.ts +2 -20
  332. package/src/components/manifest-persistence.ts +4 -24
  333. package/src/components/pilots/pilots-lint-clean.test.ts +11 -10
  334. package/src/components/run-progress.ts +6 -6
  335. package/src/components/verbs/run-agent.ts +11 -17
  336. package/src/components/verbs/verify.ts +2 -2
  337. package/src/deep-observation.test.ts +75 -0
  338. package/src/deep-observation.ts +48 -0
  339. package/src/discovery/collect.test.ts +3 -3
  340. package/src/discovery/files.ts +4 -4
  341. package/src/discovery/fold-import.ts +105 -2
  342. package/src/discovery/param-deps.test.ts +112 -1
  343. package/src/discovery/param-deps.ts +193 -0
  344. package/src/discovery/sandbox/bundle.ts +15 -1
  345. package/src/discovery/sandbox/config-wire.test.ts +2 -2
  346. package/src/discovery/sandbox/config-wire.ts +2 -2
  347. package/src/fold/fold.test.ts +56 -0
  348. package/src/fold/foldable-helpers.ts +81 -1
  349. package/src/fold-provenance-exclusion.test.ts +186 -0
  350. package/src/fold-provenance.test.ts +495 -0
  351. package/src/fold-provenance.ts +441 -0
  352. package/src/graph-ops.test.ts +2 -2
  353. package/src/graph-ops.ts +1 -1
  354. package/src/held-elsewhere.test.ts +102 -0
  355. package/src/held-elsewhere.ts +114 -0
  356. package/src/index.ts +3 -0
  357. package/src/kubectl-context.ts +3 -3
  358. package/src/lexicon-config.test.ts +5 -5
  359. package/src/lexicon-config.ts +4 -4
  360. package/src/lexicon-plugin-helpers.ts +1 -1
  361. package/src/lexicon.ts +120 -14
  362. package/src/lifecycle/change-set.test.ts +49 -0
  363. package/src/lifecycle/change-set.ts +67 -0
  364. package/src/lifecycle/converge-ledger.ts +1 -1
  365. package/src/lifecycle/deep-diff.test.ts +267 -14
  366. package/src/lifecycle/deep-diff.ts +217 -14
  367. package/src/lifecycle/deep-observe.test.ts +3 -1
  368. package/src/lifecycle/deep-observe.ts +16 -6
  369. package/src/lifecycle/gate-ledger.test.ts +74 -2
  370. package/src/lifecycle/gate-ledger.ts +143 -36
  371. package/src/lifecycle/index.ts +1 -0
  372. package/src/lifecycle/release-ledger.ts +2 -2
  373. package/src/lifecycle/run-ledger.test.ts +268 -0
  374. package/src/lifecycle/run-ledger.ts +201 -0
  375. package/src/lifecycle/symptoms.ts +3 -3
  376. package/src/lint/config.test.ts +82 -1
  377. package/src/lint/config.ts +112 -3
  378. package/src/lint/policy.test.ts +1 -1
  379. package/src/lint/post-synth.test.ts +31 -0
  380. package/src/lint/post-synth.ts +54 -1
  381. package/src/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.ts +2 -2
  382. package/src/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.ts +1 -1
  383. package/src/lint/rules/comp/comp.test.ts +1 -1
  384. package/src/lint/rules/comp/comp004-gate-needs-durable-runtime.ts +74 -0
  385. package/src/lint/rules/comp/index.ts +3 -3
  386. package/src/lint/rules/op/index.ts +23 -0
  387. package/src/lint/rules/op/ops012-activity-contract.test.ts +161 -0
  388. package/src/lint/rules/op/ops012-activity-contract.ts +64 -0
  389. package/src/lint/rules/op/ops013-step-output-ref.test.ts +237 -0
  390. package/src/lint/rules/op/ops013-step-output-ref.ts +98 -0
  391. package/src/lint/rules/op/ops014-converge-rule-refusals.test.ts +263 -0
  392. package/src/lint/rules/op/ops014-converge-rule-refusals.ts +179 -0
  393. package/src/lint/rules/op/support.ts +11 -0
  394. package/src/lint/suppressions.test.ts +163 -0
  395. package/src/lint/suppressions.ts +204 -0
  396. package/src/live-endpoint.ts +1 -1
  397. package/src/op/activities/activity-contracts.ts +66 -0
  398. package/src/op/activities/apply.test.ts +567 -0
  399. package/src/op/activities/apply.ts +662 -0
  400. package/src/op/activities/build.ts +26 -0
  401. package/src/op/activities/converge.test.ts +330 -0
  402. package/src/op/activities/converge.ts +449 -0
  403. package/src/op/activities/env-teardown.test.ts +187 -0
  404. package/src/op/activities/env-teardown.ts +171 -0
  405. package/src/op/activities/guard-validate.test.ts +139 -0
  406. package/src/op/activities/guard-validate.ts +223 -0
  407. package/src/op/activities/http-check.test.ts +48 -0
  408. package/src/op/activities/http-check.ts +55 -0
  409. package/src/op/activities/index.ts +119 -0
  410. package/src/op/activities/lexicon-upgrade.test.ts +492 -0
  411. package/src/op/activities/lexicon-upgrade.ts +673 -0
  412. package/src/op/activities/lifecycle.ts +85 -0
  413. package/src/op/activities/pipeline-audit.test.ts +67 -0
  414. package/src/op/activities/pipeline-audit.ts +201 -0
  415. package/src/op/activities/policy.test.ts +19 -0
  416. package/src/op/activities/policy.ts +37 -0
  417. package/src/op/activities/reconcile.test.ts +81 -0
  418. package/src/op/activities/reconcile.ts +187 -0
  419. package/src/op/activities/shell.ts +26 -0
  420. package/src/op/activities/teardown.ts +22 -0
  421. package/src/op/activities/util.ts +6 -0
  422. package/src/op/activities/wait.ts +69 -0
  423. package/src/op/activities/workflow-audit.test.ts +90 -0
  424. package/src/op/activities/workflow-audit.ts +203 -0
  425. package/src/op/activity-contract-registry.test.ts +98 -0
  426. package/src/op/activity-contract-registry.ts +114 -0
  427. package/src/op/activity-contract.test.ts +1 -1
  428. package/src/op/activity-contract.ts +37 -33
  429. package/src/op/activity-failure.ts +34 -0
  430. package/src/op/activity-profiles.test.ts +56 -0
  431. package/src/op/activity-profiles.ts +116 -0
  432. package/src/op/activity-registry.test.ts +37 -28
  433. package/src/op/activity-registry.ts +27 -56
  434. package/src/op/activity-runtime.ts +3 -47
  435. package/src/op/builders-exports.test.ts +0 -49
  436. package/src/op/builders-typed.test.ts +164 -0
  437. package/src/op/builders.ts +123 -39
  438. package/src/op/composites/apply-op.ts +208 -0
  439. package/src/op/composites/composites.test.ts +366 -0
  440. package/src/op/composites/converge-op.test.ts +168 -0
  441. package/src/op/composites/converge-op.ts +214 -0
  442. package/src/op/composites/index.ts +26 -0
  443. package/src/op/composites/lexicon-upgrade-op.test.ts +68 -0
  444. package/src/op/composites/lexicon-upgrade-op.ts +134 -0
  445. package/src/op/composites/pipeline-audit-op.test.ts +25 -0
  446. package/src/op/composites/pipeline-audit-op.ts +78 -0
  447. package/src/op/composites/reconcile-op.test.ts +51 -0
  448. package/src/op/composites/reconcile-op.ts +107 -0
  449. package/src/op/composites/watch-op.ts +108 -0
  450. package/src/op/composites/workflow-audit-op.test.ts +25 -0
  451. package/src/op/composites/workflow-audit-op.ts +88 -0
  452. package/src/op/converge-rule.ts +12 -10
  453. package/src/op/cron.test.ts +128 -0
  454. package/src/op/cron.ts +149 -0
  455. package/src/op/discover-root.test.ts +105 -1
  456. package/src/op/discover.test.ts +6 -2
  457. package/src/op/discover.ts +84 -16
  458. package/src/op/duration.ts +21 -0
  459. package/src/op/effect-step.test.ts +26 -11
  460. package/src/op/emulator-lifecycle.ts +1 -2
  461. package/src/op/gate.ts +155 -0
  462. package/src/op/generate-pipeline.test.ts +65 -3
  463. package/src/op/generate-pipeline.ts +19 -2
  464. package/src/op/index.ts +39 -6
  465. package/src/op/local-executor.test.ts +163 -34
  466. package/src/op/local-executor.ts +370 -102
  467. package/src/op/local-output.test.ts +95 -11
  468. package/src/op/local-output.ts +33 -4
  469. package/src/op/op-ir.test.ts +281 -0
  470. package/src/op/op-ir.ts +417 -0
  471. package/src/op/op-verb-class.ts +19 -5
  472. package/src/op/op.test.ts +26 -5
  473. package/src/op/operator.test.ts +175 -6
  474. package/src/op/operator.ts +63 -16
  475. package/src/op/receipt-store.ts +1 -1
  476. package/src/op/resource.ts +20 -2
  477. package/src/op/runtime.ts +203 -0
  478. package/src/op/runtimes/local.test.ts +181 -0
  479. package/src/op/runtimes/local.ts +212 -0
  480. package/src/op/step-output-ref.ts +15 -13
  481. package/src/op/types.ts +84 -24
  482. package/src/ownership.ts +1 -1
  483. package/src/provenance.test.ts +6 -0
  484. package/src/provenance.ts +21 -1
  485. package/src/serializer-walker.test.ts +42 -0
  486. package/src/serializer-walker.ts +21 -2
  487. package/dist/cli/handlers/op-progress.d.ts +0 -90
  488. package/dist/cli/handlers/op-progress.d.ts.map +0 -1
  489. package/dist/cli/handlers/run-client.d.ts +0 -127
  490. package/dist/cli/handlers/run-client.d.ts.map +0 -1
  491. package/dist/cli/handlers/run-report.d.ts +0 -5
  492. package/dist/cli/handlers/run-report.d.ts.map +0 -1
  493. package/dist/components/temporal-codegen-loader.d.ts +0 -27
  494. package/dist/components/temporal-codegen-loader.d.ts.map +0 -1
  495. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts +0 -37
  496. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts.map +0 -1
  497. package/src/cli/handlers/op-progress.test.ts +0 -268
  498. package/src/cli/handlers/op-progress.ts +0 -248
  499. package/src/cli/handlers/run-client.test.ts +0 -82
  500. package/src/cli/handlers/run-client.ts +0 -219
  501. package/src/cli/handlers/run-report.test.ts +0 -62
  502. package/src/cli/handlers/run-report.ts +0 -122
  503. package/src/components/temporal-codegen-loader.ts +0 -41
  504. package/src/lint/rules/comp/comp004-gate-needs-temporal.ts +0 -66
@@ -233,14 +233,14 @@ describe("runGraph", () => {
233
233
  filePath: file,
234
234
  });
235
235
 
236
- test("an op discoverOps finds at the project root becomes a Temporal::Op node", async () => {
236
+ test("an op discoverOps finds at the project root becomes a Chant::Op node", async () => {
237
237
  lintClean(); discovered();
238
238
  discoverOpsMock.mockResolvedValue({ ops: new Map([["deploy", rootOp("/proj/deploy.op.ts")]]), errors: [] });
239
239
  const exit = await runGraph({ args: makeArgs({ format: "ir", path: "/proj", detail: 3 }), plugins: [], serializers: [] });
240
240
  expect(exit).toBe(0);
241
241
  const ir = JSON.parse(stdoutBuf.join("\n"));
242
242
  const node = ir.nodes.find((n: { id: string }) => n.id === "deploy");
243
- expect(node).toMatchObject({ kind: "Temporal::Op", lexicon: "temporal", sourceLoc: { file: "deploy.op.ts" } });
243
+ expect(node).toMatchObject({ kind: "Chant::Op", lexicon: "chant", sourceLoc: { file: "deploy.op.ts" } });
244
244
  expect(node.attrs.phases[0].name).toBe("Apply");
245
245
  expect(node.attrs.depends).toEqual(["infra"]);
246
246
  });
@@ -248,7 +248,7 @@ describe("runGraph", () => {
248
248
  test("an op discovery already loaded from sourceDir is not added twice", async () => {
249
249
  lintClean();
250
250
  const entities = sampleEntities();
251
- entities.set("deploy", decl({ lexicon: "temporal", entityType: "Temporal::Op", props: { name: "deploy", phases: [] } }));
251
+ entities.set("deploy", decl({ lexicon: "chant", entityType: "Chant::Op", props: { name: "deploy", phases: [] } }));
252
252
  discoverMock.mockResolvedValue({ entities, errors: [], dependencies: new Map(), sourceFiles: ["/proj/src/deploy.op.ts"] });
253
253
  discoverOpsMock.mockResolvedValue({ ops: new Map([["deploy", rootOp("/proj/src/deploy.op.ts")]]), errors: [] });
254
254
  const exit = await runGraph({ args: makeArgs({ format: "ir", path: "/proj", detail: 3 }), plugins: [], serializers: [] });
@@ -3,6 +3,7 @@ import { sep } from "node:path";
3
3
  import { createMockPlugin, staticDescribeResources, staticObservation, staticDeepObservation, staticListArtifacts } from "@intentius/chant-test-utils";
4
4
  import type { LexiconPlugin, ResourceMetadata } from "../../lexicon";
5
5
  import { deepObservation } from "../../deep-observation";
6
+ import { heldElsewhere } from "../../held-elsewhere";
6
7
  import type { BuildResult } from "../../build";
7
8
  import type { ParsedArgs } from "../registry";
8
9
 
@@ -565,12 +566,49 @@ describe("runLifecycleDiff --live", () => {
565
566
  } as never);
566
567
  };
567
568
 
568
- test("reports the changed property and the undeclared one", async () => {
569
+ test("reports the changed property as drift and the undeclared one as unclaimed", async () => {
569
570
  await runDiff([withDeep()]);
570
571
  const output = stdoutBuf.join("\n");
571
572
  expect(output).toContain("aws (properties)");
572
573
  expect(output).toContain("Versioning: Enabled → Suspended");
573
- expect(output).toContain("Logging.Target: <undeclared> → audit");
574
+ // #2160: source never set Logging.Target, so it is somebody else's field.
575
+ // Still reported, no longer a difference chant proposes to close.
576
+ expect(output).toContain("UNCLAIMED");
577
+ expect(output).toContain("Logging.Target: audit [not in this declaration's claimed fields]");
578
+ expect(output).toContain("1 property drift across 1 resource(s)");
579
+ expect(output).toContain("1 unclaimed");
580
+ });
581
+
582
+ // #2181 - the early-return guard tested the held count twice and never the
583
+ // unclaimed count, so a report whose only finding was a live value
584
+ // nobody declared printed nothing at all: no summary line, no section. That
585
+ // is the case #2160 was opened to surface.
586
+ test("a report whose only finding is a live value nobody declared still renders", async () => {
587
+ const plugins = [withDeep()];
588
+ const build = makeBuildResult({ aws: ["bucket"] });
589
+ // Declared exactly what is live, so there is no drift left to carry the
590
+ // section into the output.
591
+ build.entities.set("bucket", {
592
+ lexicon: "aws",
593
+ entityType: "AWS::S3::Bucket",
594
+ props: { Versioning: "Suspended" },
595
+ } as never);
596
+ buildMock.mockResolvedValue(build);
597
+ fetchLifecycleMock.mockResolvedValue(undefined);
598
+ readSnapshotMock.mockResolvedValue(null);
599
+
600
+ await runLifecycleDiff({
601
+ args: makeArgs({ command: "state", path: "diff", extraPositional: "prod", live: true }),
602
+ plugins,
603
+ serializers: plugins.map((p) => p.serializer),
604
+ } as never);
605
+
606
+ const output = stdoutBuf.join("\n");
607
+ expect(output).toContain("aws (properties)");
608
+ expect(output).toContain("0 property drift across 0 resource(s)");
609
+ expect(output).toContain("1 unclaimed");
610
+ expect(output).toContain("UNCLAIMED");
611
+ expect(output).toContain("Logging.Target: audit");
574
612
  });
575
613
 
576
614
  test("a lexicon with no deep reader prints nothing extra", async () => {
@@ -579,28 +617,36 @@ describe("runLifecycleDiff --live", () => {
579
617
  });
580
618
 
581
619
  test("an accepted deviation in the baseline stops re-alerting", async () => {
620
+ // A path source declares: since #2160 the baseline only ever has drift to
621
+ // suppress, because an undeclared path is unclaimed and never alerted.
582
622
  readBlobFromPathMock.mockResolvedValue(
583
623
  JSON.stringify({
584
624
  baseline: "v1",
585
625
  environment: "prod",
586
- lexicons: { aws: { bucket: { accepted: [{ path: "Logging.Target", value: "audit" }] } } },
626
+ lexicons: { aws: { bucket: { accepted: [{ path: "Versioning", value: "Suspended" }] } } },
587
627
  }),
588
628
  );
589
629
  await runDiff([withDeep()]);
590
630
  const output = stdoutBuf.join("\n");
591
- expect(output).toContain("Versioning: Enabled → Suspended");
592
- expect(output).not.toContain("Logging.Target: <undeclared>");
631
+ expect(output).not.toContain("PROPERTY DRIFT");
593
632
  expect(output).toContain("ACCEPTED (in the baseline; not drift)");
594
633
  });
595
634
 
596
- test("--json carries the property drift under the lexicon's `deep` key", async () => {
635
+ test("--json carries the property drift and the unclaimed fields under the lexicon's `deep` key", async () => {
597
636
  await runDiff([withDeep()], { json: true });
598
637
  const payload = JSON.parse(stdoutBuf.join("\n")) as {
599
- lexicons: { aws: { deep: { drifted: Array<{ changes: Array<{ path: string }> }> } } };
638
+ lexicons: {
639
+ aws: {
640
+ deep: {
641
+ drifted: Array<{ changes: Array<{ path: string }> }>;
642
+ unclaimed: Array<{ fields: Array<{ path: string; live: unknown; source: string }> }>;
643
+ };
644
+ };
645
+ };
600
646
  };
601
- expect(payload.lexicons.aws.deep.drifted[0].changes.map((c) => c.path).sort()).toEqual([
602
- "Logging.Target",
603
- "Versioning",
647
+ expect(payload.lexicons.aws.deep.drifted[0].changes.map((c) => c.path)).toEqual(["Versioning"]);
648
+ expect(payload.lexicons.aws.deep.unclaimed[0].fields).toEqual([
649
+ { path: "Logging.Target", live: "audit", source: "claimed-fields" },
604
650
  ]);
605
651
  });
606
652
 
@@ -628,9 +674,11 @@ describe("runLifecycleDiff --live", () => {
628
674
  const written = JSON.parse(content) as {
629
675
  lexicons: { aws: { bucket: { accepted: Array<{ path: string; value: unknown }> } } };
630
676
  };
631
- expect(written.lexicons.aws.bucket.accepted.map((a) => a.path)).toEqual(["Logging.Target", "Versioning"]);
677
+ // Only drift is accepted. A held field needs no acceptance: it was never
678
+ // reported as a deviation to begin with (#2160).
679
+ expect(written.lexicons.aws.bucket.accepted.map((a) => a.path)).toEqual(["Versioning"]);
632
680
  expect(pushLifecycleMock).toHaveBeenCalled();
633
- expect(stderrBuf.join("\n")).toContain("accepted 2 deviation(s)");
681
+ expect(stderrBuf.join("\n")).toContain("accepted 1 deviation(s)");
634
682
  });
635
683
 
636
684
  test("--update-baseline with nothing reported writes nothing", async () => {
@@ -642,6 +690,72 @@ describe("runLifecycleDiff --live", () => {
642
690
  });
643
691
  });
644
692
 
693
+ // #2162 — a `heldElsewhere()` marker reports as held, not drift, and a
694
+ // held property nothing ever writes is flagged suspicious on stderr.
695
+ describe("heldElsewhere() markers (#2162)", () => {
696
+ const runHeldDiff = async (
697
+ liveProperties: Record<string, unknown>,
698
+ args: Partial<ParsedArgs> = {},
699
+ ) => {
700
+ const build = makeBuildResult({ k8s: ["web"] });
701
+ build.entities.set("web", {
702
+ lexicon: "k8s",
703
+ entityType: "K8s::Apps::Deployment",
704
+ props: { spec: { replicas: heldElsewhere<number>({ by: "hpa", reason: "the autoscaler owns replicas after the first apply" }) } },
705
+ } as never);
706
+ buildMock.mockResolvedValue(build);
707
+ fetchLifecycleMock.mockResolvedValue(undefined);
708
+ readSnapshotMock.mockResolvedValue(null);
709
+ const plugins: LexiconPlugin[] = [
710
+ createMockPlugin({
711
+ name: "k8s",
712
+ describeResources: staticObservation({ web: meta({ type: "K8s::Apps::Deployment" }) }),
713
+ observeResourcesDeep: staticDeepObservation({
714
+ web: { type: "K8s::Apps::Deployment", properties: { spec: liveProperties } },
715
+ }),
716
+ }),
717
+ ];
718
+ return runLifecycleDiff({
719
+ args: makeArgs({ command: "state", path: "diff", extraPositional: "prod", live: true, ...args }),
720
+ plugins,
721
+ serializers: plugins.map((p) => p.serializer),
722
+ } as never);
723
+ };
724
+
725
+ test("a live value on the held path renders as HELD, not drift", async () => {
726
+ await runHeldDiff({ replicas: 5 });
727
+ const output = stdoutBuf.join("\n");
728
+ expect(output).toContain("HELD (declared heldElsewhere(); not drift, never proposed for update)");
729
+ expect(output).toContain("spec.replicas: held by hpa — 5 (the autoscaler owns replicas after the first apply)");
730
+ expect(output).not.toContain("PROPERTY DRIFT");
731
+ });
732
+
733
+ test("--json carries the held set under the lexicon's deep.held key", async () => {
734
+ await runHeldDiff({ replicas: 5 }, { json: true });
735
+ const payload = JSON.parse(stdoutBuf.join("\n")) as {
736
+ lexicons: { k8s: { deep: { held: Array<{ name: string; held: Array<{ path: string; by: string }> }> } } };
737
+ };
738
+ expect(payload.lexicons.k8s.deep.held).toEqual([
739
+ {
740
+ name: "web",
741
+ type: "K8s::Apps::Deployment",
742
+ held: [
743
+ { path: "spec.replicas", by: "hpa", reason: "the autoscaler owns replicas after the first apply", live: 5, suspicious: false },
744
+ ],
745
+ },
746
+ ]);
747
+ });
748
+
749
+ test("no live value at all is flagged suspicious, on the terminal and on stderr", async () => {
750
+ await runHeldDiff({});
751
+ const output = stdoutBuf.join("\n");
752
+ expect(output).toContain("SUSPICIOUS: no live value ever showed up here");
753
+ expect(stderrBuf.join("\n")).toContain(
754
+ 'web.spec.replicas is declared heldElsewhere(by: "hpa") but no live value ever showed up there',
755
+ );
756
+ });
757
+ });
758
+
645
759
  // #1166 — an environment can declare its own endpoint (a local emulator like
646
760
  // Floci), applied to the ambient var of every observing lexicon that has one
647
761
  // unless the ambient shell already set it.
@@ -799,6 +913,75 @@ describe("runLifecyclePlan", () => {
799
913
  expect(web.queried).toBe("/apis/apps/v1/namespaces/default/deployments/web");
800
914
  });
801
915
 
916
+ // #2162 — a held property is not an action, so the plan carries it beside
917
+ // `entries` rather than inside one, attributed to its lexicon the same way
918
+ // an entry is.
919
+ test("--json carries the held set beside entries, attributed to its lexicon", async () => {
920
+ const build = makeBuildResult({ k8s: ["web"] });
921
+ build.entities.set("web", {
922
+ lexicon: "k8s",
923
+ entityType: "K8s::Apps::Deployment",
924
+ props: { spec: { replicas: heldElsewhere<number>({ by: "hpa", reason: "the autoscaler owns replicas after the first apply" }) } },
925
+ } as never);
926
+ buildMock.mockResolvedValue(build);
927
+ const plugins: LexiconPlugin[] = [
928
+ createMockPlugin({
929
+ name: "k8s",
930
+ describeResources: staticObservation({ web: meta({ type: "K8s::Apps::Deployment" }) }),
931
+ observeResourcesDeep: staticDeepObservation({
932
+ web: { type: "K8s::Apps::Deployment", properties: { spec: { replicas: 5 } } },
933
+ }),
934
+ }),
935
+ ];
936
+ const exit = await runLifecyclePlan({
937
+ args: makeArgs({ path: "plan", extraPositional: "prod", json: true }),
938
+ plugins,
939
+ serializers: plugins.map((p) => p.serializer),
940
+ });
941
+ expect(exit).toBe(0);
942
+ const plan = JSON.parse(stdoutBuf.join("\n"));
943
+ expect(plan.entries.find((e: { name: string }) => e.name === "web").action).not.toBe("update");
944
+ expect(plan.held).toEqual([
945
+ {
946
+ name: "web",
947
+ type: "K8s::Apps::Deployment",
948
+ lexicon: "k8s",
949
+ held: [
950
+ { path: "spec.replicas", by: "hpa", reason: "the autoscaler owns replicas after the first apply", live: 5, suspicious: false },
951
+ ],
952
+ },
953
+ ]);
954
+ });
955
+
956
+ test("the human render carries a HELD section, and a suspicious one warns on stderr", async () => {
957
+ const build = makeBuildResult({ k8s: ["web"] });
958
+ build.entities.set("web", {
959
+ lexicon: "k8s",
960
+ entityType: "K8s::Apps::Deployment",
961
+ props: { spec: { replicas: heldElsewhere<number>({ by: "hpa", reason: "x" }) } },
962
+ } as never);
963
+ buildMock.mockResolvedValue(build);
964
+ const plugins: LexiconPlugin[] = [
965
+ createMockPlugin({
966
+ name: "k8s",
967
+ describeResources: staticObservation({ web: meta({ type: "K8s::Apps::Deployment" }) }),
968
+ observeResourcesDeep: staticDeepObservation({
969
+ web: { type: "K8s::Apps::Deployment", properties: { spec: {} } },
970
+ }),
971
+ }),
972
+ ];
973
+ const exit = await runLifecyclePlan({
974
+ args: makeArgs({ path: "plan", extraPositional: "prod" }),
975
+ plugins,
976
+ serializers: plugins.map((p) => p.serializer),
977
+ });
978
+ expect(exit).toBe(0);
979
+ expect(stdoutBuf.join("\n")).toContain("HELD (declared heldElsewhere(); not drift, never proposed for update)");
980
+ expect(stderrBuf.join("\n")).toContain(
981
+ 'web.spec.replicas is declared heldElsewhere(by: "hpa") but no live value ever showed up there',
982
+ );
983
+ });
984
+
802
985
  // #1674 — the plan merges every lexicon's change set into one flat
803
986
  // `entries[]`; each row must still say which lexicon observed it, and carry
804
987
  // the provider's physical id so an adopt/delete/runtime row named by a live
@@ -4,8 +4,9 @@ import { build } from "../../build";
4
4
  import { takeSnapshot } from "../../lifecycle/snapshot";
5
5
  import { readSnapshot, readSnapshotAt, readEnvironmentSnapshots, listSnapshots, fetchLifecycle, pushLifecycle, snapshotStorageKey, StaleLifecycleBranchError } from "../../lifecycle/git";
6
6
  import { deepDiffForLexicon, type DeclaredEntities } from "../../lifecycle/deep-observe";
7
- import { countPropertyDrift, type DeepDiffResult } from "../../lifecycle/deep-diff";
8
- import { describePathOrigin, getPathProvenance } from "../../provenance";
7
+ import { countUnclaimed, countPropertyDrift, countHeld, suspiciousHeld, type DeepDiffResult, type DeepEntityHeld } from "../../lifecycle/deep-diff";
8
+ import { describePathOrigin, getPathProvenance, getProvenance, type EntityProvenance } from "../../provenance";
9
+ import { resolveDeepDrift, resolveDriftedField, type FieldReconcile } from "../../fold-provenance";
9
10
  import {
10
11
  acceptDeviations,
11
12
  baselineForLexicon,
@@ -18,7 +19,7 @@ import {
18
19
  } from "../../lifecycle/observation-baseline";
19
20
  import { computeBuildDigest, diffDigests } from "../../lifecycle/digest";
20
21
  import { diffLive, diffLiveArtifacts, diffSnapshots, type LiveDiffResult, type LiveArtifactDiffResult, type SnapshotDiffResult } from "../../lifecycle/live-diff";
21
- import { buildChangeSet, renderChangeSet, renderChangeSetMarkdown, gitlabMrReport, unobservedPlanNotice, type ChangeSet } from "../../lifecycle/change-set";
22
+ import { buildChangeSet, renderChangeSet, renderChangeSetMarkdown, gitlabMrReport, unobservedPlanNotice, type ChangeSet, type HeldEntitySet } from "../../lifecycle/change-set";
22
23
  import { mergeReceiptEntries, observedValueResolver, planReceipts, readReceiptValue, type ReceiptReading } from "../../lifecycle/receipt-plan";
23
24
  import { annotateDisruption, disruptionNotices } from "../../lifecycle/disruption";
24
25
  import { collectEffectReceipts, isEffectReceipt } from "../../effect-receipt";
@@ -697,6 +698,13 @@ interface LiveDiffOutcome {
697
698
  unobserved?: Record<string, UnobservedEntity>;
698
699
  /** Property-level drift (#1014), present only for lexicons with a deep reader. */
699
700
  deep?: DeepDiffResult;
701
+ /**
702
+ * chant #2161 — one verdict per drifted field: what the fold provenance
703
+ * says produced it, and what the return leg may therefore propose. A
704
+ * sibling of `deep` rather than a field inside it, because `deep` is the
705
+ * comparison and this is what may be done about it.
706
+ */
707
+ reconcile?: FieldReconcile[];
700
708
  artifacts?: LiveArtifactDiffResult;
701
709
  /** What `listArtifacts` actually saw, keyed like `artifacts`' entries
702
710
  * (behold#146). The diff alone is snapshot-relative key lists — on a
@@ -882,8 +890,25 @@ async function runLifecycleDiffLive(args: LiveDiffArgs): Promise<LiveDiffOutcome
882
890
  // otherwise one unreadable entity is counted twice.
883
891
  totalUnobserved += deep.unobserved.filter((u) => !observed.unobserved[u.name]).length;
884
892
  if (args.updateBaseline) toAccept[lexiconName] = deviationsToAccept(deep);
885
- if (args.json) (byLexicon[lexiconName] ??= {}).deep = deep;
886
- else renderDeepDiff(lexiconName, deep);
893
+ // chant #2161 — the return leg, classified by fold provenance. Read off
894
+ // the build's own entities, which is where the record lives.
895
+ const provenanceOf = (name: string): EntityProvenance | undefined => {
896
+ const entity = args.buildResult.entities.get(name);
897
+ return entity ? getProvenance(entity) : undefined;
898
+ };
899
+ if (args.json) {
900
+ const entry = (byLexicon[lexiconName] ??= {});
901
+ entry.deep = deep;
902
+ entry.reconcile = resolveDeepDrift(deep.drifted, provenanceOf);
903
+ } else renderDeepDiff(lexiconName, deep, provenanceOf);
904
+ // Printed on stderr regardless of `--json` (#2162), same reason the
905
+ // unobserved/disruption notices are: a shape with no column for
906
+ // "suspicious" must not read as clean.
907
+ for (const s of suspiciousHeld(deep)) {
908
+ console.error(formatWarning({
909
+ message: `${lexiconName}: ${s.name}.${s.path} is declared heldElsewhere(by: "${s.by}") but no live value ever showed up there — the hand-over may never have happened.`,
910
+ }));
911
+ }
887
912
  }
888
913
  }
889
914
 
@@ -939,10 +964,18 @@ function deviationsToAccept(deep: DeepDiffResult): DeviationToAccept[] {
939
964
  }
940
965
 
941
966
  /** Property-level drift report (#1014). Silent when a lexicon's deep read found nothing to say. */
942
- function renderDeepDiff(lexiconName: string, deep: DeepDiffResult): void {
967
+ function renderDeepDiff(
968
+ lexiconName: string,
969
+ deep: DeepDiffResult,
970
+ provenanceOf: (entity: string) => EntityProvenance | undefined = () => undefined,
971
+ ): void {
943
972
  const drift = countPropertyDrift(deep);
973
+ const unclaimedCount = countUnclaimed(deep);
974
+ const heldCount = countHeld(deep);
944
975
  if (
945
976
  drift === 0 &&
977
+ heldCount === 0 &&
978
+ unclaimedCount === 0 &&
946
979
  deep.accepted.length === 0 &&
947
980
  deep.unobserved.length === 0 &&
948
981
  deep.undeclaredEntities.length === 0
@@ -955,6 +988,8 @@ function renderDeepDiff(lexiconName: string, deep: DeepDiffResult): void {
955
988
  console.log(
956
989
  `${drift} property drift across ${deep.drifted.length} resource(s), ` +
957
990
  `${acceptedCount} accepted, ${deep.unchanged.length} unchanged` +
991
+ (heldCount > 0 ? `, ${heldCount} held` : "") +
992
+ (unclaimedCount > 0 ? `, ${unclaimedCount} unclaimed` : "") +
958
993
  (deep.unobserved.length > 0 ? `, ${deep.unobserved.length} unobserved` : ""),
959
994
  );
960
995
  console.log("-".repeat(80));
@@ -967,6 +1002,7 @@ function renderDeepDiff(lexiconName: string, deep: DeepDiffResult): void {
967
1002
  console.log(formatBold("\nPROPERTY DRIFT (declared vs live; baseline shown where one exists):"));
968
1003
  for (const entity of deep.drifted) {
969
1004
  console.log(` - ${entity.name} (${entity.type})`);
1005
+ const provenance = provenanceOf(entity.name);
970
1006
  for (const change of entity.changes) {
971
1007
  const declared = "declared" in change ? formatValue(change.declared) : "<undeclared>";
972
1008
  const live = "live" in change ? formatValue(change.live) : "<absent>";
@@ -975,9 +1011,42 @@ function renderDeepDiff(lexiconName: string, deep: DeepDiffResult): void {
975
1011
  const from = change.origin ? ` [from: ${describePathOrigin(change.origin)}]` : "";
976
1012
  const owner = change.owner ? ` [owner: ${change.owner}]` : "";
977
1013
  console.log(` ${change.path}: ${declared} → ${live}${baseline}${from}${owner}`);
1014
+ // chant #2161 — what may be done about it, when the origin says
1015
+ // something today's behaviour would get wrong. A field the author
1016
+ // declared directly is left unremarked: editing it IS today's
1017
+ // behaviour, and saying so on every row would bury the two that matter.
1018
+ const { resolution } = resolveDriftedField({
1019
+ entity: entity.name,
1020
+ path: change.path,
1021
+ ...("declared" in change ? { declared: change.declared } : {}),
1022
+ ...("live" in change ? { live: change.live } : {}),
1023
+ ...(change.origin ? { origin: change.origin } : {}),
1024
+ ...(provenance ? { provenance } : {}),
1025
+ });
1026
+ if (resolution.kind !== "edit-declaration") {
1027
+ console.log(` ${resolution.description}`);
1028
+ }
978
1029
  }
979
1030
  }
980
1031
  }
1032
+ if (deep.unclaimed.length > 0) {
1033
+ // Not drift, and printed after it for that reason (#2160): chant never set
1034
+ // these fields, so nothing here is a change chant proposes to make.
1035
+ console.log(formatBold("\nUNCLAIMED (live values on properties chant never declared; not drift):"));
1036
+ for (const entity of deep.unclaimed) {
1037
+ console.log(` - ${entity.name} (${entity.type})`);
1038
+ for (const field of entity.fields) {
1039
+ const holder = field.heldBy
1040
+ ? `held by ${field.heldBy}`
1041
+ : "not in this declaration's claimed fields";
1042
+ console.log(` ${field.path}: ${formatValue(field.live)} [${holder}]`);
1043
+ }
1044
+ }
1045
+ }
1046
+ if (heldCount > 0) {
1047
+ console.log(formatBold("\nHELD (declared heldElsewhere(); not drift, never proposed for update):"));
1048
+ for (const line of renderHeldLines(deep.held)) console.log(line);
1049
+ }
981
1050
  if (deep.undeclaredEntities.length > 0) {
982
1051
  console.log(formatBold("\nUNDECLARED (read deeply, never declared in source):"));
983
1052
  for (const name of deep.undeclaredEntities) console.log(` - ${name}`);
@@ -990,6 +1059,30 @@ function renderDeepDiff(lexiconName: string, deep: DeepDiffResult): void {
990
1059
  }
991
1060
  }
992
1061
 
1062
+ /**
1063
+ * Render one `heldElsewhere()` property (#2162) — shared between the deep
1064
+ * diff's HELD section and the plan's, so the two never disagree about what a
1065
+ * held field looks like on the terminal.
1066
+ */
1067
+ function formatHeldProperty(h: { path: string; by: string; reason: string; live?: unknown; suspicious: boolean; owner?: string }): string {
1068
+ const live = "live" in h ? formatValue(h.live) : "<absent>";
1069
+ const owner = h.owner ? ` [owner: ${h.owner}]` : "";
1070
+ const suspicious = h.suspicious
1071
+ ? " [SUSPICIOUS: no live value ever showed up here — check the holder is actually running, or that this is the field it writes]"
1072
+ : "";
1073
+ return ` ${h.path}: held by ${h.by} — ${live}${owner} (${h.reason})${suspicious}`;
1074
+ }
1075
+
1076
+ /** Lines for every held property across a lexicon's entities, indented and grouped by entity name. */
1077
+ function renderHeldLines(held: readonly DeepEntityHeld[]): string[] {
1078
+ const lines: string[] = [];
1079
+ for (const entity of held) {
1080
+ lines.push(` - ${entity.name} (${entity.type})`);
1081
+ for (const h of entity.held) lines.push(formatHeldProperty(h));
1082
+ }
1083
+ return lines;
1084
+ }
1085
+
993
1086
  function renderLiveDiff(lexiconName: string, environment: string, diff: LiveDiffResult): void {
994
1087
  const counts =
995
1088
  `${diff.missing.length} missing, ${diff.orphan.length} orphan, ` +
@@ -1148,6 +1241,9 @@ export async function runLifecyclePlan(ctx: CommandContext): Promise<number> {
1148
1241
 
1149
1242
  const merged: ChangeSet = { env: environment, entries: [] };
1150
1243
  let checked = 0;
1244
+ // Held properties (#2162), across every lexicon this plan checks — not an
1245
+ // action, so it rides beside `entries` rather than inside it.
1246
+ const heldSets: HeldEntitySet[] = [];
1151
1247
 
1152
1248
  // Effect receipts (#1832): declared, diffed, and observed like any resource,
1153
1249
  // but observe-only to the generic apply path — the plan compares live value
@@ -1221,6 +1317,26 @@ export async function runLifecyclePlan(ctx: CommandContext): Promise<number> {
1221
1317
  });
1222
1318
 
1223
1319
  Object.assign(allObservedResources, observed.resources);
1320
+
1321
+ // Held properties (#2162): opt-in on the same capability the deep diff
1322
+ // path gates on. A held property is never a proposal, so it is
1323
+ // computed here (once per lexicon, against this same observation) and
1324
+ // carried on the plan beside `entries` rather than folded into one.
1325
+ if (plugin.observeResourcesDeep) {
1326
+ const deep = await deepDiffForLexicon(plugin, {
1327
+ environment,
1328
+ buildOutput,
1329
+ entities,
1330
+ componentStacks,
1331
+ });
1332
+ for (const entity of deep.held) heldSets.push({ ...entity, lexicon: lexiconName });
1333
+ for (const s of suspiciousHeld(deep)) {
1334
+ console.error(formatWarning({
1335
+ message: `${lexiconName}: ${s.name}.${s.path} is declared heldElsewhere(by: "${s.by}") but no live value ever showed up there — the hand-over may never have happened.`,
1336
+ }));
1337
+ }
1338
+ }
1339
+
1224
1340
  for (const [receiptName, receipt] of receipts) {
1225
1341
  if (receipt.lexicon !== lexiconName) continue;
1226
1342
  const live = observed.resources[receiptName];
@@ -1301,6 +1417,7 @@ export async function runLifecyclePlan(ctx: CommandContext): Promise<number> {
1301
1417
  }
1302
1418
 
1303
1419
  merged.entries.sort((a, b) => a.name.localeCompare(b.name));
1420
+ if (heldSets.length > 0) merged.held = heldSets.sort((a, b) => a.name.localeCompare(b.name));
1304
1421
 
1305
1422
  // Say it on stderr too, so `--json` and `--report gitlab-mr` consumers (whose
1306
1423
  // shapes have no room for it) still learn the plan has a hole (#1089). The
@@ -24,7 +24,7 @@ const CHANT_VERSION: string = (() => {
24
24
 
25
25
  const AUDIT_FORMATS: AuditFormat[] = ["stylish", "json", "sarif", "markdown", "html"];
26
26
  const AUDIT_TIERS: AuditTier[] = ["merge-worthy", "all"];
27
- const AUDIT_FAIL_ON: AuditFailOn[] = ["merge-worthy", "warning", "none"];
27
+ const AUDIT_FAIL_ON: AuditFailOn[] = ["merge-worthy", "warning", "error", "none"];
28
28
 
29
29
  /**
30
30
  * Parse `--scope system,user,project`. Returns `undefined` (meaning "all") when