@intentius/chant 0.49.0 → 0.51.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 (294) hide show
  1. package/dist/audit/catalog.d.ts +13 -3
  2. package/dist/audit/catalog.d.ts.map +1 -1
  3. package/dist/audit/core.d.ts +9 -0
  4. package/dist/audit/core.d.ts.map +1 -1
  5. package/dist/audit/discover.d.ts +6 -0
  6. package/dist/audit/discover.d.ts.map +1 -1
  7. package/dist/audit/fetch.d.ts.map +1 -1
  8. package/dist/audit/report-html.d.ts.map +1 -1
  9. package/dist/audit/report-model.d.ts +6 -0
  10. package/dist/audit/report-model.d.ts.map +1 -1
  11. package/dist/audit/report.d.ts.map +1 -1
  12. package/dist/audit/rules-doc.d.ts.map +1 -1
  13. package/dist/audit/secrets.d.ts +95 -0
  14. package/dist/audit/secrets.d.ts.map +1 -0
  15. package/dist/audit/wrangler.d.ts +33 -0
  16. package/dist/audit/wrangler.d.ts.map +1 -0
  17. package/dist/build.d.ts.map +1 -1
  18. package/dist/cli/commands/audit.d.ts +7 -0
  19. package/dist/cli/commands/audit.d.ts.map +1 -1
  20. package/dist/cli/commands/build.d.ts +23 -0
  21. package/dist/cli/commands/build.d.ts.map +1 -1
  22. package/dist/cli/handlers/build.d.ts.map +1 -1
  23. package/dist/cli/handlers/components.d.ts +31 -0
  24. package/dist/cli/handlers/components.d.ts.map +1 -1
  25. package/dist/cli/handlers/lifecycle.d.ts +11 -0
  26. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  27. package/dist/cli/handlers/op-progress.d.ts +57 -0
  28. package/dist/cli/handlers/op-progress.d.ts.map +1 -0
  29. package/dist/cli/handlers/operator.d.ts +32 -0
  30. package/dist/cli/handlers/operator.d.ts.map +1 -0
  31. package/dist/cli/handlers/run-client.d.ts +21 -1
  32. package/dist/cli/handlers/run-client.d.ts.map +1 -1
  33. package/dist/cli/handlers/run-report.d.ts.map +1 -1
  34. package/dist/cli/handlers/run.d.ts.map +1 -1
  35. package/dist/cli/handlers/scenario.d.ts +39 -0
  36. package/dist/cli/handlers/scenario.d.ts.map +1 -0
  37. package/dist/cli/handlers/search.d.ts +22 -0
  38. package/dist/cli/handlers/search.d.ts.map +1 -1
  39. package/dist/cli/main.d.ts.map +1 -1
  40. package/dist/cli/mcp/op-tools.d.ts.map +1 -1
  41. package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
  42. package/dist/cli/mcp/server.d.ts +35 -2
  43. package/dist/cli/mcp/server.d.ts.map +1 -1
  44. package/dist/cli/mcp/types.d.ts +29 -1
  45. package/dist/cli/mcp/types.d.ts.map +1 -1
  46. package/dist/cli/registry.d.ts +47 -3
  47. package/dist/cli/registry.d.ts.map +1 -1
  48. package/dist/codegen/docs-rule-scanning.d.ts.map +1 -1
  49. package/dist/components/capability.d.ts +17 -2
  50. package/dist/components/capability.d.ts.map +1 -1
  51. package/dist/components/cli-support.d.ts +7 -0
  52. package/dist/components/cli-support.d.ts.map +1 -1
  53. package/dist/components/component.d.ts +15 -0
  54. package/dist/components/component.d.ts.map +1 -1
  55. package/dist/components/driver.d.ts.map +1 -1
  56. package/dist/components/run-progress.d.ts +7 -5
  57. package/dist/components/run-progress.d.ts.map +1 -1
  58. package/dist/components/verbs/index.d.ts +6 -1
  59. package/dist/components/verbs/index.d.ts.map +1 -1
  60. package/dist/components/verbs/run-agent.d.ts +499 -0
  61. package/dist/components/verbs/run-agent.d.ts.map +1 -0
  62. package/dist/components/verbs/sign.d.ts +30 -0
  63. package/dist/components/verbs/sign.d.ts.map +1 -1
  64. package/dist/composite.d.ts +6 -1
  65. package/dist/composite.d.ts.map +1 -1
  66. package/dist/discovery/collect.d.ts.map +1 -1
  67. package/dist/discovery/fold-import.d.ts +15 -1
  68. package/dist/discovery/fold-import.d.ts.map +1 -1
  69. package/dist/discovery/fold-rank.d.ts +66 -0
  70. package/dist/discovery/fold-rank.d.ts.map +1 -0
  71. package/dist/discovery/index.d.ts +15 -0
  72. package/dist/discovery/index.d.ts.map +1 -1
  73. package/dist/discovery/param-deps.d.ts +17 -0
  74. package/dist/discovery/param-deps.d.ts.map +1 -0
  75. package/dist/fold/fold.d.ts +55 -2
  76. package/dist/fold/fold.d.ts.map +1 -1
  77. package/dist/fold/subset.d.ts +21 -14
  78. package/dist/fold/subset.d.ts.map +1 -1
  79. package/dist/lexicon-schema.d.ts +2 -0
  80. package/dist/lexicon-schema.d.ts.map +1 -1
  81. package/dist/lexicon.d.ts +134 -0
  82. package/dist/lexicon.d.ts.map +1 -1
  83. package/dist/lifecycle/assert-live.d.ts +77 -0
  84. package/dist/lifecycle/assert-live.d.ts.map +1 -0
  85. package/dist/lifecycle/change-set.d.ts +17 -0
  86. package/dist/lifecycle/change-set.d.ts.map +1 -1
  87. package/dist/lifecycle/converge-ledger.d.ts +90 -0
  88. package/dist/lifecycle/converge-ledger.d.ts.map +1 -0
  89. package/dist/lifecycle/deep-diff.d.ts +18 -0
  90. package/dist/lifecycle/deep-diff.d.ts.map +1 -1
  91. package/dist/lifecycle/deep-observe.d.ts +9 -1
  92. package/dist/lifecycle/deep-observe.d.ts.map +1 -1
  93. package/dist/lifecycle/disruption.d.ts +96 -0
  94. package/dist/lifecycle/disruption.d.ts.map +1 -0
  95. package/dist/lifecycle/gate-ledger.d.ts +33 -0
  96. package/dist/lifecycle/gate-ledger.d.ts.map +1 -0
  97. package/dist/lifecycle/git.d.ts +145 -21
  98. package/dist/lifecycle/git.d.ts.map +1 -1
  99. package/dist/lifecycle/index.d.ts +6 -0
  100. package/dist/lifecycle/index.d.ts.map +1 -1
  101. package/dist/lifecycle/lease.d.ts +113 -0
  102. package/dist/lifecycle/lease.d.ts.map +1 -0
  103. package/dist/lifecycle/replay.d.ts +2 -0
  104. package/dist/lifecycle/replay.d.ts.map +1 -1
  105. package/dist/lifecycle/scenario-eval.d.ts +42 -0
  106. package/dist/lifecycle/scenario-eval.d.ts.map +1 -0
  107. package/dist/lifecycle/scenario.d.ts +163 -0
  108. package/dist/lifecycle/scenario.d.ts.map +1 -0
  109. package/dist/lifecycle/symptoms.d.ts +63 -0
  110. package/dist/lifecycle/symptoms.d.ts.map +1 -0
  111. package/dist/lint/output-docs.d.ts +94 -0
  112. package/dist/lint/output-docs.d.ts.map +1 -0
  113. package/dist/lint/post-synth.d.ts +29 -0
  114. package/dist/lint/post-synth.d.ts.map +1 -1
  115. package/dist/lint/rules/__fixtures__/comp/comp003/pass/agent-turn.component.d.ts +11 -0
  116. package/dist/lint/rules/__fixtures__/comp/comp003/pass/agent-turn.component.d.ts.map +1 -0
  117. package/dist/lsp/lexicon-providers.d.ts +7 -0
  118. package/dist/lsp/lexicon-providers.d.ts.map +1 -1
  119. package/dist/op/activity-contract.d.ts +139 -0
  120. package/dist/op/activity-contract.d.ts.map +1 -0
  121. package/dist/op/builders.d.ts +42 -2
  122. package/dist/op/builders.d.ts.map +1 -1
  123. package/dist/op/converge-rule.d.ts +161 -0
  124. package/dist/op/converge-rule.d.ts.map +1 -0
  125. package/dist/op/generate-pipeline.d.ts +39 -0
  126. package/dist/op/generate-pipeline.d.ts.map +1 -0
  127. package/dist/op/index.d.ts +14 -0
  128. package/dist/op/index.d.ts.map +1 -1
  129. package/dist/op/local-executor.d.ts +7 -1
  130. package/dist/op/local-executor.d.ts.map +1 -1
  131. package/dist/op/op-verb-class.d.ts +42 -0
  132. package/dist/op/op-verb-class.d.ts.map +1 -0
  133. package/dist/op/operator.d.ts +128 -0
  134. package/dist/op/operator.d.ts.map +1 -0
  135. package/dist/op/step-output-ref.d.ts +187 -0
  136. package/dist/op/step-output-ref.d.ts.map +1 -0
  137. package/dist/op/types.d.ts +18 -1
  138. package/dist/op/types.d.ts.map +1 -1
  139. package/dist/provenance.d.ts +73 -3
  140. package/dist/provenance.d.ts.map +1 -1
  141. package/dist/runtime-adapter.d.ts +7 -1
  142. package/dist/runtime-adapter.d.ts.map +1 -1
  143. package/dist/serializer.d.ts +18 -0
  144. package/dist/serializer.d.ts.map +1 -1
  145. package/dist/testing.d.ts +23 -2
  146. package/dist/testing.d.ts.map +1 -1
  147. package/dist/toml.d.ts +40 -5
  148. package/dist/toml.d.ts.map +1 -1
  149. package/package.json +1 -1
  150. package/src/audit/catalog.test.ts +1 -1
  151. package/src/audit/catalog.ts +75 -3
  152. package/src/audit/core.ts +9 -0
  153. package/src/audit/discover.ts +29 -2
  154. package/src/audit/fetch.test.ts +216 -3
  155. package/src/audit/fetch.ts +270 -59
  156. package/src/audit/report-html.ts +5 -2
  157. package/src/audit/report-model.ts +9 -0
  158. package/src/audit/report.test.ts +22 -0
  159. package/src/audit/report.ts +3 -2
  160. package/src/audit/rules-doc.ts +2 -0
  161. package/src/audit/secrets.test.ts +303 -0
  162. package/src/audit/secrets.ts +406 -0
  163. package/src/audit/wrangler.test.ts +230 -0
  164. package/src/audit/wrangler.ts +290 -0
  165. package/src/build.ts +8 -3
  166. package/src/cli/command-group.ts +1 -1
  167. package/src/cli/commands/__fixtures__/schemas/sarif-2.1.0.schema.json +2882 -0
  168. package/src/cli/commands/audit.test.ts +215 -1
  169. package/src/cli/commands/audit.ts +86 -17
  170. package/src/cli/commands/build.test.ts +167 -2
  171. package/src/cli/commands/build.ts +114 -23
  172. package/src/cli/handlers/build.ts +2 -0
  173. package/src/cli/handlers/components.test.ts +199 -1
  174. package/src/cli/handlers/components.ts +160 -3
  175. package/src/cli/handlers/graph.test.ts +20 -0
  176. package/src/cli/handlers/graph.ts +10 -1
  177. package/src/cli/handlers/lifecycle.test.ts +90 -0
  178. package/src/cli/handlers/lifecycle.ts +30 -5
  179. package/src/cli/handlers/op-progress.test.ts +202 -0
  180. package/src/cli/handlers/op-progress.ts +192 -0
  181. package/src/cli/handlers/operator.test.ts +255 -0
  182. package/src/cli/handlers/operator.ts +240 -0
  183. package/src/cli/handlers/run-client.test.ts +82 -0
  184. package/src/cli/handlers/run-client.ts +85 -2
  185. package/src/cli/handlers/run-report.test.ts +62 -0
  186. package/src/cli/handlers/run-report.ts +20 -58
  187. package/src/cli/handlers/run.test.ts +144 -0
  188. package/src/cli/handlers/run.ts +40 -18
  189. package/src/cli/handlers/scenario.test.ts +456 -0
  190. package/src/cli/handlers/scenario.ts +330 -0
  191. package/src/cli/handlers/search-drift.test.ts +263 -0
  192. package/src/cli/handlers/search.ts +150 -1
  193. package/src/cli/main.test.ts +23 -0
  194. package/src/cli/main.ts +81 -1
  195. package/src/cli/mcp/op-tools.ts +17 -6
  196. package/src/cli/mcp/resource-handlers.ts +13 -5
  197. package/src/cli/mcp/server.test.ts +265 -2
  198. package/src/cli/mcp/server.ts +84 -7
  199. package/src/cli/mcp/types.ts +27 -1
  200. package/src/cli/registry.ts +47 -3
  201. package/src/codegen/docs-rule-scanning.test.ts +42 -0
  202. package/src/codegen/docs-rule-scanning.ts +25 -2
  203. package/src/components/README.md +7 -0
  204. package/src/components/capability.ts +17 -2
  205. package/src/components/cli-support.test.ts +17 -0
  206. package/src/components/cli-support.ts +13 -1
  207. package/src/components/component-schema.test.ts +32 -0
  208. package/src/components/component.schema.json +6 -0
  209. package/src/components/component.test.ts +21 -0
  210. package/src/components/component.ts +15 -0
  211. package/src/components/driver.ts +12 -4
  212. package/src/components/run-progress.ts +9 -5
  213. package/src/components/verbs/index.ts +6 -1
  214. package/src/components/verbs/run-agent.test.ts +683 -0
  215. package/src/components/verbs/run-agent.ts +786 -0
  216. package/src/components/verbs/sign.test.ts +19 -0
  217. package/src/components/verbs/sign.ts +34 -2
  218. package/src/composite.ts +31 -2
  219. package/src/discovery/collect.ts +11 -2
  220. package/src/discovery/fold-import.test.ts +54 -0
  221. package/src/discovery/fold-import.ts +178 -38
  222. package/src/discovery/fold-rank.test.ts +197 -0
  223. package/src/discovery/fold-rank.ts +346 -0
  224. package/src/discovery/index.ts +16 -1
  225. package/src/discovery/param-deps.test.ts +118 -0
  226. package/src/discovery/param-deps.ts +170 -0
  227. package/src/fold/fold.test.ts +6 -2
  228. package/src/fold/fold.ts +184 -3
  229. package/src/fold/subset.test.ts +82 -19
  230. package/src/fold/subset.ts +79 -41
  231. package/src/lexicon-schema.ts +3 -0
  232. package/src/lexicon.ts +154 -2
  233. package/src/lifecycle/assert-live.test.ts +125 -0
  234. package/src/lifecycle/assert-live.ts +154 -0
  235. package/src/lifecycle/change-set.ts +35 -3
  236. package/src/lifecycle/converge-ledger.test.ts +199 -0
  237. package/src/lifecycle/converge-ledger.ts +179 -0
  238. package/src/lifecycle/deep-diff.test.ts +79 -1
  239. package/src/lifecycle/deep-diff.ts +23 -0
  240. package/src/lifecycle/deep-observe.ts +13 -2
  241. package/src/lifecycle/disruption.test.ts +186 -0
  242. package/src/lifecycle/disruption.ts +224 -0
  243. package/src/lifecycle/gate-ledger.test.ts +103 -0
  244. package/src/lifecycle/gate-ledger.ts +140 -0
  245. package/src/lifecycle/git.test.ts +430 -0
  246. package/src/lifecycle/git.ts +446 -84
  247. package/src/lifecycle/index.ts +6 -0
  248. package/src/lifecycle/lease.test.ts +343 -0
  249. package/src/lifecycle/lease.ts +270 -0
  250. package/src/lifecycle/replay.test.ts +25 -0
  251. package/src/lifecycle/replay.ts +11 -3
  252. package/src/lifecycle/scenario-eval.test.ts +199 -0
  253. package/src/lifecycle/scenario-eval.ts +158 -0
  254. package/src/lifecycle/scenario.test.ts +195 -0
  255. package/src/lifecycle/scenario.ts +321 -0
  256. package/src/lifecycle/symptoms.test.ts +116 -0
  257. package/src/lifecycle/symptoms.ts +126 -0
  258. package/src/lint/output-docs.test.ts +220 -0
  259. package/src/lint/output-docs.ts +204 -0
  260. package/src/lint/post-synth.test.ts +97 -0
  261. package/src/lint/post-synth.ts +45 -0
  262. package/src/lint/rules/__fixtures__/comp/comp003/pass/agent-turn.component.ts +26 -0
  263. package/src/lint/rules/comp/comp.test.ts +49 -1
  264. package/src/lint/rules/evl001-non-literal-expression.test.ts +8 -3
  265. package/src/lint/rules/evl001-non-literal-expression.ts +6 -6
  266. package/src/lsp/lexicon-providers.test.ts +44 -0
  267. package/src/lsp/lexicon-providers.ts +11 -1
  268. package/src/op/activity-contract.test.ts +180 -0
  269. package/src/op/activity-contract.ts +278 -0
  270. package/src/op/builders-exports.test.ts +17 -1
  271. package/src/op/builders.ts +59 -5
  272. package/src/op/converge-rule.test.ts +179 -0
  273. package/src/op/converge-rule.ts +311 -0
  274. package/src/op/generate-pipeline.test.ts +53 -0
  275. package/src/op/generate-pipeline.ts +99 -0
  276. package/src/op/index.ts +30 -0
  277. package/src/op/local-executor.test.ts +92 -0
  278. package/src/op/local-executor.ts +52 -10
  279. package/src/op/local-output.ts +1 -1
  280. package/src/op/op-verb-class.test.ts +126 -0
  281. package/src/op/op-verb-class.ts +115 -0
  282. package/src/op/operator.test.ts +346 -0
  283. package/src/op/operator.ts +213 -0
  284. package/src/op/step-output-ref.test.ts +334 -0
  285. package/src/op/step-output-ref.ts +453 -0
  286. package/src/op/types.ts +18 -1
  287. package/src/provenance.test.ts +151 -4
  288. package/src/provenance.ts +118 -4
  289. package/src/runtime-adapter.ts +31 -10
  290. package/src/serializer.ts +18 -0
  291. package/src/testing.test.ts +89 -2
  292. package/src/testing.ts +63 -3
  293. package/src/toml.test.ts +157 -384
  294. package/src/toml.ts +371 -5
