@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
@@ -20,6 +20,17 @@
20
20
  * {@link UNRESOLVED} — an unevaluated intrinsic (`Fn::Sub`, `Ref`) has no
21
21
  * source-side value to compare, and reporting one as drift would light up every
22
22
  * interpolated property forever.
23
+ *
24
+ * ## The claim decides what counts as drift (#2160)
25
+ *
26
+ * The declared tree is not only one side of a comparison, it is a statement of
27
+ * which fields chant ever set — the claimed-field set (../claimed-fields.ts).
28
+ * A live value on a path outside it is somebody else's field: an autoscaler's
29
+ * replica count, a controller's annotation, a value a person typed into a
30
+ * console. It is reported in {@link DeepDiffResult.unclaimed}, with the
31
+ * manager's name where the substrate records one, and it is not drift. Only a
32
+ * claimed path that moved is, and drift is the only thing that may become an
33
+ * update.
23
34
  */
24
35
 
25
36
  import {
@@ -29,6 +40,8 @@ import {
29
40
  type DeepNormalizationHooks,
30
41
  type NormalizedDeepObservation,
31
42
  } from "../deep-observation";
43
+ import { claimedFieldsFromPaths, heldBy, isClaimed, type FieldClaimSource } from "../claimed-fields";
44
+ import { isNormalizedHeldElsewhere } from "../held-elsewhere";
32
45
  import { originOfPath, type PathOrigin } from "../provenance";
33
46
  import type { UnobservedResource } from "./live-diff";
34
47
  import { acceptedDeviation, type BaselineLexicon } from "./observation-baseline";
@@ -37,20 +50,30 @@ import { acceptedDeviation, type BaselineLexicon } from "./observation-baseline"
37
50
  * How a property differs.
38
51
  *
39
52
  * - `changed` — declared and live both have the path, with different values.
40
- * - `undeclared` — live has it, source never did. cdk-real-drift's whole reason
41
- * to exist: the console-added property CloudFormation itself will not report.
53
+ * The only kind that may become an update.
42
54
  * - `absent` — source declares it and the live tree does not carry it. Weaker
43
- * than the other two: a provider that omits a property it considers unset is
55
+ * than `changed`: a provider that omits a property it considers unset is
44
56
  * common, which is what the lexicon's pruning hook is for.
57
+ *
58
+ * A live value on a path source never declared used to be a third kind,
59
+ * `undeclared`. It is no longer drift at all (#2160): chant never set that
60
+ * field, so somebody else holds it, and it is reported in
61
+ * {@link DeepDiffResult.unclaimed} instead. It is still reported — pruning
62
+ * what chant cannot attribute is how #1191 lost a console-added label — it just
63
+ * stopped being a difference chant proposes to close.
45
64
  */
46
- export type PropertyDriftKind = "changed" | "undeclared" | "absent";
65
+ export type PropertyDriftKind = "changed" | "absent";
47
66
 
48
67
  /** One property-level difference. */
49
68
  export interface PropertyDrift {
50
69
  /** Path within the normalized property tree (`Tags[0].Value`). */
51
70
  path: string;
52
71
  kind: PropertyDriftKind;
53
- /** Value in source. Absent for `undeclared`. */
72
+ /**
73
+ * Value in source. Always present since #2160 — a drift row is only ever
74
+ * raised for a path the declaration claims — and kept optional for the wire
75
+ * shape consumers already branch on.
76
+ */
54
77
  declared?: unknown;
55
78
  /** Value in the cloud. Absent for `absent`. */
56
79
  live?: unknown;
@@ -64,7 +87,8 @@ export interface PropertyDrift {
64
87
  * The field manager that owns this path live, where the substrate records one
65
88
  * (#1189) — Kubernetes' `managedFields`, and nowhere else today.
66
89
  *
67
- * `kind` says a path is `undeclared` or `changed`; this says who did it.
90
+ * `kind` says whether a claimed path changed or went missing; this says who
91
+ * holds it live.
68
92
  * "Owned by `kubectl-client-side-apply`" and "owned by `hpa-controller`" are
69
93
  * the same `kind` and mean opposite things: one is somebody bypassing the
70
94
  * pipeline, the other is a controller doing its job. Absent on a substrate
@@ -92,6 +116,76 @@ export interface DeepEntityDrift {
92
116
  changes: PropertyDrift[];
93
117
  }
94
118
 
119
+ /**
120
+ * One live property value on a path this declaration never claimed (#2160) —
121
+ * a field that exists and that chant did not set.
122
+ *
123
+ * Reported, never proposed. The interesting column is {@link source}: on
124
+ * Kubernetes the API server names the manager and this row says
125
+ * `hpa-controller`; everywhere else the declaration is the only witness and the
126
+ * row says "not mine" without saying whose.
127
+ */
128
+ export interface PropertyUnclaimed {
129
+ /** Path within the normalized property tree (`spec.replicas`). */
130
+ path: string;
131
+ /** The value the cloud is carrying. */
132
+ live: unknown;
133
+ /**
134
+ * The field manager holding it, where the substrate records one. Absent on
135
+ * every substrate but Kubernetes, and absent on Kubernetes for a path no
136
+ * `managedFields` entry covers.
137
+ */
138
+ heldBy?: string;
139
+ /** Which source answered: the substrate's manager, or the claimed-field set. */
140
+ source: FieldClaimSource;
141
+ /**
142
+ * The accepted value from the baseline, when this path has one. An unclaimed
143
+ * field needs no acceptance to stay quiet, so this is carried for continuity with
144
+ * baselines recorded before #2160 rather than because it changes anything.
145
+ */
146
+ baseline?: unknown;
147
+ }
148
+
149
+ /** Live property values one declared entity is not claiming. */
150
+ export interface DeepEntityUnclaimed {
151
+ name: string;
152
+ type: string;
153
+ fields: PropertyUnclaimed[];
154
+ }
155
+
156
+ /**
157
+ * One property declared `heldElsewhere()` (#2162) — a fact about who owns the
158
+ * field at runtime, reported beside drift rather than folded into it. Never a
159
+ * {@link PropertyDrift}: a held property is never `changed`, `undeclared`, or
160
+ * `absent`, and is never proposed for update.
161
+ */
162
+ export interface PropertyHeld {
163
+ /** Path within the normalized property tree, same addressing as {@link PropertyDrift.path}. */
164
+ path: string;
165
+ /** Who holds it — the marker's `by`. */
166
+ by: string;
167
+ /** Why chant does not reconcile it — the marker's `reason`. */
168
+ reason: string;
169
+ /** The live value, when the deep read found one at this path. */
170
+ live?: unknown;
171
+ /**
172
+ * True when this path carried no live value at all — the deep read found
173
+ * nothing here, not even a provider default. A declared hand-over with no
174
+ * evidence anything was ever written is itself worth a look: either the
175
+ * holder never ran, or this is not the field it actually writes.
176
+ */
177
+ suspicious: boolean;
178
+ /** The field manager that owns this path live, where the substrate records one (#1189). See {@link PropertyDrift.owner}. */
179
+ owner?: string;
180
+ }
181
+
182
+ /** Held properties (#2162) for one declared entity. */
183
+ export interface DeepEntityHeld {
184
+ name: string;
185
+ type: string;
186
+ held: PropertyHeld[];
187
+ }
188
+
95
189
  export interface DeepDiffResult {
96
190
  /** Entities with at least one reportable property difference. Sorted by name. */
97
191
  drifted: DeepEntityDrift[];
@@ -101,6 +195,28 @@ export interface DeepDiffResult {
101
195
  * being held back and the count never silently changes meaning.
102
196
  */
103
197
  accepted: DeepEntityDrift[];
198
+ /**
199
+ * Live values on paths nobody declared (#2160), per entity. Sorted by name.
200
+ *
201
+ * Not drift, and deliberately a sibling of `drifted` rather than a kind
202
+ * inside it: `countPropertyDrift` does not see this list, `--update-baseline`
203
+ * does not record it, and nothing downstream may turn one of these into an
204
+ * update. It exists so the report can still say the field is there and who
205
+ * has it.
206
+ *
207
+ * Not `held`, which is the opposite fact: `held` is a field source declared
208
+ * and then handed over with `heldElsewhere()`, and this is a field source
209
+ * never declared at all.
210
+ */
211
+ unclaimed: DeepEntityUnclaimed[];
212
+ /**
213
+ * Properties declared `heldElsewhere()` (#2162) — reported here instead of
214
+ * in `drifted`/`accepted`, whatever the live value is. Not a suppression:
215
+ * every held property is listed, with its holder and reason, so a reader of
216
+ * the plan sees which fields are not being managed and by whom. Sorted by
217
+ * name.
218
+ */
219
+ held: DeepEntityHeld[];
104
220
  /** Entities whose property trees matched. Sorted. */
105
221
  unchanged: string[];
106
222
  /** Declared entities whose *properties* could not be read (#1089). Sorted. */
@@ -144,6 +260,8 @@ export function diffDeep(input: DiffDeepInput): DeepDiffResult {
144
260
  const baseline = input.baseline ?? {};
145
261
  const drifted: DeepEntityDrift[] = [];
146
262
  const accepted: DeepEntityDrift[] = [];
263
+ const unclaimed: DeepEntityUnclaimed[] = [];
264
+ const held: DeepEntityHeld[] = [];
147
265
  const unchanged: string[] = [];
148
266
  const unobserved: UnobservedResource[] = [];
149
267
  const undeclaredEntities: string[] = [];
@@ -187,39 +305,85 @@ export function diffDeep(input: DiffDeepInput): DeepDiffResult {
187
305
  hooks: input.hooks,
188
306
  });
189
307
 
308
+ // The claim (#2160): every path this declaration's props flatten to, in the
309
+ // grammar this loop addresses paths by. Derived here rather than read off
310
+ // `liveEntity.claimedFields` so `diffDeep` stays a pure function of its two
311
+ // trees; `lifecycle/deep-observe.ts` computes the identical set from the
312
+ // identical normalized tree and carries it on the observation for consumers.
313
+ const claimed = claimedFieldsFromPaths(declaredFlat.keys());
314
+
190
315
  const paths = [...new Set([...declaredFlat.keys(), ...liveFlat.keys()])].sort();
191
316
  const reported: PropertyDrift[] = [];
192
317
  const suppressed: PropertyDrift[] = [];
318
+ const unclaimedFields: PropertyUnclaimed[] = [];
319
+ const heldHere: PropertyHeld[] = [];
193
320
 
194
321
  for (const path of paths) {
195
- const hasDeclared = declaredFlat.has(path);
322
+ const hasDeclared = isClaimed(claimed, path);
196
323
  const hasLive = liveFlat.has(path);
197
324
  const declaredValue = declaredFlat.get(path);
198
325
  const liveValue = liveFlat.get(path);
199
326
 
327
+ // A `heldElsewhere()` marker (#2162): never drift, whatever the live
328
+ // value is (or isn't) — reported in its own section instead, with its
329
+ // holder and reason, and never proposed for update. Checked ahead of
330
+ // the UNRESOLVED/equality shortcuts below: a held path is not "no
331
+ // source-side value" (UNRESOLVED) and its declared side will never
332
+ // structurally equal a live value, so without this check every held
333
+ // property would fall through and report as ordinary `changed` drift.
334
+ if (hasDeclared && isNormalizedHeldElsewhere(declaredValue)) {
335
+ heldHere.push({
336
+ path,
337
+ by: declaredValue.by,
338
+ reason: declaredValue.reason,
339
+ ...(hasLive ? { live: liveValue } : {}),
340
+ // No live value at all is the one thing chant can check in a
341
+ // single observation: a claimed hand-over that produced no
342
+ // evidence — not even a provider default — is worth a look.
343
+ suspicious: !hasLive,
344
+ ...(hasLive && liveEntity.fieldOwners?.[path] ? { owner: liveEntity.fieldOwners[path] } : {}),
345
+ });
346
+ continue;
347
+ }
348
+
200
349
  // An unevaluated intrinsic has no source-side value to compare against.
201
350
  if (hasDeclared && declaredValue === UNRESOLVED) continue;
202
351
  if (hasDeclared && hasLive && deepValueEqual(declaredValue, liveValue)) continue;
203
352
 
204
- const kind: PropertyDriftKind = !hasDeclared ? "undeclared" : !hasLive ? "absent" : "changed";
353
+ const acceptedEntry = acceptedDeviation(baseline, name, path);
354
+
355
+ // A live value on a path nobody claimed is somebody else's field, not a
356
+ // difference chant proposes to close (#2160). Reported, never drift,
357
+ // never a candidate for an update — and the manager name, where the
358
+ // substrate records one, is the whole answer an operator wants.
359
+ if (!hasDeclared) {
360
+ const { holder, source } = heldBy(liveEntity.fieldOwners, path);
361
+ unclaimedFields.push({
362
+ path,
363
+ live: liveValue,
364
+ ...(holder ? { heldBy: holder } : {}),
365
+ source,
366
+ ...(acceptedEntry ? { baseline: acceptedEntry.value } : {}),
367
+ });
368
+ continue;
369
+ }
370
+
371
+ const kind: PropertyDriftKind = !hasLive ? "absent" : "changed";
205
372
  // Who owns the path live, where the substrate records it (#1189). Only
206
373
  // meaningful for a path that exists live — an `absent` drift has no live
207
374
  // field for anyone to own.
208
375
  const owner = hasLive ? liveEntity.fieldOwners?.[path] : undefined;
209
- // The declared-side counterpart (#1443). Only meaningful for a path
210
- // source actually declares — an `undeclared` drift has no authored
211
- // expression for anything to have produced.
212
- const origin = hasDeclared ? originOfPath(declaredEntity.pathOrigins, path) : undefined;
376
+ // The declared-side counterpart (#1443).
377
+ const origin = originOfPath(declaredEntity.pathOrigins, path);
213
378
  const drift: PropertyDrift = {
214
379
  path,
215
380
  kind,
216
- ...(hasDeclared ? { declared: declaredValue } : {}),
381
+ declared: declaredValue,
217
382
  ...(hasLive ? { live: liveValue } : {}),
218
383
  ...(owner ? { owner } : {}),
219
384
  ...(origin ? { origin } : {}),
220
385
  };
221
386
 
222
- const acceptedEntry = acceptedDeviation(baseline, name, path);
223
387
  if (acceptedEntry) {
224
388
  drift.baseline = acceptedEntry.value;
225
389
  // Value-bound acceptance: the accepted value is not drift, a different
@@ -233,13 +397,20 @@ export function diffDeep(input: DiffDeepInput): DeepDiffResult {
233
397
  }
234
398
 
235
399
  if (suppressed.length > 0) accepted.push({ name, type, changes: suppressed });
400
+ if (unclaimedFields.length > 0) unclaimed.push({ name, type, fields: unclaimedFields });
236
401
  if (reported.length > 0) drifted.push({ name, type, changes: reported });
402
+ // Held fields do not disqualify an entity from `unchanged`: every property
403
+ // chant declared matches, and the epic's whole point is that a controller's
404
+ // field stops being a finding chant restates on every tick.
237
405
  else if (suppressed.length === 0) unchanged.push(name);
406
+ if (heldHere.length > 0) held.push({ name, type, held: heldHere });
238
407
  }
239
408
 
240
409
  return {
241
410
  drifted: drifted.sort((a, b) => a.name.localeCompare(b.name)),
242
411
  accepted: accepted.sort((a, b) => a.name.localeCompare(b.name)),
412
+ unclaimed: unclaimed.sort((a, b) => a.name.localeCompare(b.name)),
413
+ held: held.sort((a, b) => a.name.localeCompare(b.name)),
243
414
  unchanged: unchanged.sort(),
244
415
  unobserved: unobserved.sort((a, b) => a.name.localeCompare(b.name)),
245
416
  undeclaredEntities: undeclaredEntities.sort(),
@@ -250,3 +421,35 @@ export function diffDeep(input: DiffDeepInput): DeepDiffResult {
250
421
  export function countPropertyDrift(result: DeepDiffResult): number {
251
422
  return result.drifted.reduce((n, e) => n + e.changes.length, 0);
252
423
  }
424
+
425
+ /** Total live values on paths no declaration claimed, across every entity (#2160). Never added to the drift count. */
426
+ export function countUnclaimed(result: DeepDiffResult): number {
427
+ return result.unclaimed.reduce((n, e) => n + e.fields.length, 0);
428
+ }
429
+
430
+ /** Total held properties (#2162) across every entity. */
431
+ export function countHeld(result: DeepDiffResult): number {
432
+ return result.held.reduce((n, e) => n + e.held.length, 0);
433
+ }
434
+
435
+ /** One held property flagged suspicious (#2162), with its entity attached. */
436
+ export interface SuspiciousHeld extends PropertyHeld {
437
+ name: string;
438
+ type: string;
439
+ }
440
+
441
+ /**
442
+ * Every held property whose declaration looks like a hand-over that never
443
+ * happened — no live value ever showed up for it. Worth a look: either the
444
+ * named holder never ran, or this is not the field it actually writes.
445
+ * Sorted by entity name, then path.
446
+ */
447
+ export function suspiciousHeld(result: DeepDiffResult): SuspiciousHeld[] {
448
+ const out: SuspiciousHeld[] = [];
449
+ for (const entity of result.held) {
450
+ for (const h of entity.held) {
451
+ if (h.suspicious) out.push({ name: entity.name, type: entity.type, ...h });
452
+ }
453
+ }
454
+ return out.sort((a, b) => a.name.localeCompare(b.name) || a.path.localeCompare(b.path));
455
+ }
@@ -156,10 +156,12 @@ describe("deepDiffForLexicon", () => {
156
156
  observeResourcesDeep: async () =>
157
157
  deepObservation({ a: { type: "T", properties: { Extra: "accepted-value" } } }),
158
158
  });
159
+ // A path source declares: since #2160 the baseline only ever suppresses
160
+ // drift, and a path nobody declared never reaches it (it is unclaimed).
159
161
  const opts = {
160
162
  environment: "prod",
161
163
  buildOutput: "",
162
- entities: entities({ a: { entityType: "T", props: {} } }),
164
+ entities: entities({ a: { entityType: "T", props: { Extra: "ours" } } }),
163
165
  };
164
166
  const withoutBaseline = await deepDiffForLexicon(plugin, opts);
165
167
  expect(withoutBaseline.drifted[0].changes[0].path).toBe("Extra");
@@ -26,6 +26,7 @@ import {
26
26
  type DeepResourceObservation,
27
27
  type NormalizedDeepObservation,
28
28
  } from "../deep-observation";
29
+ import { claimedFieldPaths, claimedFieldsOfTree } from "../claimed-fields";
29
30
  import { unobservedAll, type UnobservedEntity } from "../observation";
30
31
  import type { PathOrigin } from "../provenance";
31
32
  import { diffDeep, type DeclaredDeepEntity, type DeepDiffResult } from "./deep-diff";
@@ -140,14 +141,15 @@ export function diffDeepObservation(
140
141
  const declaredPaths = deepPathSet(declaredRaw);
141
142
  const livePaths = deepPathSet(liveRaw);
142
143
 
144
+ const declaredProperties = normalizeDeepProperties(declaredRaw, {
145
+ entityType: entity.entityType,
146
+ side: "declared",
147
+ hooks,
148
+ counterpartPaths: livePaths,
149
+ });
143
150
  declared[name] = {
144
151
  type: entity.entityType,
145
- properties: normalizeDeepProperties(declaredRaw, {
146
- entityType: entity.entityType,
147
- side: "declared",
148
- hooks,
149
- counterpartPaths: livePaths,
150
- }),
152
+ properties: declaredProperties,
151
153
  ...(entity.pathOrigins ? { pathOrigins: entity.pathOrigins } : {}),
152
154
  };
153
155
  if (liveEntity) {
@@ -158,6 +160,14 @@ export function diffDeepObservation(
158
160
  // up by the flattened path, so a keyed list element (`[#name]`) has
159
161
  // no owner today — its raw index path is what the reader recorded.
160
162
  ...(liveEntity.fieldOwners ? { fieldOwners: liveEntity.fieldOwners } : {}),
163
+ // The claimed-field set (#2160), computed from the declaration and
164
+ // carried on the observation so a consumer holding only this envelope
165
+ // can classify a live value three ways rather than two. Same normalized
166
+ // tree and same hooks `diffDeep` flattens, so the two never disagree
167
+ // about a path.
168
+ claimedFields: claimedFieldPaths(
169
+ claimedFieldsOfTree(declaredProperties, { entityType: entity.entityType, ...(hooks ? { hooks } : {}) }),
170
+ ),
161
171
  properties: normalizeDeepProperties(liveRaw, {
162
172
  entityType: liveEntity.type || entity.entityType,
163
173
  side: "live",
@@ -3,8 +3,12 @@ import { withTestDir } from "@intentius/chant-test-utils";
3
3
  import { spawnSync } from "node:child_process";
4
4
  import { writeFileSync } from "node:fs";
5
5
  import { join } from "node:path";
6
- import { appendGateResolution, readGateResolutions, latestResolutionSince, resolveApprovalUrl, isApprovalUrl } from "./gate-ledger";
7
- import { readBlobFromPath } from "./git";
6
+ import {
7
+ appendGateResolution, appendPendingGate, readGateResolutions, readGateLedger,
8
+ latestResolutionSince, latestPendingGate, isPendingGateExpired,
9
+ resolveApprovalUrl, isApprovalUrl, type PendingGateRecord,
10
+ } from "./gate-ledger";
11
+ import { readBlobFromPath, writeBlobToPath } from "./git";
8
12
 
9
13
  function git(args: string[], cwd: string): { stdout: string; exitCode: number } {
10
14
  const r = spawnSync("git", args, { cwd, encoding: "utf-8" });
@@ -181,3 +185,71 @@ describe("lifecycle/gate-ledger", () => {
181
185
  });
182
186
  });
183
187
  });
188
+
189
+ describe("lifecycle/gate-ledger — pending facts (#2119)", () => {
190
+ test("pending facts and resolutions share the file and read back separately", async () => {
191
+ await withTestDir(async (dir) => {
192
+ await initRepo(dir);
193
+ const { record: pending } = await appendPendingGate(
194
+ {
195
+ op: "fountain-apply", gate: "rollout-gate",
196
+ description: "release manager signs off",
197
+ runId: "local-1",
198
+ timestamp: "2026-01-01T00:00:00.000Z",
199
+ expiresAt: "2026-01-03T00:00:00.000Z",
200
+ },
201
+ { cwd: dir },
202
+ );
203
+ await appendGateResolution(
204
+ { op: "fountain-apply", gate: "rollout-gate", resolvedBy: "alex", timestamp: "2026-01-02T00:00:00.000Z" },
205
+ { cwd: dir },
206
+ );
207
+
208
+ const { resolutions, pending: pendings, malformed } = await readGateLedger("fountain-apply", { cwd: dir });
209
+ expect(malformed).toBe(0);
210
+ expect(pendings).toEqual([pending]);
211
+ expect(resolutions.map((r) => r.resolvedBy)).toEqual(["alex"]);
212
+
213
+ // A pending line is not a malformed resolution to the narrow reader.
214
+ const narrow = await readGateResolutions("fountain-apply", { cwd: dir });
215
+ expect(narrow.malformed).toBe(0);
216
+ expect(narrow.records).toHaveLength(1);
217
+ });
218
+ });
219
+
220
+ test("latestPendingGate picks the newest fact for the gate, expired or not", () => {
221
+ const at = (timestamp: string, gate = "g"): PendingGateRecord => ({
222
+ version: 1, kind: "pending", op: "o", gate, timestamp,
223
+ expiresAt: "2026-01-09T00:00:00.000Z",
224
+ });
225
+ const records = [at("2026-01-01T00:00:00.000Z"), at("2026-01-05T00:00:00.000Z"), at("2026-01-07T00:00:00.000Z", "other")];
226
+ expect(latestPendingGate(records, "g")?.timestamp).toBe("2026-01-05T00:00:00.000Z");
227
+ expect(latestPendingGate(records, "absent")).toBeUndefined();
228
+ });
229
+
230
+ test("isPendingGateExpired is true at and after expiresAt", () => {
231
+ const record: PendingGateRecord = {
232
+ version: 1, kind: "pending", op: "o", gate: "g",
233
+ timestamp: "2026-01-01T00:00:00.000Z", expiresAt: "2026-01-03T00:00:00.000Z",
234
+ };
235
+ expect(isPendingGateExpired(record, "2026-01-02T23:59:59.000Z")).toBe(false);
236
+ expect(isPendingGateExpired(record, "2026-01-03T00:00:00.000Z")).toBe(true);
237
+ expect(isPendingGateExpired(record, "2026-01-04T00:00:00.000Z")).toBe(true);
238
+ });
239
+
240
+ test("a pending line missing expiresAt is malformed, not read as a resolution", async () => {
241
+ await withTestDir(async (dir) => {
242
+ await initRepo(dir);
243
+ await writeBlobToPath(
244
+ "_gates", "fountain-apply.jsonl",
245
+ JSON.stringify({ version: 1, kind: "pending", op: "fountain-apply", gate: "g", timestamp: "2026-01-01T00:00:00.000Z" }),
246
+ "hand-written",
247
+ { cwd: dir },
248
+ );
249
+ const { resolutions, pending, malformed } = await readGateLedger("fountain-apply", { cwd: dir });
250
+ expect(malformed).toBe(1);
251
+ expect(pending).toEqual([]);
252
+ expect(resolutions).toEqual([]);
253
+ });
254
+ });
255
+ });