@@ -0,0 +1,77 @@
1
+ /**
2
+ * assertLive (#1857) — the read half of the test harness (#1224):
3
+ * observation-backed assertions against a live deploy, for exactly one
4
+ * declared entity at a time. Same primitive teardown.ts's fallback path
5
+ * uses — `describeResources` — turned into a pass/throw instead of a
6
+ * would-delete set.
7
+ *
8
+ * The observation contract (#1089) draws a hard line between OBSERVED-ABSENT
9
+ * and NOT-OBSERVED: a declared entity the read could not cover is never the
10
+ * same as one confirmed missing. `assertLiveEntity` preserves that line by
11
+ * construction — NOT-OBSERVED throws {@link UnobservedAssertionError}, a type
12
+ * distinct from the {@link LiveAssertionError} an observed-absent, foreign,
13
+ * or status-mismatched verdict throws, so a caller can tell "could not tell"
14
+ * from "confirmed wrong" without parsing a message.
15
+ *
16
+ * Marker verification is best-effort by the same logic {@link
17
+ * ResourceMetadata.marker}'s own contract states: a lexicon with no marker
18
+ * channel on this read path (aws's thin `describeResources`, `ownership:
19
+ * "unknown"`) reports no marker at all, which is not the same claim as
20
+ * "foreign". Enforcing a match whenever the channel exists — a present
21
+ * mismatch, or `ownership: "foreign"` with no marker to show — catches the
22
+ * case the harness cares about (a same-named leftover from another env);
23
+ * an absent channel is passed through unverified rather than making every
24
+ * lexicon without one unusable.
25
+ */
26
+ import { type UnobservedReason } from "../observation.js";
27
+ import type { ObservationLexicon, ResourceMetadata } from "../lexicon.js";
28
+ import type { OwnershipMarker } from "../ownership.js";
29
+ /**
30
+ * Thrown by {@link assertLiveEntity} for a confirmed failure: observed
31
+ * absent, a marker that names another stack/env, a resource confirmed
32
+ * foreign, or a status mismatch. Never thrown for NOT-OBSERVED — see {@link
33
+ * UnobservedAssertionError}.
34
+ */
35
+ export declare class LiveAssertionError extends Error {
36
+ constructor(message: string);
37
+ }
38
+ /**
39
+ * Thrown when the entity is NOT-OBSERVED (#1089) rather than confirmed
40
+ * present or absent. Kept as its own type, not a flag on {@link
41
+ * LiveAssertionError}: a suite (or a CI policy) that wants to fail loudly on
42
+ * "could not tell" but treat it differently from a confirmed miss can catch
43
+ * this one specifically.
44
+ */
45
+ export declare class UnobservedAssertionError extends Error {
46
+ readonly entity: string;
47
+ readonly reason: UnobservedReason;
48
+ readonly detail?: string | undefined;
49
+ constructor(entity: string, reason: UnobservedReason, detail?: string | undefined);
50
+ }
51
+ export interface AssertLiveOptions {
52
+ /** Expected `ResourceMetadata.status`, where the lexicon reports one. Skipped when omitted. */
53
+ status?: string;
54
+ }
55
+ export interface AssertLiveEntityOptions extends AssertLiveOptions {
56
+ plugin: ObservationLexicon;
57
+ /** chant entity name — the key to assert on. */
58
+ name: string;
59
+ entityType: string;
60
+ props: Record<string, unknown>;
61
+ /** This lexicon's own built output for the deploy, or `""` when none was built. */
62
+ buildOutput: string;
63
+ environment: string;
64
+ /** This deploy's identity — the marker an observed resource is checked against. */
65
+ marker: OwnershipMarker;
66
+ }
67
+ /**
68
+ * Assert one declared entity is live: observed present in `environment`, not
69
+ * a confirmed-foreign resource, and — when `status` is given — reporting
70
+ * that status. Resolves to the entity's {@link ResourceMetadata} on success.
71
+ *
72
+ * Throws {@link UnobservedAssertionError} for NOT-OBSERVED. Throws {@link
73
+ * LiveAssertionError} for observed-absent, a confirmed-foreign identity, or a
74
+ * status mismatch.
75
+ */
76
+ export declare function assertLiveEntity(opts: AssertLiveEntityOptions): Promise<ResourceMetadata>;
77
+ //# sourceMappingURL=assert-live.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assert-live.d.ts","sourceRoot":"","sources":["../../src/lifecycle/assert-live.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAA6D,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClH,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD;;;;;GAKG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;GAMG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;aAE/B,MAAM,EAAE,MAAM;aACd,MAAM,EAAE,gBAAgB;aACxB,MAAM,CAAC,EAAE,MAAM;gBAFf,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,gBAAgB,EACxB,MAAM,CAAC,EAAE,MAAM,YAAA;CAQlC;AAED,MAAM,WAAW,iBAAiB;IAChC,+FAA+F;IAC/F,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAwB,SAAQ,iBAAiB;IAChE,MAAM,EAAE,kBAAkB,CAAC;IAC3B,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,mFAAmF;IACnF,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,mFAAmF;IACnF,MAAM,EAAE,eAAe,CAAC;CACzB;AAQD;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAwD/F"}
@@ -14,6 +14,7 @@
14
14
  */
15
15
  import { type AttributeChange, type DiffLiveInput } from "./live-diff.js";
16
16
  import { type UnobservedReason } from "../observation.js";
17
+ import { type Disruption } from "./disruption.js";
17
18
  /**
18
19
  * What the projection proposes for a single resource.
19
20
  *
@@ -125,6 +126,22 @@ export interface ChangeSetEntry {
125
126
  effectReason?: EffectFireReason;
126
127
  /** Human-readable backing for `effectReason` (the digests that differ, the unresolved path). */
127
128
  effectDetail?: string;
129
+ /**
130
+ * How much applying this change hurts (#1665) — in-place / rolling / replace
131
+ * / destroy / unknown. Set on `update` entries only: every other action
132
+ * carries its blast radius in the action itself.
133
+ *
134
+ * The verdict comes from the lexicon that owns the spec
135
+ * ({@link LexiconPlugin.classifyDisruption}), never from core, which has no
136
+ * per-provider replacement rules and must not grow any. `unknown` is the
137
+ * default and the only fallback — read it as "nobody could say", never as
138
+ * "probably in place".
139
+ */
140
+ disruption?: Disruption;
141
+ /** The attribute paths that forced `disruption` (#1665). */
142
+ disruptionBecause?: string[];
143
+ /** Human-readable backing for `disruption` — the spec knowledge behind the call, or why there is none. */
144
+ disruptionDetail?: string;
128
145
  }
129
146
  export interface ChangeSet {
130
147
  env: string;
@@ -1 +1 @@
1
- {"version":3,"file":"change-set.d.ts","sourceRoot":"","sources":["../../src/lifecycle/change-set.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAY,KAAK,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,EAAwB,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAE7E;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,YAAY,CAAC;AAEnH;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,gBAAgB,GAAG,gBAAgB,GAAG,eAAe,GAAG,kBAAkB,CAAC;AAEvF;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;AAExD,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,YAAY,CAAC;IACrB,kEAAkE;IAClE,QAAQ,EAAE;QACR,oCAAoC;QACpC,QAAQ,EAAE,OAAO,CAAC;QAClB,oCAAoC;QACpC,UAAU,EAAE,OAAO,CAAC;QACpB,qDAAqD;QACrD,IAAI,EAAE,OAAO,CAAC;QACd;;;;;WAKG;QACH,QAAQ,EAAE,OAAO,CAAC;KACnB,CAAC;IACF,6CAA6C;IAC7C,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;IAC3B,oDAAoD;IACpD,SAAS,EAAE,SAAS,CAAC;IACrB,gFAAgF;IAChF,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,gGAAgG;IAChG,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oGAAoG;IACpG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,gGAAgG;IAChG,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,gBAAgB;IAC/B,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CA6GvG;AAID,gCAAgC;AAChC,wBAAgB,SAAS,CAAC,EAAE,EAAE,SAAS,GAAG,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAarE;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,EAAE,EAAE,SAAS,GAAG,cAAc,CAG5D;AAED,sEAAsE;AACtE,wBAAgB,eAAe,CAAC,EAAE,EAAE,SAAS,GAAG,MAAM,CAsCrD"}
1
+ {"version":3,"file":"change-set.d.ts","sourceRoot":"","sources":["../../src/lifecycle/change-set.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAY,KAAK,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,EAAwB,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EAAyC,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAEtF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,YAAY,CAAC;AAEnH;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,gBAAgB,GAAG,gBAAgB,GAAG,eAAe,GAAG,kBAAkB,CAAC;AAEvF;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;AAExD,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,YAAY,CAAC;IACrB,kEAAkE;IAClE,QAAQ,EAAE;QACR,oCAAoC;QACpC,QAAQ,EAAE,OAAO,CAAC;QAClB,oCAAoC;QACpC,UAAU,EAAE,OAAO,CAAC;QACpB,qDAAqD;QACrD,IAAI,EAAE,OAAO,CAAC;QACd;;;;;WAKG;QACH,QAAQ,EAAE,OAAO,CAAC;KACnB,CAAC;IACF,6CAA6C;IAC7C,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;IAC3B,oDAAoD;IACpD,SAAS,EAAE,SAAS,CAAC;IACrB,gFAAgF;IAChF,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,gGAAgG;IAChG,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oGAAoG;IACpG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,gGAAgG;IAChG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;;;;OAUG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,4DAA4D;IAC5D,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,0GAA0G;IAC1G,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,gBAAgB;IAC/B,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CA6GvG;AAID,gCAAgC;AAChC,wBAAgB,SAAS,CAAC,EAAE,EAAE,SAAS,GAAG,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAarE;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,EAAE,EAAE,SAAS,GAAG,cAAc,CAG5D;AAED,sEAAsE;AACtE,wBAAgB,eAAe,CAAC,EAAE,EAAE,SAAS,GAAG,MAAM,CAqDrD"}
@@ -0,0 +1,90 @@
1
+ /** One rule's outcome within a tick. */
2
+ export interface ConvergeRuleOutcome {
3
+ ruleId: string;
4
+ /**
5
+ * What actually happened for this fired rule this tick. `"gated"` (#1485)
6
+ * is gate-as-fact: the dispatched op's own run hit a gate it can't clear
7
+ * on the local executor, and the tick records that as a terminal, durable
8
+ * fact — `gateName` names the gate — rather than treating it as a
9
+ * dispatch failure. Resolution is out-of-band (`chant approve <op>
10
+ * <gate>`, ./gate-ledger.ts, or a merged PR); the tick itself never
11
+ * blocks waiting for it.
12
+ */
13
+ action: "ran" | "reported" | "skipped-budget" | "skipped-flap" | "gated";
14
+ /** The dispatched Op name, for `action: "ran"` or `"gated"`. */
15
+ op?: string;
16
+ /** The gate's signal name, for `action: "gated"`. */
17
+ gateName?: string;
18
+ /** The report reason, for `action: "reported"` (including a flap-damped rule's forced report) — and the human-readable explanation for `action: "gated"`. */
19
+ reason?: string;
20
+ }
21
+ /** One immutable converge-tick record. */
22
+ export interface ConvergeTickRecord {
23
+ /** Schema version, so an incompatible future shape is detected before being misread. */
24
+ version: 1;
25
+ /** The ConvergeOp's name (`OpConfig.name`). */
26
+ op: string;
27
+ env: string;
28
+ /** ISO-8601 timestamp, caller-supplied (same convention as `ReleaseRecord.timestamp` — library code never calls `Date.now()` internally). */
29
+ timestamp: string;
30
+ /** Every rule id whose predicate matched this tick, regardless of what action followed — the flap-damping input. */
31
+ firedRuleIds: string[];
32
+ /** Per-rule outcome, for every fired rule. */
33
+ outcomes: ConvergeRuleOutcome[];
34
+ /** Aggregate counts backing the tick's one log line. */
35
+ summary: {
36
+ drifted: number;
37
+ remediated: number;
38
+ reported: number;
39
+ skippedBudget: number;
40
+ skippedFlap: number;
41
+ unobserved: number;
42
+ adopted: number;
43
+ /** Rules whose dispatch hit a gate this tick (#1485) — a terminal, non-blocking fact; see `ConvergeRuleOutcome.action`'s doc. @default 0, so an older record without this field still reads as zero, not undefined. */
44
+ gated?: number;
45
+ };
46
+ /** The one human-readable log line this tick produced (issue: "one log line and one ledger record per tick"). */
47
+ log: string;
48
+ }
49
+ export type ConvergeTickRecordInput = Omit<ConvergeTickRecord, "version">;
50
+ /**
51
+ * Append one immutable tick record. Does not push to the remote — call
52
+ * `pushLifecycle` (./git.ts) afterward, same two-step shape every other
53
+ * ledger write here uses.
54
+ *
55
+ * Retries the whole read-modify-write cycle (#1485) on `RefCASConflictError`
56
+ * — `writeBlobToPath`'s ref write is CAS-guarded, so a concurrent writer to
57
+ * a *different* env's file on the same orphan branch (two operators ticking
58
+ * two environments at once is the ordinary case, not an edge case) can lose
59
+ * the race and needs to re-read the branch tip and retry, not fail the
60
+ * tick. Each retry re-reads `existing` fresh, so it always appends onto
61
+ * whatever the other writer just committed rather than reintroducing a stale
62
+ * read. Exhausting the budget re-throws the conflict — a real, sustained
63
+ * pile-up of writers is a signal worth surfacing, not silently swallowing.
64
+ *
65
+ * The baseline read must be `readPathSha` + `readBlobBySha` rather than
66
+ * `readBlobFromPath`, so the exact sha `existing` came from can be passed as
67
+ * `expectPriorPathSha`. See `writeBlobToPath` (./git.ts) for the race that
68
+ * closes.
69
+ */
70
+ export declare function appendConvergeRecord(input: ConvergeTickRecordInput, opts?: {
71
+ cwd?: string;
72
+ }): Promise<{
73
+ commit: string;
74
+ record: ConvergeTickRecord;
75
+ }>;
76
+ /** Read every tick record for `environment`, oldest first. Malformed lines are skipped, not thrown on — a corrupted or hand-edited ledger degrades gracefully, the same stance `readReleaseLedger` takes. */
77
+ export declare function readConvergeLedger(environment: string, opts?: {
78
+ cwd?: string;
79
+ }): Promise<{
80
+ records: ConvergeTickRecord[];
81
+ malformed: number;
82
+ }>;
83
+ /**
84
+ * How many consecutive most-recent ticks (newest first) fired `ruleId`,
85
+ * stopping at the first tick where it did not — the count a rule's
86
+ * `flapThreshold` is compared against. `0` when the newest tick didn't fire
87
+ * it (including an empty ledger).
88
+ */
89
+ export declare function consecutiveRuleFires(records: ConvergeTickRecord[], ruleId: string): number;
90
+ //# sourceMappingURL=converge-ledger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"converge-ledger.d.ts","sourceRoot":"","sources":["../../src/lifecycle/converge-ledger.ts"],"names":[],"mappings":"AAmCA,wCAAwC;AACxC,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;OAQG;IACH,MAAM,EAAE,KAAK,GAAG,UAAU,GAAG,gBAAgB,GAAG,cAAc,GAAG,OAAO,CAAC;IACzE,gEAAgE;IAChE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6JAA6J;IAC7J,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,0CAA0C;AAC1C,MAAM,WAAW,kBAAkB;IACjC,wFAAwF;IACxF,OAAO,EAAE,CAAC,CAAC;IACX,+CAA+C;IAC/C,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,6IAA6I;IAC7I,SAAS,EAAE,MAAM,CAAC;IAClB,oHAAoH;IACpH,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,8CAA8C;IAC9C,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,wDAAwD;IACxD,OAAO,EAAE;QACP,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,uNAAuN;QACvN,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,iHAAiH;IACjH,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,uBAAuB,GAAG,IAAI,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC;AAE1E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,uBAAuB,EAC9B,IAAI,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACtB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,kBAAkB,CAAA;CAAE,CAAC,CAqBzD;AAED,6MAA6M;AAC7M,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACtB,OAAO,CAAC;IAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CA0B/D;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,kBAAkB,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAO1F"}
@@ -22,6 +22,7 @@
22
22
  * interpolated property forever.
23
23
  */
24
24
  import { type DeepNormalizationHooks, type NormalizedDeepObservation } from "../deep-observation.js";
25
+ import { type PathOrigin } from "../provenance.js";
25
26
  import type { UnobservedResource } from "./live-diff.js";
26
27
  import { type BaselineLexicon } from "./observation-baseline.js";
27
28
  /**
@@ -61,6 +62,18 @@ export interface PropertyDrift {
61
62
  * with no per-field ownership, which is every substrate but k8s.
62
63
  */
63
64
  owner?: string;
65
+ /**
66
+ * What produced this path on the DECLARED side (#1443) — the counterpart of
67
+ * {@link owner}, and the reason the two are reported together: "owned live by
68
+ * `hpa-controller`, governed in source by the `tier` parameter" says where
69
+ * each half of a disagreement has to be fixed, which neither half says alone.
70
+ *
71
+ * Resolved by longest prefix from the entity's recorded path origins, so a
72
+ * field inside a keyed list element inherits the origin recorded for the
73
+ * list. Absent when the build recorded none — the run path, and a sandboxed
74
+ * child, have no expression to attribute (see `EntityProvenance.paths`).
75
+ */
76
+ origin?: PathOrigin;
64
77
  }
65
78
  /** Property-level drift for one declared entity. */
66
79
  export interface DeepEntityDrift {
@@ -88,6 +101,11 @@ export interface DeepDiffResult {
88
101
  export interface DeclaredDeepEntity {
89
102
  type: string;
90
103
  properties: Record<string, unknown>;
104
+ /**
105
+ * The entity's recorded path origins (#1443), as `EntityProvenance.paths`.
106
+ * Omit for a build that recorded none.
107
+ */
108
+ pathOrigins?: Record<string, PathOrigin>;
91
109
  }
92
110
  export interface DiffDeepInput {
93
111
  /** Normalized declared property trees, keyed by chant entity name. */
@@ -1 +1 @@
1
- {"version":3,"file":"deep-diff.d.ts","sourceRoot":"","sources":["../../src/lifecycle/deep-diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAIL,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,EAC/B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEjF;;;;;;;;;GASG;AACH,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,YAAY,GAAG,QAAQ,CAAC;AAEpE,qCAAqC;AACrC,MAAM,WAAW,aAAa;IAC5B,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,iBAAiB,CAAC;IACxB,gDAAgD;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;;;OASG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,oDAAoD;AACpD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,iFAAiF;IACjF,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,qDAAqD;IACrD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,8EAA8E;IAC9E,UAAU,EAAE,kBAAkB,EAAE,CAAC;IACjC,0EAA0E;IAC1E,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,sFAAsF;AACtF,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,aAAa;IAC5B,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC7C,4EAA4E;IAC5E,IAAI,EAAE,yBAAyB,CAAC;IAChC,yEAAyE;IACzE,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B;;;;;OAKG;IACH,KAAK,CAAC,EAAE,sBAAsB,CAAC;CAChC;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CAmG7D;AAED,+DAA+D;AAC/D,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAEjE"}
1
+ {"version":3,"file":"deep-diff.d.ts","sourceRoot":"","sources":["../../src/lifecycle/deep-diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAIL,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,EAC/B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEjF;;;;;;;;;GASG;AACH,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,YAAY,GAAG,QAAQ,CAAC;AAEpE,qCAAqC;AACrC,MAAM,WAAW,aAAa;IAC5B,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,iBAAiB,CAAC;IACxB,gDAAgD;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;;;OASG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED,oDAAoD;AACpD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,iFAAiF;IACjF,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,qDAAqD;IACrD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,8EAA8E;IAC9E,UAAU,EAAE,kBAAkB,EAAE,CAAC;IACjC,0EAA0E;IAC1E,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,sFAAsF;AACtF,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,aAAa;IAC5B,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC7C,4EAA4E;IAC5E,IAAI,EAAE,yBAAyB,CAAC;IAChC,yEAAyE;IACzE,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B;;;;;OAKG;IACH,KAAK,CAAC,EAAE,sBAAsB,CAAC;CAChC;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CAwG7D;AAED,+DAA+D;AAC/D,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAEjE"}
@@ -18,12 +18,20 @@
18
18
  */
19
19
  import type { ObservationLexicon } from "../lexicon.js";
20
20
  import { type DeepNormalizationHooks, type NormalizedDeepObservation } from "../deep-observation.js";
21
+ import type { PathOrigin } from "../provenance.js";
21
22
  import { type DeepDiffResult } from "./deep-diff.js";
22
23
  import type { BaselineLexicon } from "./observation-baseline.js";
23
- /** Declared entities for one lexicon, in the shape the observe paths pass around. */
24
+ /**
25
+ * Declared entities for one lexicon, in the shape the observe paths pass around.
26
+ *
27
+ * `pathOrigins` (#1443) rides alongside `props` rather than being read off the
28
+ * entity, because callers rebuild this map as plain objects and the symbol-keyed
29
+ * provenance channel does not survive that copy.
30
+ */
24
31
  export type DeclaredEntities = Map<string, {
25
32
  entityType: string;
26
33
  props: Record<string, unknown>;
34
+ pathOrigins?: Record<string, PathOrigin>;
27
35
  }>;
28
36
  export interface DeepObserveOptions {
29
37
  environment: string;
@@ -1 +1 @@
1
- {"version":3,"file":"deep-observe.d.ts","sourceRoot":"","sources":["../../src/lifecycle/deep-observe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAIL,KAAK,sBAAsB,EAE3B,KAAK,yBAAyB,EAC/B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAqC,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,qFAAqF;AACrF,MAAM,MAAM,gBAAgB,GAAG,GAAG,CAAC,MAAM,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CAAC;AAEnG,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;4CAEwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kFAAkF;IAClF,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,QAAQ,CAAC,yBAAyB,CAAC,GACzC,yBAAyB,CAS3B;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,kBAAkB,EAC1B,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,yBAAyB,CAAC,CA+BpC;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,gBAAgB,EAC1B,IAAI,EAAE,yBAAyB,EAC/B,KAAK,CAAC,EAAE,sBAAsB,EAC9B,QAAQ,CAAC,EAAE,eAAe,GACzB,cAAc,CAmDhB;AAED,oFAAoF;AACpF,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,kBAAkB,EAC1B,IAAI,EAAE,kBAAkB,GAAG;IAAE,QAAQ,CAAC,EAAE,eAAe,CAAA;CAAE,GACxD,OAAO,CAAC,cAAc,CAAC,CAGzB"}
1
+ {"version":3,"file":"deep-observe.d.ts","sourceRoot":"","sources":["../../src/lifecycle/deep-observe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAIL,KAAK,sBAAsB,EAE3B,KAAK,yBAAyB,EAC/B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAqC,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,GAAG,CAChC,MAAM,EACN;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;CAAE,CACjG,CAAC;AAEF,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;4CAEwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kFAAkF;IAClF,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,QAAQ,CAAC,yBAAyB,CAAC,GACzC,yBAAyB,CAS3B;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,kBAAkB,EAC1B,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,yBAAyB,CAAC,CA+BpC;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,gBAAgB,EAC1B,IAAI,EAAE,yBAAyB,EAC/B,KAAK,CAAC,EAAE,sBAAsB,EAC9B,QAAQ,CAAC,EAAE,eAAe,GACzB,cAAc,CAoDhB;AAED,oFAAoF;AACpF,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,kBAAkB,EAC1B,IAAI,EAAE,kBAAkB,GAAG;IAAE,QAAQ,CAAC,EAAE,eAAe,CAAA;CAAE,GACxD,OAAO,CAAC,cAAc,CAAC,CAGzB"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Per-change disruption classification (#1665).
3
+ *
4
+ * The change set says WHAT a pending change is (`create`/`update`/`delete`/…).
5
+ * It says nothing about what applying it costs. An `update` that flips a tag
6
+ * and an `update` that rebuilds a database read identically, and the second one
7
+ * is the one that wakes somebody up.
8
+ *
9
+ * The knowledge that separates them is spec knowledge. CloudFormation's
10
+ * registry schema declares `createOnlyProperties` per type; Kubernetes' SSA
11
+ * schema knows which field changes roll a workload. Core owns neither, and
12
+ * hardcoding either here would put per-provider replacement rules in the tool
13
+ * — the same mistake `postSynthChecks` exists to avoid. So core defines the
14
+ * contract and the reporting, and the lexicon that compiled the spec supplies
15
+ * the answer, via {@link LexiconPlugin.classifyDisruption}.
16
+ *
17
+ * The invariant that makes the field trustworthy is that `unknown` is the
18
+ * default and the only fallback. No classifier, a classifier that says nothing
19
+ * about an entry, a classifier that throws, a classifier that returns a level
20
+ * outside the vocabulary — all of them land on `unknown`, never on `in-place`.
21
+ * A confident "this mutates in place" is only ever a lexicon's own claim.
22
+ */
23
+ import type { AttributeChange } from "./live-diff.js";
24
+ import type { ChangeSet, ChangeSetEntry } from "./change-set.js";
25
+ /**
26
+ * How much applying one pending change hurts.
27
+ *
28
+ * - `in-place` — the provider mutates the existing resource. No new identity,
29
+ * no window where it is absent.
30
+ * - `rolling` — the resource survives, but its workload is replaced
31
+ * incrementally (a Deployment's pod template changing). Disruptive to what
32
+ * is running, not to the resource.
33
+ * - `replace` — a new resource is created and the old one removed. The
34
+ * physical id changes; anything holding the old one has to be updated.
35
+ * - `destroy` — replacement that removes the old resource FIRST. There is a
36
+ * window with nothing there, and whatever the old one held is gone.
37
+ * - `unknown` — nobody could say. The honest value, and the default: it is
38
+ * what a change gets when no lexicon classifies it, and it must never be
39
+ * read as "probably fine".
40
+ */
41
+ export type Disruption = "in-place" | "rolling" | "replace" | "destroy" | "unknown";
42
+ /** Every level, most disruptive last — also the guard core validates a lexicon's answer against. */
43
+ export declare const DISRUPTION_LEVELS: readonly Disruption[];
44
+ /** One pending change put to a lexicon for classification. */
45
+ export interface DisruptionQuery {
46
+ /** The change set entry's `name` — the key a verdict comes back under. */
47
+ name: string;
48
+ /** Resource type, when the observation reported one. */
49
+ type?: string;
50
+ /** The attribute-level changes the entry carries. */
51
+ deltas: AttributeChange[];
52
+ }
53
+ /** A lexicon's answer for one query. */
54
+ export interface DisruptionVerdict {
55
+ disruption: Disruption;
56
+ /** The attribute paths that forced the verdict — empty or absent when none did. */
57
+ because?: string[];
58
+ /** One line of human-readable backing, naming the spec knowledge behind the call. */
59
+ detail?: string;
60
+ }
61
+ /**
62
+ * The shape of {@link LexiconPlugin.classifyDisruption}. Keyed by query `name`;
63
+ * a name the lexicon says nothing about degrades to `unknown`, so a partial
64
+ * answer is a valid answer.
65
+ */
66
+ export type DisruptionClassifier = (options: {
67
+ environment: string;
68
+ changes: DisruptionQuery[];
69
+ }) => Record<string, DisruptionVerdict> | Promise<Record<string, DisruptionVerdict>>;
70
+ /** The verdict every fallback path produces. */
71
+ export declare function unknownDisruption(detail: string): DisruptionVerdict;
72
+ /**
73
+ * Annotate one lexicon's change set with a disruption verdict per `update`.
74
+ *
75
+ * Only `update` entries are asked about: every other action already carries its
76
+ * blast radius in the action itself. Called once per lexicon, before the plan
77
+ * merges the change sets, so `classify` is always the lexicon that produced the
78
+ * entries — the only party that can map its own observation's attribute paths
79
+ * back onto spec properties.
80
+ *
81
+ * Returns a new change set; the input is not mutated.
82
+ */
83
+ export declare function annotateDisruption(cs: ChangeSet, environment: string, classify?: DisruptionClassifier): Promise<ChangeSet>;
84
+ /** Count `update` entries per level. Entries with no verdict at all are not counted. */
85
+ export declare function summarizeDisruption(cs: ChangeSet): Record<Disruption, number>;
86
+ /** The most disruptive verdict in the set, or undefined when nothing was classified. */
87
+ export declare function worstDisruption(cs: ChangeSet): Disruption | undefined;
88
+ /**
89
+ * Warnings a plan should print on stderr — so a `--json` or `--report gitlab-mr`
90
+ * consumer, whose shape has no column for disruption, still hears about the
91
+ * expensive rows. Same discipline as the unobserved warning (#1089).
92
+ */
93
+ export declare function disruptionNotices(cs: ChangeSet): string[];
94
+ /** Render one entry's verdict for the human plan, or "" when there is none. */
95
+ export declare function renderDisruption(entry: ChangeSetEntry): string;
96
+ //# sourceMappingURL=disruption.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"disruption.d.ts","sourceRoot":"","sources":["../../src/lifecycle/disruption.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAEpF,oGAAoG;AACpG,eAAO,MAAM,iBAAiB,EAAE,SAAS,UAAU,EAMlD,CAAC;AAWF,8DAA8D;AAC9D,MAAM,WAAW,eAAe;IAC9B,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAED,wCAAwC;AACxC,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,UAAU,CAAC;IACvB,mFAAmF;IACnF,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,EAAE;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,eAAe,EAAE,CAAC;CAC5B,KAAK,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;AAErF,gDAAgD;AAChD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,CAEnE;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,SAAS,EACb,WAAW,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,OAAO,CAAC,SAAS,CAAC,CA0CpB;AAiBD,wFAAwF;AACxF,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,SAAS,GAAG,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAY7E;AAED,wFAAwF;AACxF,wBAAgB,eAAe,CAAC,EAAE,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,CAOrE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,SAAS,GAAG,MAAM,EAAE,CAkBzD;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAG9D"}
@@ -0,0 +1,33 @@
1
+ /** One immutable gate-resolution record. */
2
+ export interface GateResolutionRecord {
3
+ /** Schema version, so an incompatible future shape is detected before being misread. */
4
+ version: 1;
5
+ /** The dispatched op the gate belongs to. */
6
+ op: string;
7
+ /** The gate's signal name (matches `ConvergeRuleOutcome.gateName`). */
8
+ gate: string;
9
+ /** Who resolved it — an actor name, the same convention `components release --actor` and `run signal --approver` use. */
10
+ resolvedBy: string;
11
+ /** ISO-8601 timestamp, caller-supplied (library code never calls `Date.now()` internally). */
12
+ timestamp: string;
13
+ /** Optional free-text context (e.g. a PR URL — "or a merged PR" is the issue's other resolution path; recording its link here keeps both paths visible from one ledger). */
14
+ note?: string;
15
+ }
16
+ export type GateResolutionInput = Omit<GateResolutionRecord, "version">;
17
+ /** Append one immutable gate-resolution record. Does not push to the remote — call `pushLifecycle` (./git.ts) afterward, same two-step shape every other ledger write here uses. Retries on `RefCASConflictError` the same way `appendConvergeRecord` does (./converge-ledger.ts) — a concurrent writer to a different op's/env's file on the same orphan branch is the ordinary case, not an edge case. The baseline read must be `readPathSha` + `readBlobBySha` rather than `readBlobFromPath`, so the exact sha `existing` came from can be passed as `expectPriorPathSha` — see `writeBlobToPath` (./git.ts) for the race that closes. */
18
+ export declare function appendGateResolution(input: GateResolutionInput, opts?: {
19
+ cwd?: string;
20
+ }): Promise<{
21
+ commit: string;
22
+ record: GateResolutionRecord;
23
+ }>;
24
+ /** Read every gate-resolution record for `op`, oldest first. Malformed lines are skipped, not thrown on, the same graceful-degradation stance `readConvergeLedger` takes. Returns `[]` (never throws) when `op` has no resolutions recorded yet. */
25
+ export declare function readGateResolutions(op: string, opts?: {
26
+ cwd?: string;
27
+ }): Promise<{
28
+ records: GateResolutionRecord[];
29
+ malformed: number;
30
+ }>;
31
+ /** The most recent resolution for `gate` recorded after `sinceIso` (a gated tick's own timestamp) — what `chant operator status` uses to tell a resolved gate from a still-pending one. `undefined` when no such resolution exists. */
32
+ export declare function latestResolutionSince(records: GateResolutionRecord[], gate: string, sinceIso: string): GateResolutionRecord | undefined;
33
+ //# sourceMappingURL=gate-ledger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate-ledger.d.ts","sourceRoot":"","sources":["../../src/lifecycle/gate-ledger.ts"],"names":[],"mappings":"AA4CA,4CAA4C;AAC5C,MAAM,WAAW,oBAAoB;IACnC,wFAAwF;IACxF,OAAO,EAAE,CAAC,CAAC;IACX,6CAA6C;IAC7C,EAAE,EAAE,MAAM,CAAC;IACX,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,yHAAyH;IACzH,UAAU,EAAE,MAAM,CAAC;IACnB,8FAA8F;IAC9F,SAAS,EAAE,MAAM,CAAC;IAClB,4KAA4K;IAC5K,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,CAAC;AAMxE,+mBAA+mB;AAC/mB,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,mBAAmB,EAC1B,IAAI,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACtB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,oBAAoB,CAAA;CAAE,CAAC,CAqB3D;AAED,oPAAoP;AACpP,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,MAAM,EACV,IAAI,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACtB,OAAO,CAAC;IAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CA0BjE;AAED,uOAAuO;AACvO,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,oBAAoB,EAAE,EAC/B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,oBAAoB,GAAG,SAAS,CASlC"}
@@ -1,26 +1,6 @@
1
- /**
2
- * Write a blob to an arbitrary `<environment>/<filename>` path on the orphan
3
- * branch, preserving every other env/file entry already on the branch.
4
- *
5
- * Pipeline: hash-object → mktree → commit-tree → update-ref. Factored out of
6
- * `writeSnapshot` so the release ledger (#568, epic #551 "Build & deploy
7
- * observability") can reuse the identical git-plumbing path for a different
8
- * filename (`releases.jsonl`) under the same env directory, rather than a
9
- * parallel storage mechanism.
10
- *
11
- * Exported (not just used internally) so ./build-ledger-store.ts (#609) can
12
- * reuse this exact plumbing for a top-level directory that isn't really an
13
- * "environment" (`_builds`, keyed by manifest digest rather than env name) —
14
- * a build archive is promoted by digest across environments, never owned by
15
- * one, so it belongs in its own digest-keyed namespace on the same orphan
16
- * branch rather than duplicated per env. The parameter is still named
17
- * `environment` because it is literally the first path segment / root-tree
18
- * entry name this function's tree-building logic groups by; callers outside
19
- * this module that pass a non-env value (like `_builds`) are relying on that
20
- * generic behavior, not on any env-specific semantics.
21
- */
22
1
  export declare function writeBlobToPath(environment: string, filename: string, content: string, commitMessage: string, opts?: {
23
2
  cwd?: string;
3
+ expectPriorPathSha?: string | null;
24
4
  }): Promise<string>;
25
5
  /**
26
6
  * Read a blob from an arbitrary `<environment>/<filename>` path on the orphan
@@ -32,6 +12,16 @@ export declare function writeBlobToPath(environment: string, filename: string, c
32
12
  export declare function readBlobFromPath(environment: string, filename: string, opts?: {
33
13
  cwd?: string;
34
14
  }): Promise<string | null>;
15
+ /**
16
+ * Read the blob SHA stored at `<environment>/<filename>` on the orphan branch,
17
+ * or `null` if absent. Sibling of `readBlobFromPath` returning the
18
+ * content-address rather than the content. A read-modify-write ledger append
19
+ * pairs this with {@link readBlobBySha} to pin its baseline read to an exact
20
+ * sha, then passes that sha as `writeBlobToPath`'s `expectPriorPathSha`.
21
+ */
22
+ export declare function readPathSha(environment: string, filename: string, opts?: {
23
+ cwd?: string;
24
+ }): Promise<string | null>;
35
25
  /**
36
26
  * Storage key for a snapshot on the orphan branch. Single-stack projects key by
37
27
  * lexicon (`<env>/<lexicon>.json`, unchanged). A multi-stack project (see
@@ -81,6 +71,20 @@ export declare function readSnapshotAt(environment: string, lexicon: string, ref
81
71
  * Returns the new orphan-branch commit SHA — the caller still owns pushing
82
72
  * via `pushLifecycle` under the same concurrent-write lease `writeSnapshot`
83
73
  * uses.
74
+ *
75
+ * Retries the whole read-modify-append cycle on `RefCASConflictError`
76
+ * (#1959 finding 1), the same shape `appendConvergeRecord`
77
+ * (./converge-ledger.ts) and `appendGateResolution` (./gate-ledger.ts) use
78
+ * for their own append-only ledgers: `writeBlobToPath`'s own retry only
79
+ * absorbs a conflict caused by some *other* env/file changing — a conflict
80
+ * on this exact `releases.jsonl` (e.g. two deploys to the same env racing)
81
+ * needs `existing` re-read fresh so the appended line list is rebuilt onto
82
+ * whatever the other writer just committed, not silently dropped by
83
+ * retrying with a blob computed from a stale read.
84
+ *
85
+ * The baseline read must be `readPathSha` + `readBlobBySha` rather than
86
+ * `readBlobFromPath`, so the exact sha `existing` came from can be passed as
87
+ * `expectPriorPathSha`. See `writeBlobToPath` for the race that closes.
84
88
  */
85
89
  export declare function appendReleaseRecordLine(environment: string, recordJson: string, opts?: {
86
90
  cwd?: string;
@@ -166,6 +170,126 @@ export declare function pushLifecycle(opts?: {
166
170
  export declare function fetchLifecycle(opts?: {
167
171
  cwd?: string;
168
172
  }): Promise<boolean>;
173
+ /**
174
+ * Thrown by {@link updateRefCAS}/{@link deleteRefCAS} when `ref` no longer
175
+ * points at the `oldValue` the caller last observed — another writer moved
176
+ * it concurrently. Deliberately a distinct type from a generic git failure so
177
+ * callers (a lease acquire, a retried ledger append) can tell "I lost a race"
178
+ * from "git itself failed" and react differently to each.
179
+ */
180
+ export declare class RefCASConflictError extends Error {
181
+ readonly ref: string;
182
+ readonly expected: string | null;
183
+ constructor(ref: string, expected: string | null, stderr: string);
184
+ }
185
+ /**
186
+ * Thrown by {@link updateRefCAS}/{@link deleteRefCAS} when the ref update
187
+ * failed because git found a stale `.lock` file already sitting next to the
188
+ * ref (#1959 finding 2) — what a `chant operator`/`chant approve`/etc.
189
+ * process leaves behind when it is killed (SIGKILL, OOM, `kill -9`) mid-write,
190
+ * *before* `git update-ref`'s own lockfile-then-rename completes. This is
191
+ * exactly the crash this feature must recover from, and it is NOT a CAS
192
+ * conflict: nobody else actually holds the ref (no other writer is racing,
193
+ * the previous one is simply dead), so it must never be misread as "someone
194
+ * else updated it first" — see `./lease.ts`'s `acquireLease`, which used to
195
+ * (before this fix) read this as "lease held by someone else" and quietly
196
+ * back off forever, since the dead process's lock file never goes away on
197
+ * its own.
198
+ */
199
+ export declare class StaleLockError extends Error {
200
+ readonly ref: string;
201
+ readonly lockPath: string;
202
+ constructor(ref: string, lockPath: string, stderr: string);
203
+ }
204
+ /** Read the SHA `ref` currently points to, or `null` if it doesn't exist. Works for any ref, not just the lifecycle branch. */
205
+ export declare function readRefSha(ref: string, opts?: {
206
+ cwd?: string;
207
+ }): Promise<string | null>;
208
+ /**
209
+ * Compare-and-swap update of an arbitrary ref. `oldValue` is the SHA the
210
+ * caller last observed the ref at, or `null` to assert the ref does not yet
211
+ * exist (git's own convention: an empty `<oldvalue>` argument to
212
+ * `update-ref` means "must not exist"). Throws {@link RefCASConflictError}
213
+ * when the ref genuinely moved since `oldValue` was read, {@link
214
+ * StaleLockError} when a leftover lock file from a killed process is
215
+ * blocking the write, or a plain `Error` for anything else — never silently
216
+ * overwrites, and never misclassifies one failure as another (#1959 finding
217
+ * 2; see {@link classifyRefFailure}).
218
+ */
219
+ export declare function updateRefCAS(ref: string, newValue: string, oldValue: string | null, opts?: {
220
+ cwd?: string;
221
+ }): Promise<void>;
222
+ /**
223
+ * Compare-and-swap delete of an arbitrary ref — `oldValue` is required (no
224
+ * "delete unconditionally" escape hatch here) so releasing a lease you no
225
+ * longer hold can never delete someone else's newer one. Same failure
226
+ * classification as {@link updateRefCAS} (#1959 finding 2).
227
+ */
228
+ export declare function deleteRefCAS(ref: string, oldValue: string, opts?: {
229
+ cwd?: string;
230
+ }): Promise<void>;
231
+ /**
232
+ * Write arbitrary content as a git blob object — no tree, no commit, no ref
233
+ * update. The building block a CAS ref's value can point at directly: a
234
+ * lease record (`./lease.ts`) has no meaningful "tree of files", so its ref
235
+ * targets a blob SHA rather than a commit the way `writeBlobToPath`'s tree-
236
+ * building pipeline does.
237
+ */
238
+ export declare function writeBlob(content: string, opts?: {
239
+ cwd?: string;
240
+ }): Promise<string>;
241
+ /** Read a blob's raw content by its SHA (whatever object a ref points at directly). Returns `null` when the object doesn't exist locally. */
242
+ export declare function readBlobBySha(sha: string, opts?: {
243
+ cwd?: string;
244
+ }): Promise<string | null>;
245
+ /**
246
+ * Push one arbitrary ref (e.g. a lease ref) to the remote, guarded the same
247
+ * way {@link pushLifecycle} guards the ledger branch: `--force-with-lease`
248
+ * keyed to the remote SHA last observed locally, so a concurrent push from a
249
+ * second machine is rejected rather than silently clobbered. Plain `--force`
250
+ * underneath that lease — a lease ref's value is a bare blob SHA, not a
251
+ * commit descending from the previous one, so there is no "fast-forward" to
252
+ * preserve, only the CAS the lease guard already provides.
253
+ *
254
+ * Returns `false` (never throws) when no remote is configured — a
255
+ * remote-less project's lease is local-only by construction (see
256
+ * `./lease.ts`'s module doc), or when the push itself is rejected (the
257
+ * caller re-reads and retries; see `acquireLease`).
258
+ */
259
+ export declare function pushRef(ref: string, opts?: {
260
+ cwd?: string;
261
+ }): Promise<boolean>;
262
+ /**
263
+ * Fetch one arbitrary remote ref into a local ref of a possibly *different*
264
+ * name (#1959 finding 3). `+` forces the update even when it isn't a
265
+ * fast-forward (a lease ref's new value is rarely a descendant of its old
266
+ * one). Returns `false` (never throws) when no remote is configured.
267
+ *
268
+ * The `localRef !== remoteRef` shape exists so a read path can observe what
269
+ * the remote currently holds without ever touching a local ref another code
270
+ * path treats as CAS-authoritative — see {@link fetchRef}'s doc and
271
+ * `./lease.ts`'s `readLease`, which fetches into a side tracking ref
272
+ * (`refs/chant/lease-remote/<op>`) for exactly this reason.
273
+ */
274
+ export declare function fetchRefInto(remoteRef: string, localRef: string, opts?: {
275
+ cwd?: string;
276
+ }): Promise<boolean>;
277
+ /**
278
+ * Fetch one arbitrary ref from remote into the same local ref name.
279
+ *
280
+ * **Caution for a read path (#1959 finding 3):** this force-overwrites
281
+ * `ref` locally (`+ref:ref`) — safe for a ref only a CAS write path ever
282
+ * mutates locally between fetches, but NOT safe to call from a plain read
283
+ * before every read if some other local writer (in the same clone) might be
284
+ * mid-write: fetching here would force the local ref back to whatever the
285
+ * remote last had, clobbering a just-written, not-yet-pushed local value out
286
+ * from under it. `./lease.ts`'s `readLease` used to do exactly that; it now
287
+ * uses {@link fetchRefInto} against a side tracking ref instead. Prefer
288
+ * `fetchRefInto` for any new read-before-decide path.
289
+ */
290
+ export declare function fetchRef(ref: string, opts?: {
291
+ cwd?: string;
292
+ }): Promise<boolean>;
169
293
  /**
170
294
  * Get the current HEAD commit SHA of the main working branch.
171
295
  */